diff --git a/DEPS b/DEPS
index 9ab67643..230a24e 100644
--- a/DEPS
+++ b/DEPS
@@ -40,7 +40,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': '03e058414cfc2f65d01a786024378092eed8833d',
+  'skia_revision': 'c777b88c631dd1367b55ec515188556730cd91c0',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling V8
   # and whatever else without interference from each other.
@@ -64,7 +64,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling PDFium
   # and whatever else without interference from each other.
-  'pdfium_revision': 'b9c69ab7f1338684ffddbc1076052c546f1a95ad',
+  'pdfium_revision': '40e0a819100b6b2cf63070c1a91393cf42820c69',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling openmax_dl
   # and whatever else without interference from each other.
@@ -96,7 +96,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': '0bd2f03bedefec8ca50c162a346d56c2b0b89e3e',
+  'catapult_revision': '794fff6c814784ad5f9a2b33b52d74cf1148d91f',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libFuzzer
   # and whatever else without interference from each other.
diff --git a/android_webview/glue/java/src/com/android/webview/chromium/WebViewChromiumFactoryProvider.java b/android_webview/glue/java/src/com/android/webview/chromium/WebViewChromiumFactoryProvider.java
index 22db843..7043674 100644
--- a/android_webview/glue/java/src/com/android/webview/chromium/WebViewChromiumFactoryProvider.java
+++ b/android_webview/glue/java/src/com/android/webview/chromium/WebViewChromiumFactoryProvider.java
@@ -592,7 +592,7 @@
         final String yahooMailPackageId = "com.yahoo.mobile.client.android.mail";
         if (appName.startsWith(yahooMailPackageId)) {
             if (appTargetSdkVersion > Build.VERSION_CODES.M) return false;
-            if (versionCode > 1315849) return false;
+            if (versionCode > 1315850) return false;
             shouldDisable = true;
         }
 
diff --git a/ash/BUILD.gn b/ash/BUILD.gn
index ebb51f7..711c8ec 100644
--- a/ash/BUILD.gn
+++ b/ash/BUILD.gn
@@ -1157,43 +1157,98 @@
   testonly = true
   sources = [
     "accelerators/accelerator_controller_unittest.cc",
+    "accelerators/accelerator_filter_unittest.cc",
+    "accelerators/spoken_feedback_toggler_unittest.cc",
+    "ash_touch_exploration_manager_chromeos_unittest.cc",
+    "autoclick/autoclick_unittest.cc",
     "common/accelerators/accelerator_table_unittest.cc",
     "common/devtools/ash_devtools_unittest.cc",
     "common/drag_drop/drag_image_view_unittest.cc",
+    "common/frame/caption_buttons/frame_caption_button_container_view_unittest.cc",
+    "common/frame/custom_frame_view_ash_unittest.cc",
+    "common/frame/default_header_painter_unittest.cc",
+    "common/metrics/pointer_metrics_recorder_unittest.cc",
     "common/mus_property_mirror_ash_unittest.cc",
+    "common/session/session_controller_unittest.cc",
+    "common/shelf/shelf_application_menu_model_unittest.cc",
+    "common/shelf/shelf_background_animator_unittest.cc",
+    "common/shelf/shelf_button_pressed_metric_tracker_unittest.cc",
+    "common/shelf/shelf_locking_manager_unittest.cc",
+    "common/shelf/shelf_model_unittest.cc",
+    "common/shelf/shelf_tooltip_manager_unittest.cc",
+    "common/shelf/shelf_window_watcher_unittest.cc",
     "common/system/chromeos/audio/tray_audio_unittest.cc",
     "common/system/chromeos/brightness/tray_brightness_unittest.cc",
+    "common/system/chromeos/ime_menu/ime_menu_tray_unittest.cc",
+    "common/system/chromeos/media_security/multi_profile_media_tray_item_unittest.cc",
+    "common/system/chromeos/network/sms_observer_unittest.cc",
     "common/system/chromeos/network/vpn_list_unittest.cc",
+    "common/system/chromeos/palette/mock_palette_tool_delegate.cc",
+    "common/system/chromeos/palette/mock_palette_tool_delegate.h",
+    "common/system/chromeos/palette/palette_tool_manager_unittest.cc",
+    "common/system/chromeos/palette/tools/create_note_unittest.cc",
+    "common/system/chromeos/palette/tools/screenshot_unittest.cc",
+    "common/system/chromeos/power/power_status_unittest.cc",
+    "common/system/chromeos/power/power_status_view_unittest.cc",
+    "common/system/chromeos/power/tray_power_unittest.cc",
     "common/system/chromeos/screen_security/screen_tray_item_unittest.cc",
+    "common/system/chromeos/session/logout_confirmation_controller_unittest.cc",
+    "common/system/chromeos/session/tray_session_length_limit_unittest.cc",
     "common/system/chromeos/supervised/tray_supervised_user_unittest.cc",
     "common/system/date/date_view_unittest.cc",
+    "common/system/date/system_info_default_view_unittest.cc",
+    "common/system/ime/tray_ime_chromeos_unittest.cc",
+    "common/system/tiles/tray_tiles_unittest.cc",
+    "common/system/tray/size_range_layout_unittest.cc",
+    "common/system/tray/system_tray_unittest.cc",
+    "common/system/tray/tray_details_view_unittest.cc",
+    "common/system/tray/tri_view_unittest.cc",
     "common/system/update/tray_update_unittest.cc",
+    "common/system/user/tray_user_unittest.cc",
+    "common/wallpaper/wallpaper_controller_unittest.cc",
     "common/wm/container_finder_unittest.cc",
     "common/wm/mru_window_tracker_unittest.cc",
+    "common/wm/overview/cleanup_animation_observer_unittest.cc",
     "common/wm/workspace/workspace_layout_manager_unittest.cc",
     "common/wm_window_unittest.cc",
     "common/wm_window_user_data_unittest.cc",
+    "first_run/first_run_helper_unittest.cc",
+    "focus_cycler_unittest.cc",
+    "frame/caption_buttons/frame_size_button_unittest.cc",
+    "laser/laser_pointer_controller_unittest.cc",
+    "laser/laser_pointer_points_unittest.cc",
+    "laser/laser_segment_utils_unittest.cc",
   ]
   deps = [
     "//ash",
     "//ash/common/test:test_support",
     "//ash/public/cpp:ash_public_cpp",
     "//ash/public/interfaces",
+    "//ash/resources/vector_icons",
     "//ash/test:test_support_without_content",
     "//base",
     "//base/test:test_support",
+    "//chromeos",
+    "//chromeos:power_manager_proto",
+    "//chromeos:test_support_without_gmock",
     "//services/ui/public/interfaces",
+    "//testing/gmock",
+    "//ui/accessibility",
     "//ui/app_list/presenter",
     "//ui/app_list/presenter:test_support",
     "//ui/aura",
     "//ui/aura:test_support",
     "//ui/base",
     "//ui/base:test_support",
+    "//ui/compositor:test_support",
     "//ui/display",
     "//ui/events:test_support",
+    "//ui/gfx:test_support",
     "//ui/keyboard",
     "//ui/message_center",
+    "//ui/message_center:test_support",
     "//ui/views",
+    "//ui/views:test_support",
     "//ui/wm",
   ]
   public_deps = [
@@ -1203,62 +1258,24 @@
 
 test("ash_unittests") {
   sources = [
+    # TODO: move to common_unittests. Fails because of http://crbug.com/622486.
     "accelerators/accelerator_commands_unittest.cc",
-    "accelerators/accelerator_filter_unittest.cc",
+
+    # TODO: move to common_unittests. Fails because of http://crbug.com/557401.
     "accelerators/magnifier_key_scroller_unittest.cc",
-    "accelerators/spoken_feedback_toggler_unittest.cc",
+
+    # Specific to classic-ash.
     "app_list/app_list_presenter_delegate_unittest.cc",
-    "ash_touch_exploration_manager_chromeos_unittest.cc",
+
+    # TODO: move to common_unittests when http://crbug.com/693790 is fixed.
     "aura/pointer_watcher_adapter_unittest.cc",
-    "autoclick/autoclick_unittest.cc",
-    "common/session/session_controller_unittest.cc",
 
-    # TODO: convert to use AshTest http://crbug.com/654489.
-    "common/frame/caption_buttons/frame_caption_button_container_view_unittest.cc",
-    "common/frame/custom_frame_view_ash_unittest.cc",
-    "common/frame/default_header_painter_unittest.cc",
-
-    # TODO: convert to use AshTest http://crbug.com/654492.
-    "common/metrics/pointer_metrics_recorder_unittest.cc",
-
-    # TODO: convert to use AshTest http://crbug.com/654494.
-    "common/shelf/shelf_application_menu_model_unittest.cc",
-    "common/shelf/shelf_background_animator_unittest.cc",
-    "common/shelf/shelf_button_pressed_metric_tracker_unittest.cc",
-    "common/shelf/shelf_locking_manager_unittest.cc",
-    "common/shelf/shelf_model_unittest.cc",
-    "common/shelf/shelf_tooltip_manager_unittest.cc",
-    "common/shelf/shelf_window_watcher_unittest.cc",
-
-    # TODO: convert to use AshTest http://crbug.com/654495.
-    "common/system/chromeos/ime_menu/ime_menu_tray_unittest.cc",
-    "common/system/chromeos/media_security/multi_profile_media_tray_item_unittest.cc",
-    "common/system/chromeos/network/sms_observer_unittest.cc",
-    "common/system/chromeos/palette/mock_palette_tool_delegate.cc",
-    "common/system/chromeos/palette/mock_palette_tool_delegate.h",
-    "common/system/chromeos/palette/palette_tool_manager_unittest.cc",
-    "common/system/chromeos/palette/tools/create_note_unittest.cc",
-    "common/system/chromeos/palette/tools/screenshot_unittest.cc",
-    "common/system/chromeos/power/power_status_unittest.cc",
-    "common/system/chromeos/power/power_status_view_unittest.cc",
-    "common/system/chromeos/power/tray_power_unittest.cc",
-    "common/system/chromeos/session/logout_confirmation_controller_unittest.cc",
-    "common/system/chromeos/session/tray_session_length_limit_unittest.cc",
-    "common/system/date/system_info_default_view_unittest.cc",
-    "common/system/ime/tray_ime_chromeos_unittest.cc",
-    "common/system/tiles/tray_tiles_unittest.cc",
-    "common/system/tray/size_range_layout_unittest.cc",
-    "common/system/tray/system_tray_unittest.cc",
-    "common/system/tray/tray_details_view_unittest.cc",
-    "common/system/tray/tri_view_unittest.cc",
-    "common/system/user/tray_user_unittest.cc",
-
-    # TODO: convert to use AshTest http://crbug.com/654517.
-    "common/wallpaper/wallpaper_controller_unittest.cc",
-
-    # TODO: convert to use AshTest http://crbug.com/654524.
-    "common/wm/overview/cleanup_animation_observer_unittest.cc",
+    # TODO: decide if this needs to be ported. http://crbug.com/695566.
     "dip_unittest.cc",
+
+    # TODO: tests in display generally use display_manger(), which doesn't
+    # exist in mash. Decide which of these tests need to be ported to mash.
+    # http://crbug.com/695569
     "display/cursor_window_controller_unittest.cc",
     "display/display_color_manager_chromeos_unittest.cc",
     "display/display_error_observer_chromeos_unittest.cc",
@@ -1275,16 +1292,16 @@
     "display/screen_position_controller_unittest.cc",
     "display/unified_mouse_warp_controller_unittest.cc",
     "display/window_tree_host_manager_unittest.cc",
+
+    # These exercise classic-ash specific functionality.
     "drag_drop/drag_drop_controller_unittest.cc",
     "drag_drop/drag_drop_tracker_unittest.cc",
+
+    # TODO: port to mash. http://crbug.com/695570.
     "extended_desktop_unittest.cc",
-    "first_run/first_run_helper_unittest.cc",
-    "focus_cycler_unittest.cc",
-    "frame/caption_buttons/frame_size_button_unittest.cc",
+
+    # Specific to X11.
     "host/ash_window_tree_host_x11_unittest.cc",
-    "laser/laser_pointer_controller_unittest.cc",
-    "laser/laser_pointer_points_unittest.cc",
-    "laser/laser_segment_utils_unittest.cc",
     "magnifier/magnification_controller_unittest.cc",
     "magnifier/partial_magnification_controller_unittest.cc",
     "metrics/desktop_task_switch_metric_recorder_unittest.cc",
@@ -1382,7 +1399,6 @@
     "//base",
     "//base/test:test_support",
     "//chromeos",
-    "//chromeos:power_manager_proto",
     "//chromeos:test_support_without_gmock",
     "//components/quirks",
     "//components/signin/core/account_id",
@@ -1415,12 +1431,10 @@
     "//ui/events:test_support",
     "//ui/events/devices",
     "//ui/gfx",
-    "//ui/gfx:test_support",
     "//ui/gfx/geometry",
     "//ui/gl:test_support",
     "//ui/keyboard",
     "//ui/message_center",
-    "//ui/message_center:test_support",
     "//ui/resources",
     "//ui/strings",
     "//ui/views",
diff --git a/ash/accelerators/accelerator_filter_unittest.cc b/ash/accelerators/accelerator_filter_unittest.cc
index 9c9a7e4..f19204b8 100644
--- a/ash/accelerators/accelerator_filter_unittest.cc
+++ b/ash/accelerators/accelerator_filter_unittest.cc
@@ -35,6 +35,10 @@
 
 // Tests if AcceleratorFilter works without a focused window.
 TEST_F(AcceleratorFilterTest, TestFilterWithoutFocus) {
+  // TODO: mash doesn't support ScreenshotDelgate yet. http://crbug.com/632111.
+  if (WmShell::Get()->IsRunningInMash())
+    return;
+
   const TestScreenshotDelegate* delegate = GetScreenshotDelegate();
   EXPECT_EQ(0, delegate->handle_take_screenshot_count());
 
@@ -49,6 +53,10 @@
 
 // Tests if AcceleratorFilter works as expected with a focused window.
 TEST_F(AcceleratorFilterTest, TestFilterWithFocus) {
+  // TODO: mash doesn't support ScreenshotDelgate yet. http://crbug.com/632111.
+  if (WmShell::Get()->IsRunningInMash())
+    return;
+
   aura::test::TestWindowDelegate test_delegate;
   std::unique_ptr<aura::Window> window(
       CreateTestWindowInShellWithDelegate(&test_delegate, -1, gfx::Rect()));
@@ -71,6 +79,10 @@
 
 // Tests if AcceleratorFilter ignores the flag for Caps Lock.
 TEST_F(AcceleratorFilterTest, TestCapsLockMask) {
+  // TODO: mash doesn't support ScreenshotDelgate yet. http://crbug.com/632111.
+  if (WmShell::Get()->IsRunningInMash())
+    return;
+
   const TestScreenshotDelegate* delegate = GetScreenshotDelegate();
   EXPECT_EQ(0, delegate->handle_take_screenshot_count());
 
diff --git a/ash/autoclick/autoclick_unittest.cc b/ash/autoclick/autoclick_unittest.cc
index db20ff32..73d21ff 100644
--- a/ash/autoclick/autoclick_unittest.cc
+++ b/ash/autoclick/autoclick_unittest.cc
@@ -3,6 +3,7 @@
 // found in the LICENSE file.
 
 #include "ash/autoclick/autoclick_controller.h"
+#include "ash/common/wm_shell.h"
 #include "ash/shell.h"
 #include "ash/test/ash_test_base.h"
 #include "ui/aura/test/test_window_delegate.h"
@@ -70,7 +71,8 @@
 
     // Make sure the display is initialized so we don't fail the test due to any
     // input events caused from creating the display.
-    Shell::GetInstance()->display_manager()->UpdateDisplays();
+    if (!WmShell::Get()->IsRunningInMash())
+      Shell::GetInstance()->display_manager()->UpdateDisplays();
     RunAllPendingInMessageLoop();
   }
 
diff --git a/ash/common/shelf/shelf_button_pressed_metric_tracker_unittest.cc b/ash/common/shelf/shelf_button_pressed_metric_tracker_unittest.cc
index 79bcc83..2461306 100644
--- a/ash/common/shelf/shelf_button_pressed_metric_tracker_unittest.cc
+++ b/ash/common/shelf/shelf_button_pressed_metric_tracker_unittest.cc
@@ -7,6 +7,7 @@
 #include <utility>
 
 #include "ash/common/shelf/wm_shelf.h"
+#include "ash/common/wm_shell.h"
 #include "ash/test/ash_test_base.h"
 #include "ash/test/shelf_button_pressed_metric_tracker_test_api.h"
 #include "ash/test/shelf_view_test_api.h"
@@ -153,6 +154,10 @@
 // a button is pressed by a mouse event.
 TEST_F(ShelfButtonPressedMetricTrackerTest,
        Launcher_ButtonPressed_MouseIsRecordedWhenIconActivatedByMouse) {
+  // TODO: investigate failure in mash. http://crbug.com/695565.
+  if (WmShell::Get()->IsRunningInMash())
+    return;
+
   const ui::MouseEvent mouse_event(ui::ET_MOUSE_PRESSED, gfx::Point(),
                                    gfx::Point(), base::TimeTicks(), 0, 0);
 
@@ -166,6 +171,10 @@
 // a button is pressed by a touch event.
 TEST_F(ShelfButtonPressedMetricTrackerTest,
        Launcher_ButtonPressed_MouseIsRecordedWhenIconActivatedByTouch) {
+  // TODO: investigate failure in mash. http://crbug.com/695565.
+  if (WmShell::Get()->IsRunningInMash())
+    return;
+
   const ui::TouchEvent touch_event(ui::ET_GESTURE_TAP, gfx::Point(), 0,
                                    base::TimeTicks());
 
@@ -179,6 +188,10 @@
 // pressing a button causes a new window to be created.
 TEST_F(ShelfButtonPressedMetricTrackerTest,
        Launcher_LaunchTaskIsRecordedWhenNewWindowIsCreated) {
+  // TODO: investigate failure in mash. http://crbug.com/695565.
+  if (WmShell::Get()->IsRunningInMash())
+    return;
+
   base::UserActionTester user_action_tester;
   ButtonPressed(SHELF_ACTION_NEW_WINDOW_CREATED);
   EXPECT_EQ(1, user_action_tester.GetActionCount("Launcher_LaunchTask"));
@@ -188,6 +201,10 @@
 // pressing a button causes an existing window to be minimized.
 TEST_F(ShelfButtonPressedMetricTrackerTest,
        Launcher_MinimizeTaskIsRecordedWhenWindowIsMinimized) {
+  // TODO: investigate failure in mash. http://crbug.com/695565.
+  if (WmShell::Get()->IsRunningInMash())
+    return;
+
   base::UserActionTester user_action_tester;
   ButtonPressed(SHELF_ACTION_WINDOW_MINIMIZED);
   EXPECT_EQ(1, user_action_tester.GetActionCount("Launcher_MinimizeTask"));
@@ -197,6 +214,10 @@
 // pressing a button causes an existing window to be activated.
 TEST_F(ShelfButtonPressedMetricTrackerTest,
        Launcher_SwitchTaskIsRecordedWhenExistingWindowIsActivated) {
+  // TODO: investigate failure in mash. http://crbug.com/695565.
+  if (WmShell::Get()->IsRunningInMash())
+    return;
+
   base::UserActionTester user_action_tester;
   ButtonPressed(SHELF_ACTION_WINDOW_ACTIVATED);
   EXPECT_EQ(1, user_action_tester.GetActionCount("Launcher_SwitchTask"));
diff --git a/ash/common/shelf/shelf_tooltip_manager_unittest.cc b/ash/common/shelf/shelf_tooltip_manager_unittest.cc
index 268626e..955301fd 100644
--- a/ash/common/shelf/shelf_tooltip_manager_unittest.cc
+++ b/ash/common/shelf/shelf_tooltip_manager_unittest.cc
@@ -9,6 +9,7 @@
 #include "ash/common/shelf/shelf_view.h"
 #include "ash/common/shelf/wm_shelf.h"
 #include "ash/common/test/test_shelf_item_delegate.h"
+#include "ash/common/wm_shell.h"
 #include "ash/test/ash_test_base.h"
 #include "ash/test/shelf_view_test_api.h"
 #include "base/memory/ptr_util.h"
@@ -165,6 +166,10 @@
 }
 
 TEST_F(ShelfTooltipManagerTest, HideForEvents) {
+  // TODO: investigate failure in mash. http://crbug.com/695563.
+  if (WmShell::Get()->IsRunningInMash())
+    return;
+
   ui::test::EventGenerator& generator = GetEventGenerator();
   gfx::Rect shelf_bounds = shelf_view_->GetBoundsInScreen();
 
@@ -197,6 +202,10 @@
 }
 
 TEST_F(ShelfTooltipManagerTest, HideForExternalEvents) {
+  // TODO: investigate failure in mash. http://crbug.com/695563.
+  if (WmShell::Get()->IsRunningInMash())
+    return;
+
   ui::test::EventGenerator& generator = GetEventGenerator();
 
   // Should hide for touches outside the shelf.
diff --git a/ash/common/shelf/shelf_window_watcher_unittest.cc b/ash/common/shelf/shelf_window_watcher_unittest.cc
index d50c8579..9634e82 100644
--- a/ash/common/shelf/shelf_window_watcher_unittest.cc
+++ b/ash/common/shelf/shelf_window_watcher_unittest.cc
@@ -72,6 +72,10 @@
 }
 
 TEST_F(ShelfWindowWatcherTest, CreateAndRemoveShelfItemProperties) {
+  // TODO: investigate failure in mash. http://crbug.com/695562.
+  if (WmShell::Get()->IsRunningInMash())
+    return;
+
   // ShelfModel only has an APP_LIST item.
   EXPECT_EQ(1, model_->item_count());
 
@@ -109,6 +113,10 @@
 }
 
 TEST_F(ShelfWindowWatcherTest, ActivateWindow) {
+  // TODO: investigate failure in mash. http://crbug.com/695562.
+  if (WmShell::Get()->IsRunningInMash())
+    return;
+
   // ShelfModel only have APP_LIST item.
   EXPECT_EQ(1, model_->item_count());
   std::unique_ptr<views::Widget> widget1 =
@@ -143,6 +151,10 @@
 }
 
 TEST_F(ShelfWindowWatcherTest, UpdateWindowProperty) {
+  // TODO: investigate failure in mash. http://crbug.com/695562.
+  if (WmShell::Get()->IsRunningInMash())
+    return;
+
   // ShelfModel only has an APP_LIST item.
   EXPECT_EQ(1, model_->item_count());
 
@@ -167,6 +179,10 @@
 }
 
 TEST_F(ShelfWindowWatcherTest, MaximizeAndRestoreWindow) {
+  // TODO: investigate failure in mash. http://crbug.com/695562.
+  if (WmShell::Get()->IsRunningInMash())
+    return;
+
   // ShelfModel only has an APP_LIST item.
   EXPECT_EQ(1, model_->item_count());
 
@@ -204,6 +220,10 @@
 
 // Check that an item is maintained when its associated Window is docked.
 TEST_F(ShelfWindowWatcherTest, DockWindow) {
+  // TODO: investigate failure in mash. http://crbug.com/695562.
+  if (WmShell::Get()->IsRunningInMash())
+    return;
+
   // ShelfModel only has an APP_LIST item.
   EXPECT_EQ(1, model_->item_count());
 
@@ -240,6 +260,10 @@
 // Check |window|'s item is not changed during the dragging.
 // TODO(simonhong): Add a test for removing a Window during the dragging.
 TEST_F(ShelfWindowWatcherTest, DragWindow) {
+  // TODO: investigate failure in mash. http://crbug.com/695562.
+  if (WmShell::Get()->IsRunningInMash())
+    return;
+
   // ShelfModel only has an APP_LIST item.
   EXPECT_EQ(1, model_->item_count());
 
@@ -268,6 +292,10 @@
 
 // Ensure shelf items are added and removed as panels are opened and closed.
 TEST_F(ShelfWindowWatcherTest, PanelWindow) {
+  // TODO: investigate failure in mash. http://crbug.com/695562.
+  if (WmShell::Get()->IsRunningInMash())
+    return;
+
   // ShelfModel only has an APP_LIST item.
   EXPECT_EQ(1, model_->item_count());
 
diff --git a/ash/common/system/ime/tray_ime_chromeos_unittest.cc b/ash/common/system/ime/tray_ime_chromeos_unittest.cc
index c90fa49..fb6e8bc0 100644
--- a/ash/common/system/ime/tray_ime_chromeos_unittest.cc
+++ b/ash/common/system/ime/tray_ime_chromeos_unittest.cc
@@ -159,6 +159,10 @@
 // Tests that if no IMEs are present the default view is hidden when a11y is
 // enabled.
 TEST_F(TrayIMETest, HidesOnA11yEnabled) {
+  // TODO: investigate failure in mash. http://crbug.com/695561.
+  if (WmShell::Get()->IsRunningInMash())
+    return;
+
   SetIMELength(0);
   SuppressKeyboard();
   EXPECT_TRUE(default_view()->visible());
@@ -173,6 +177,10 @@
 // Tests that clicking on the keyboard toggle causes the virtual keyboard
 // to toggle between enabled and disabled.
 TEST_F(TrayIMETest, PerformActionOnDetailedView) {
+  // TODO: investigate failure in mash. http://crbug.com/695561.
+  if (WmShell::Get()->IsRunningInMash())
+    return;
+
   SetIMELength(0);
   SuppressKeyboard();
   EXPECT_FALSE(keyboard::IsKeyboardEnabled());
diff --git a/ash/common/system/tray/system_tray_unittest.cc b/ash/common/system/tray/system_tray_unittest.cc
index 092c4c6..d76f249 100644
--- a/ash/common/system/tray/system_tray_unittest.cc
+++ b/ash/common/system/tray/system_tray_unittest.cc
@@ -204,6 +204,10 @@
 // Make sure the opening system tray bubble will not deactivate the
 // other window. crbug.com/120680.
 TEST_F(SystemTrayTest, Activation) {
+  // TODO: investigate why this fails in mash. http://crbug.com/695559.
+  if (WmShell::Get()->IsRunningInMash())
+    return;
+
   SystemTray* tray = GetPrimarySystemTray();
   std::unique_ptr<views::Widget> widget(CreateTestWidget(
       nullptr, kShellWindowId_DefaultContainer, gfx::Rect(0, 0, 100, 100)));
@@ -456,6 +460,10 @@
 }
 
 TEST_F(SystemTrayTest, PersistentBubble) {
+  // TODO: investigate why this fails in mash. http://crbug.com/695559.
+  if (WmShell::Get()->IsRunningInMash())
+    return;
+
   SystemTray* tray = GetPrimarySystemTray();
   ASSERT_TRUE(tray->GetWidget());
 
diff --git a/ash/common/test/ash_test.cc b/ash/common/test/ash_test.cc
index 245ce6b..ba087ec2 100644
--- a/ash/common/test/ash_test.cc
+++ b/ash/common/test/ash_test.cc
@@ -105,6 +105,10 @@
 bool AshTest::SetSecondaryDisplayPlacement(
     display::DisplayPlacement::Position position,
     int offset) {
+  if (WmShell::Get()->IsRunningInMash()) {
+    NOTIMPLEMENTED();
+    return false;
+  }
   return test_impl_->SetSecondaryDisplayPlacement(position, offset);
 }
 
diff --git a/ash/common/wallpaper/wallpaper_controller_unittest.cc b/ash/common/wallpaper/wallpaper_controller_unittest.cc
index b05bf4a..d7aac9f 100644
--- a/ash/common/wallpaper/wallpaper_controller_unittest.cc
+++ b/ash/common/wallpaper/wallpaper_controller_unittest.cc
@@ -375,10 +375,13 @@
   EXPECT_EQ("1000x300",
             WallpaperController::GetMaxDisplaySizeInNative().ToString());
 
-  // Rotated display should return the rotated size.
-  UpdateDisplay("1000x300*2/r");
-  EXPECT_EQ("300x1000",
-            WallpaperController::GetMaxDisplaySizeInNative().ToString());
+  // TODO: mash doesn't support rotation yet, http://crbug.com/695556.
+  if (!WmShell::Get()->IsRunningInMash()) {
+    // Rotated display should return the rotated size.
+    UpdateDisplay("1000x300*2/r");
+    EXPECT_EQ("300x1000",
+              WallpaperController::GetMaxDisplaySizeInNative().ToString());
+  }
 
   // UI Scaling shouldn't affect the native size.
   UpdateDisplay("1000x300*2@1.5");
diff --git a/ash/extended_desktop_unittest.cc b/ash/extended_desktop_unittest.cc
index 8747c72..8dc73cba 100644
--- a/ash/extended_desktop_unittest.cc
+++ b/ash/extended_desktop_unittest.cc
@@ -751,16 +751,14 @@
           kShellWindowId_SettingBubbleContainer);
   aura::Window* window =
       aura::test::CreateTestWindowWithId(100, settings_bubble_container);
-  window->SetBoundsInScreen(gfx::Rect(150, 10, 50, 50),
-                            display_manager()->GetSecondaryDisplay());
+  window->SetBoundsInScreen(gfx::Rect(150, 10, 50, 50), GetSecondaryDisplay());
   EXPECT_EQ(root_windows[0], window->GetRootWindow());
 
   aura::Window* status_container =
       Shell::GetPrimaryRootWindowController()->GetContainer(
           kShellWindowId_StatusContainer);
   window = aura::test::CreateTestWindowWithId(100, status_container);
-  window->SetBoundsInScreen(gfx::Rect(150, 10, 50, 50),
-                            display_manager()->GetSecondaryDisplay());
+  window->SetBoundsInScreen(gfx::Rect(150, 10, 50, 50), GetSecondaryDisplay());
   EXPECT_EQ(root_windows[0], window->GetRootWindow());
 }
 
@@ -773,8 +771,7 @@
       display::Screen::GetScreen()->GetPrimaryDisplay().bounds());
   widget1->Show();
   EXPECT_EQ(root_windows[0], widget1->GetNativeView()->GetRootWindow());
-  views::Widget* widget2 =
-      CreateTestWidget(display_manager()->GetSecondaryDisplay().bounds());
+  views::Widget* widget2 = CreateTestWidget(GetSecondaryDisplay().bounds());
   widget2->Show();
   EXPECT_EQ(root_windows[1], widget2->GetNativeView()->GetRootWindow());
 
diff --git a/ash/mus/bridge/wm_shell_mus.cc b/ash/mus/bridge/wm_shell_mus.cc
index 814fc5f..ac17b7a6 100644
--- a/ash/mus/bridge/wm_shell_mus.cc
+++ b/ash/mus/bridge/wm_shell_mus.cc
@@ -268,11 +268,13 @@
 }
 
 bool WmShellMus::IsPinned() {
+  // TODO: http://crbug.com/622486.
   NOTIMPLEMENTED();
   return false;
 }
 
 void WmShellMus::SetPinnedWindow(WmWindow* window) {
+  // TODO: http://crbug.com/622486.
   NOTIMPLEMENTED();
 }
 
diff --git a/ash/root_window_controller.cc b/ash/root_window_controller.cc
index f621e805..dde74bfe 100644
--- a/ash/root_window_controller.cc
+++ b/ash/root_window_controller.cc
@@ -486,9 +486,8 @@
                             location_in_root, ui::EventTimeForNow(),
                             ui::EF_NONE, ui::EF_NONE);
   ui::EventTarget* event_handler =
-      root_window->GetHost()
-          ->dispatcher()
-          ->GetDefaultEventTargeter()
+      static_cast<ui::EventTarget*>(root_window)
+          ->GetEventTargeter()
           ->FindTargetForEvent(root_window, &test_event);
   return WmWindow::Get(static_cast<aura::Window*>(event_handler));
 }
diff --git a/ash/shelf/shelf_widget_unittest.cc b/ash/shelf/shelf_widget_unittest.cc
index 2e8941af..69e4315 100644
--- a/ash/shelf/shelf_widget_unittest.cc
+++ b/ash/shelf/shelf_widget_unittest.cc
@@ -214,9 +214,8 @@
   gfx::Rect widget_bounds = widget->GetWindowBoundsInScreen();
   EXPECT_TRUE(widget_bounds.Intersects(shelf_bounds));
 
-  aura::Window* root = widget->GetNativeWindow()->GetRootWindow();
-  ui::EventTargeter* targeter =
-      root->GetHost()->dispatcher()->GetDefaultEventTargeter();
+  ui::EventTarget* root = widget->GetNativeWindow()->GetRootWindow();
+  ui::EventTargeter* targeter = root->GetEventTargeter();
   {
     // Create a mouse-event targeting the top of the shelf widget. The
     // window-targeter should find |widget| as the target (instead of the
@@ -291,9 +290,8 @@
   widget->Init(params);
   widget->Show();
 
-  aura::Window* root = shelf_widget->GetNativeWindow()->GetRootWindow();
-  ui::EventTargeter* targeter =
-      root->GetHost()->dispatcher()->GetDefaultEventTargeter();
+  ui::EventTarget* root = shelf_widget->GetNativeWindow()->GetRootWindow();
+  ui::EventTargeter* targeter = root->GetEventTargeter();
   // Touch just over the shelf. Since the shelf is visible, the window-targeter
   // should not find the shelf as the target.
   {
diff --git a/ash/wm/immersive_fullscreen_controller_unittest.cc b/ash/wm/immersive_fullscreen_controller_unittest.cc
index fc97085..a9665e5 100644
--- a/ash/wm/immersive_fullscreen_controller_unittest.cc
+++ b/ash/wm/immersive_fullscreen_controller_unittest.cc
@@ -736,9 +736,8 @@
 
   ui::TouchEvent touch(ui::ET_TOUCH_MOVED, gfx::Point(10, top), 0,
                        ui::EventTimeForNow());
-  aura::Window* root = window()->GetRootWindow();
-  ui::EventTargeter* targeter =
-      root->GetHost()->dispatcher()->GetDefaultEventTargeter();
+  ui::EventTarget* root = window()->GetRootWindow();
+  ui::EventTargeter* targeter = root->GetEventTargeter();
   EXPECT_EQ(window(), targeter->FindTargetForEvent(root, &touch));
 
   SetEnabled(true);
diff --git a/ash/wm/overview/window_selector_unittest.cc b/ash/wm/overview/window_selector_unittest.cc
index 61f5d17b..154ef43 100644
--- a/ash/wm/overview/window_selector_unittest.cc
+++ b/ash/wm/overview/window_selector_unittest.cc
@@ -1028,8 +1028,7 @@
                         ui::EventTimeForNow(), ui::EF_NONE, ui::EF_NONE);
 
   ui::EventTarget* root_target = root_window;
-  ui::EventTargeter* targeter =
-      root_window->GetHost()->dispatcher()->GetDefaultEventTargeter();
+  ui::EventTargeter* targeter = root_target->GetEventTargeter();
 
   // The event should target the window because we are still not in overview
   // mode.
diff --git a/ash/wm/panels/panel_layout_manager_unittest.cc b/ash/wm/panels/panel_layout_manager_unittest.cc
index 01f0bbc0..5c8c445 100644
--- a/ash/wm/panels/panel_layout_manager_unittest.cc
+++ b/ash/wm/panels/panel_layout_manager_unittest.cc
@@ -839,9 +839,8 @@
   aura::test::TestWindowDelegate delegate;
   std::unique_ptr<aura::Window> w(
       CreatePanelWindowWithDelegate(&delegate, gfx::Rect(0, 0, 200, 200)));
-  aura::Window* root = w->GetRootWindow();
-  ui::EventTargeter* targeter =
-      root->GetHost()->dispatcher()->GetDefaultEventTargeter();
+  ui::EventTarget* root = w->GetRootWindow();
+  ui::EventTargeter* targeter = root->GetEventTargeter();
 
   // Note that the constants used in the touch locations below are
   // arbitrarily-selected small numbers which will ensure the point is
diff --git a/ash/wm/workspace_controller_unittest.cc b/ash/wm/workspace_controller_unittest.cc
index e51dedc49..db4d586 100644
--- a/ash/wm/workspace_controller_unittest.cc
+++ b/ash/wm/workspace_controller_unittest.cc
@@ -1481,9 +1481,8 @@
   ParentWindowInPrimaryRootWindow(second.get());
   second->Show();
 
-  aura::Window* root = first->GetRootWindow();
-  ui::EventTargeter* targeter =
-      root->GetHost()->dispatcher()->GetDefaultEventTargeter();
+  ui::EventTarget* root = first->GetRootWindow();
+  ui::EventTargeter* targeter = root->GetEventTargeter();
 
   // The windows overlap, and |second| is on top of |first|. Events targeted
   // slightly outside the edges of the |second| window should still be targeted
@@ -1531,9 +1530,8 @@
   aura::test::TestWindowDelegate delegate;
   std::unique_ptr<Window> window(
       CreateTestPanel(&delegate, gfx::Rect(20, 10, 100, 50)));
-  aura::Window* root = window->GetRootWindow();
-  ui::EventTargeter* targeter =
-      root->GetHost()->dispatcher()->GetDefaultEventTargeter();
+  ui::EventTarget* root = window->GetRootWindow();
+  ui::EventTargeter* targeter = root->GetEventTargeter();
   const gfx::Rect bounds = window->bounds();
   const int kNumPoints = 5;
   struct {
@@ -1568,9 +1566,8 @@
   aura::test::TestWindowDelegate delegate;
   std::unique_ptr<Window> window(
       CreateTestPanel(&delegate, gfx::Rect(20, 10, 100, 50)));
-  aura::Window* root = window->GetRootWindow();
-  ui::EventTargeter* targeter =
-      root->GetHost()->dispatcher()->GetDefaultEventTargeter();
+  ui::EventTarget* root = window->GetRootWindow();
+  ui::EventTargeter* targeter = root->GetEventTargeter();
   const gfx::Rect bounds = window->bounds();
   const int kNumPoints = 5;
   struct {
@@ -1610,9 +1607,8 @@
       window->GetRootWindow(), kShellWindowId_DockedContainer);
   docked_container->AddChild(window.get());
   window->Show();
-  aura::Window* root = window->GetRootWindow();
-  ui::EventTargeter* targeter =
-      root->GetHost()->dispatcher()->GetDefaultEventTargeter();
+  ui::EventTarget* root = window->GetRootWindow();
+  ui::EventTargeter* targeter = root->GetEventTargeter();
   const gfx::Rect bounds = window->bounds();
   const int kNumPoints = 5;
   struct {
diff --git a/base/task_scheduler/scheduler_worker.cc b/base/task_scheduler/scheduler_worker.cc
index 1d6cfee..3e77436 100644
--- a/base/task_scheduler/scheduler_worker.cc
+++ b/base/task_scheduler/scheduler_worker.cc
@@ -25,8 +25,8 @@
  public:
   ~Thread() override = default;
 
-  static std::unique_ptr<Thread> Create(SchedulerWorker* outer) {
-    std::unique_ptr<Thread> thread(new Thread(outer));
+  static std::unique_ptr<Thread> Create(scoped_refptr<SchedulerWorker> outer) {
+    std::unique_ptr<Thread> thread(new Thread(std::move(outer)));
     thread->Initialize();
     if (thread->thread_handle_.is_null())
       return nullptr;
@@ -38,7 +38,7 @@
     // Set if this thread was detached.
     std::unique_ptr<Thread> detached_thread;
 
-    outer_->delegate_->OnMainEntry(outer_);
+    outer_->delegate_->OnMainEntry(outer_.get());
 
     // A SchedulerWorker starts out waiting for work.
     WaitForWork();
@@ -51,8 +51,7 @@
     }
 #endif
 
-    while (!outer_->task_tracker_->IsShutdownComplete() &&
-           !outer_->should_exit_for_testing_.IsSet()) {
+    while (!outer_->ShouldExit()) {
       DCHECK(outer_);
 
 #if defined(OS_MACOSX)
@@ -62,10 +61,11 @@
       UpdateThreadPriority(GetDesiredThreadPriority());
 
       // Get the sequence containing the next task to execute.
-      scoped_refptr<Sequence> sequence = outer_->delegate_->GetWork(outer_);
+      scoped_refptr<Sequence> sequence =
+          outer_->delegate_->GetWork(outer_.get());
       if (!sequence) {
-        if (outer_->delegate_->CanDetach(outer_)) {
-          detached_thread = outer_->Detach();
+        if (outer_->delegate_->CanDetach(outer_.get())) {
+          detached_thread = outer_->DetachThreadObject(DetachNotify::DELEGATE);
           if (detached_thread) {
             outer_ = nullptr;
             DCHECK_EQ(detached_thread.get(), this);
@@ -106,6 +106,19 @@
     // stuck forever.
     DCHECK(!detached_thread || !IsWakeUpPending()) <<
         "This thread was detached and woken up at the same time.";
+
+    // This thread is generally responsible for cleaning itself up except when
+    // JoinForTesting() is called.
+    // We arrive here in the following cases:
+    // Thread Detachment Request:
+    //   * |detached_thread| will not be nullptr.
+    // ShouldExit() returns true:
+    //   * Shutdown: DetachThreadObject() returns the thread object.
+    //   * Cleanup: DetachThreadObject() returns the thread object.
+    //   * Join: DetachThreadObject() could return either the thread object or
+    //           nullptr. JoinForTesting() cleans up if we get nullptr.
+    if (!detached_thread)
+      detached_thread = outer_->DetachThreadObject(DetachNotify::SILENT);
   }
 
   void Join() { PlatformThread::Join(thread_handle_); }
@@ -115,8 +128,8 @@
   bool IsWakeUpPending() { return wake_up_event_.IsSignaled(); }
 
  private:
-  Thread(SchedulerWorker* outer)
-      : outer_(outer),
+  Thread(scoped_refptr<SchedulerWorker> outer)
+      : outer_(std::move(outer)),
         wake_up_event_(WaitableEvent::ResetPolicy::MANUAL,
                        WaitableEvent::InitialState::NOT_SIGNALED),
         current_thread_priority_(GetDesiredThreadPriority()) {
@@ -175,7 +188,7 @@
 
   PlatformThreadHandle thread_handle_;
 
-  SchedulerWorker* outer_;
+  scoped_refptr<SchedulerWorker> outer_;
 
   // Event signaled to wake up this thread.
   WaitableEvent wake_up_event_;
@@ -187,15 +200,15 @@
   DISALLOW_COPY_AND_ASSIGN(Thread);
 };
 
-std::unique_ptr<SchedulerWorker> SchedulerWorker::Create(
+scoped_refptr<SchedulerWorker> SchedulerWorker::Create(
     ThreadPriority priority_hint,
     std::unique_ptr<Delegate> delegate,
     TaskTracker* task_tracker,
     InitialState initial_state,
     SchedulerBackwardCompatibility backward_compatibility) {
-  auto worker =
-      WrapUnique(new SchedulerWorker(priority_hint, std::move(delegate),
-                                     task_tracker, backward_compatibility));
+  scoped_refptr<SchedulerWorker> worker(
+      new SchedulerWorker(priority_hint, std::move(delegate), task_tracker,
+                          backward_compatibility));
   // Creation happens before any other thread can reference this one, so no
   // synchronization is necessary.
   if (initial_state == SchedulerWorker::InitialState::ALIVE) {
@@ -208,17 +221,10 @@
   return worker;
 }
 
-SchedulerWorker::~SchedulerWorker() {
-  // It is unexpected for |thread_| to be alive and for SchedulerWorker to
-  // destroy since SchedulerWorker owns the delegate needed by |thread_|.
-  // For testing, this generally means JoinForTesting was not called.
-  DCHECK(!thread_);
-}
-
 void SchedulerWorker::WakeUp() {
   AutoSchedulerLock auto_lock(thread_lock_);
 
-  DCHECK(!should_exit_for_testing_.IsSet());
+  DCHECK(!join_called_for_testing_.IsSet());
 
   if (!thread_)
     CreateThreadAssertSynchronized();
@@ -228,8 +234,8 @@
 }
 
 void SchedulerWorker::JoinForTesting() {
-  DCHECK(!should_exit_for_testing_.IsSet());
-  should_exit_for_testing_.Set();
+  DCHECK(!join_called_for_testing_.IsSet());
+  join_called_for_testing_.Set();
 
   std::unique_ptr<Thread> thread;
 
@@ -238,7 +244,7 @@
 
     if (thread_) {
       // Make sure the thread is awake. It will see that
-      // |should_exit_for_testing_| is set and exit shortly after.
+      // |join_called_for_testing_| is set and exit shortly after.
       thread_->WakeUp();
       thread = std::move(thread_);
     }
@@ -253,6 +259,18 @@
   return !!thread_;
 }
 
+void SchedulerWorker::Cleanup() {
+  // |should_exit_| is synchronized with |thread_| for writes here so that we
+  // can maintain access to |thread_| for wakeup. Otherwise, the thread may take
+  // away |thread_| for destruction.
+  AutoSchedulerLock auto_lock(thread_lock_);
+  DCHECK(!should_exit_.IsSet());
+  if (thread_) {
+    should_exit_.Set();
+    thread_->WakeUp();
+  }
+}
+
 SchedulerWorker::SchedulerWorker(
     ThreadPriority priority_hint,
     std::unique_ptr<Delegate> delegate,
@@ -270,12 +288,20 @@
   DCHECK(task_tracker_);
 }
 
-std::unique_ptr<SchedulerWorker::Thread> SchedulerWorker::Detach() {
+SchedulerWorker::~SchedulerWorker() {
+  // It is unexpected for |thread_| to be alive and for SchedulerWorker to
+  // destroy since SchedulerWorker owns the delegate needed by |thread_|.
+  // For testing, this generally means JoinForTesting was not called.
+  DCHECK(!thread_);
+}
+
+std::unique_ptr<SchedulerWorker::Thread> SchedulerWorker::DetachThreadObject(
+    DetachNotify detach_notify) {
   AutoSchedulerLock auto_lock(thread_lock_);
 
   // Do not detach if the thread is being joined.
   if (!thread_) {
-    DCHECK(should_exit_for_testing_.IsSet());
+    DCHECK(join_called_for_testing_.IsSet());
     return nullptr;
   }
 
@@ -285,15 +311,17 @@
   if (thread_->IsWakeUpPending())
     return nullptr;
 
-  // Call OnDetach() within the scope of |thread_lock_| to prevent the delegate
-  // from being used concurrently from an old and a new thread.
-  delegate_->OnDetach();
+  if (detach_notify == DetachNotify::DELEGATE) {
+    // Call OnDetach() within the scope of |thread_lock_| to prevent the
+    // delegate from being used concurrently from an old and a new thread.
+    delegate_->OnDetach();
+  }
 
   return std::move(thread_);
 }
 
 void SchedulerWorker::CreateThread() {
-  thread_ = Thread::Create(this);
+  thread_ = Thread::Create(make_scoped_refptr(this));
 }
 
 void SchedulerWorker::CreateThreadAssertSynchronized() {
@@ -301,5 +329,10 @@
   CreateThread();
 }
 
+bool SchedulerWorker::ShouldExit() {
+  return task_tracker_->IsShutdownComplete() ||
+         join_called_for_testing_.IsSet() || should_exit_.IsSet();
+}
+
 }  // namespace internal
 }  // namespace base
diff --git a/base/task_scheduler/scheduler_worker.h b/base/task_scheduler/scheduler_worker.h
index f8174de9..0f9bc7f 100644
--- a/base/task_scheduler/scheduler_worker.h
+++ b/base/task_scheduler/scheduler_worker.h
@@ -37,7 +37,8 @@
 // guidance from the delegate.
 //
 // This class is thread-safe.
-class BASE_EXPORT SchedulerWorker {
+class BASE_EXPORT SchedulerWorker
+    : public RefCountedThreadSafe<SchedulerWorker> {
  public:
   // Delegate interface for SchedulerWorker. The methods are always called from
   // the thread managed by the SchedulerWorker instance.
@@ -95,8 +96,9 @@
   // |worker_state| is DETACHED, the thread will be created upon a WakeUp().
   // Returns nullptr if creating the underlying platform thread fails during
   // Create(). |backward_compatibility| indicates whether backward compatibility
-  // is enabled.
-  static std::unique_ptr<SchedulerWorker> Create(
+  // is enabled. Either JoinForTesting() or Cleanup() must be called before
+  // releasing the last external reference.
+  static scoped_refptr<SchedulerWorker> Create(
       ThreadPriority priority_hint,
       std::unique_ptr<Delegate> delegate,
       TaskTracker* task_tracker,
@@ -104,11 +106,6 @@
       SchedulerBackwardCompatibility backward_compatibility =
           SchedulerBackwardCompatibility::DISABLED);
 
-  // Destroying a SchedulerWorker in production is not allowed; it is always
-  // leaked. In tests, it can only be destroyed after JoinForTesting() has
-  // returned.
-  ~SchedulerWorker();
-
   // Wakes up this SchedulerWorker if it wasn't already awake. After this
   // is called, this SchedulerWorker will run Tasks from Sequences
   // returned by the GetWork() method of its delegate until it returns nullptr.
@@ -129,27 +126,55 @@
   // Returns true if the worker is alive.
   bool ThreadAliveForTesting() const;
 
+  // Makes a request to cleanup the worker. This may be called from any thread.
+  // The caller is expected to release its reference to this object after
+  // calling Cleanup(). Further method calls after Cleanup() returns are
+  // undefined.
+  //
+  // Expected Usage:
+  //   scoped_refptr<SchedulerWorker> worker_ = /* Existing Worker */
+  //   worker_->Cleanup();
+  //   worker_ = nullptr;
+  void Cleanup();
+
  private:
+  friend class RefCountedThreadSafe<SchedulerWorker>;
   class Thread;
+  enum class DetachNotify {
+    // Do not notify any component.
+    SILENT,
+    // Notify the delegate.
+    DELEGATE,
+  };
 
   SchedulerWorker(ThreadPriority thread_priority,
                   std::unique_ptr<Delegate> delegate,
                   TaskTracker* task_tracker,
                   SchedulerBackwardCompatibility backward_compatibility);
+  ~SchedulerWorker();
 
-  // Returns the thread instance if the detach was successful so that it can be
-  // freed upon termination of the thread.
-  // If the detach is not possible, returns nullptr.
-  std::unique_ptr<SchedulerWorker::Thread> Detach();
+  // Returns ownership of the thread instance when appropriate so that it can be
+  // freed upon termination of the thread. If ownership transfer is not
+  // possible, returns nullptr.
+  std::unique_ptr<SchedulerWorker::Thread> DetachThreadObject(
+      DetachNotify detach_notify);
 
   void CreateThread();
 
   void CreateThreadAssertSynchronized();
 
-  // Synchronizes access to |thread_|.
+  bool ShouldExit();
+
+  // Synchronizes access to |thread_| (read+write) as well as |should_exit_|
+  // (write-only). See Cleanup() for details.
   mutable SchedulerLock thread_lock_;
 
+  AtomicFlag should_exit_;
+
   // The underlying thread for this SchedulerWorker.
+  // The thread object will be cleaned up by the running thread unless we join
+  // against the thread. Joining requires the thread object to remain alive for
+  // the Thread::Join() call.
   std::unique_ptr<Thread> thread_;
 
   const ThreadPriority priority_hint_;
@@ -162,7 +187,7 @@
 #endif
 
   // Set once JoinForTesting() has been called.
-  AtomicFlag should_exit_for_testing_;
+  AtomicFlag join_called_for_testing_;
 
   DISALLOW_COPY_AND_ASSIGN(SchedulerWorker);
 };
diff --git a/base/task_scheduler/scheduler_worker_pool_impl.cc b/base/task_scheduler/scheduler_worker_pool_impl.cc
index 8bea1766..22aa2c65 100644
--- a/base/task_scheduler/scheduler_worker_pool_impl.cc
+++ b/base/task_scheduler/scheduler_worker_pool_impl.cc
@@ -130,13 +130,12 @@
 };
 
 // Only used in DCHECKs.
-bool ContainsWorker(
-    const std::vector<std::unique_ptr<SchedulerWorker>>& workers,
-    const SchedulerWorker* worker) {
+bool ContainsWorker(const std::vector<scoped_refptr<SchedulerWorker>>& workers,
+                    const SchedulerWorker* worker) {
   auto it = std::find_if(workers.begin(), workers.end(),
-      [worker](const std::unique_ptr<SchedulerWorker>& i) {
-        return i.get() == worker;
-      });
+                         [worker](const scoped_refptr<SchedulerWorker>& i) {
+                           return i.get() == worker;
+                         });
   return it != workers.end();
 }
 
@@ -697,7 +696,7 @@
         (index == 0 && !is_standby_lazy)
             ? SchedulerWorker::InitialState::ALIVE
             : SchedulerWorker::InitialState::DETACHED;
-    std::unique_ptr<SchedulerWorker> worker = SchedulerWorker::Create(
+    scoped_refptr<SchedulerWorker> worker = SchedulerWorker::Create(
         params.priority_hint(),
         MakeUnique<SchedulerWorkerDelegateImpl>(
             this, re_enqueue_sequence_callback, &shared_priority_queue_, index),
diff --git a/base/task_scheduler/scheduler_worker_pool_impl.h b/base/task_scheduler/scheduler_worker_pool_impl.h
index 253f2020..d4b8440 100644
--- a/base/task_scheduler/scheduler_worker_pool_impl.h
+++ b/base/task_scheduler/scheduler_worker_pool_impl.h
@@ -145,7 +145,7 @@
 
   // All worker owned by this worker pool. Only modified during initialization
   // of the worker pool.
-  std::vector<std::unique_ptr<SchedulerWorker>> workers_;
+  std::vector<scoped_refptr<SchedulerWorker>> workers_;
 
   // Synchronizes access to |next_worker_index_|.
   SchedulerLock next_worker_index_lock_;
diff --git a/base/task_scheduler/scheduler_worker_stack_unittest.cc b/base/task_scheduler/scheduler_worker_stack_unittest.cc
index 520e52c..b7d57ff 100644
--- a/base/task_scheduler/scheduler_worker_stack_unittest.cc
+++ b/base/task_scheduler/scheduler_worker_stack_unittest.cc
@@ -67,9 +67,9 @@
     worker_c_->JoinForTesting();
   }
 
-  std::unique_ptr<SchedulerWorker> worker_a_;
-  std::unique_ptr<SchedulerWorker> worker_b_;
-  std::unique_ptr<SchedulerWorker> worker_c_;
+  scoped_refptr<SchedulerWorker> worker_a_;
+  scoped_refptr<SchedulerWorker> worker_b_;
+  scoped_refptr<SchedulerWorker> worker_c_;
 
  private:
   TaskTracker task_tracker_;
diff --git a/base/task_scheduler/scheduler_worker_unittest.cc b/base/task_scheduler/scheduler_worker_unittest.cc
index b65d50c..b8dea8e 100644
--- a/base/task_scheduler/scheduler_worker_unittest.cc
+++ b/base/task_scheduler/scheduler_worker_unittest.cc
@@ -13,13 +13,16 @@
 #include "base/bind_helpers.h"
 #include "base/macros.h"
 #include "base/memory/ptr_util.h"
+#include "base/memory/ref_counted.h"
 #include "base/synchronization/condition_variable.h"
 #include "base/synchronization/waitable_event.h"
 #include "base/task_scheduler/scheduler_lock.h"
 #include "base/task_scheduler/sequence.h"
 #include "base/task_scheduler/task.h"
 #include "base/task_scheduler/task_tracker.h"
+#include "base/test/test_timeouts.h"
 #include "base/threading/platform_thread.h"
+#include "base/threading/simple_thread.h"
 #include "base/time/time.h"
 #include "build/build_config.h"
 #include "testing/gmock/include/gmock/gmock.h"
@@ -121,7 +124,7 @@
     return re_enqueued_sequences_;
   }
 
-  std::unique_ptr<SchedulerWorker> worker_;
+  scoped_refptr<SchedulerWorker> worker_;
 
  private:
   class TestSchedulerWorkerDelegate : public SchedulerWorkerDefaultDelegate {
@@ -355,34 +358,92 @@
 
 class ControllableDetachDelegate : public SchedulerWorkerDefaultDelegate {
  public:
-  ControllableDetachDelegate(TaskTracker* task_tracker)
-      : task_tracker_(task_tracker),
-        work_processed_(WaitableEvent::ResetPolicy::MANUAL,
-                        WaitableEvent::InitialState::NOT_SIGNALED),
-        detach_requested_(WaitableEvent::ResetPolicy::MANUAL,
+  class Controls : public RefCountedThreadSafe<Controls> {
+   public:
+    Controls()
+        : work_running_(WaitableEvent::ResetPolicy::MANUAL,
+                        WaitableEvent::InitialState::SIGNALED),
+          work_processed_(WaitableEvent::ResetPolicy::MANUAL,
                           WaitableEvent::InitialState::NOT_SIGNALED),
-        detached_(WaitableEvent::ResetPolicy::MANUAL,
-                  WaitableEvent::InitialState::NOT_SIGNALED) {
-    EXPECT_TRUE(task_tracker_);
-  }
+          detach_requested_(WaitableEvent::ResetPolicy::MANUAL,
+                            WaitableEvent::InitialState::NOT_SIGNALED),
+          detached_(WaitableEvent::ResetPolicy::MANUAL,
+                    WaitableEvent::InitialState::NOT_SIGNALED),
+          can_detach_block_(WaitableEvent::ResetPolicy::MANUAL,
+                            WaitableEvent::InitialState::SIGNALED),
+          destroyed_(WaitableEvent::ResetPolicy::MANUAL,
+                     WaitableEvent::InitialState::NOT_SIGNALED) {}
 
-  ~ControllableDetachDelegate() override = default;
+    void HaveWorkBlock() { work_running_.Reset(); }
 
-  // SchedulerWorker::Delegate:
-  MOCK_METHOD1(OnMainEntry, void(SchedulerWorker* worker));
+    void UnblockWork() { work_running_.Signal(); }
+
+    void MakeCanDetachBlock() { can_detach_block_.Reset(); }
+
+    void UnblockCanDetach() { can_detach_block_.Signal(); }
+
+    void WaitForWorkToRun() { work_processed_.Wait(); }
+
+    void WaitForDetachRequest() { detach_requested_.Wait(); }
+
+    void WaitForDetach() { detached_.Wait(); }
+
+    void WaitForDelegateDestroy() { destroyed_.Wait(); }
+
+    void ResetState() {
+      work_running_.Signal();
+      work_processed_.Reset();
+      detach_requested_.Reset();
+      can_detach_block_.Signal();
+      work_requested_ = false;
+    }
+
+    void set_can_detach(bool can_detach) { can_detach_ = can_detach; }
+
+   private:
+    friend class ControllableDetachDelegate;
+    friend class RefCountedThreadSafe<Controls>;
+    ~Controls() = default;
+
+    WaitableEvent work_running_;
+    WaitableEvent work_processed_;
+    WaitableEvent detach_requested_;
+    WaitableEvent detached_;
+    WaitableEvent can_detach_block_;
+    WaitableEvent destroyed_;
+
+    bool can_detach_ = false;
+    bool work_requested_ = false;
+
+    DISALLOW_COPY_AND_ASSIGN(Controls);
+  };
+
+  ControllableDetachDelegate(TaskTracker* task_tracker)
+      : task_tracker_(task_tracker), controls_(new Controls()) {}
+
+  ~ControllableDetachDelegate() override { controls_->destroyed_.Signal(); }
 
   scoped_refptr<Sequence> GetWork(SchedulerWorker* worker)
       override {
     // Sends one item of work to signal |work_processed_|. On subsequent calls,
     // sends nullptr to indicate there's no more work to be done.
-    if (work_requested_)
+    if (controls_->work_requested_)
       return nullptr;
 
-    work_requested_ = true;
+    controls_->work_requested_ = true;
     scoped_refptr<Sequence> sequence(new Sequence);
     std::unique_ptr<Task> task(new Task(
-        FROM_HERE, Bind(&WaitableEvent::Signal, Unretained(&work_processed_)),
-        TaskTraits(), TimeDelta()));
+        FROM_HERE,
+        Bind(
+            [](WaitableEvent* work_processed, WaitableEvent* work_running) {
+              work_processed->Signal();
+              work_running->Wait();
+            },
+            Unretained(&controls_->work_processed_),
+            Unretained(&controls_->work_running_)),
+        TaskTraits().WithBaseSyncPrimitives().WithShutdownBehavior(
+            TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN),
+        TimeDelta()));
     EXPECT_TRUE(task_tracker_->WillPostTask(task.get()));
     sequence->PushTask(std::move(task));
     return sequence;
@@ -391,94 +452,275 @@
   void DidRunTask() override {}
 
   bool CanDetach(SchedulerWorker* worker) override {
-    detach_requested_.Signal();
-    return can_detach_;
+    // Saving |can_detach_| now so that callers waiting on |detach_requested_|
+    // have the thread go to sleep and then allow detachment.
+    bool can_detach = controls_->can_detach_;
+    controls_->detach_requested_.Signal();
+    controls_->can_detach_block_.Wait();
+    return can_detach;
   }
 
   void OnDetach() override {
-    EXPECT_TRUE(can_detach_);
-    EXPECT_TRUE(detach_requested_.IsSignaled());
-    detached_.Signal();
+    EXPECT_TRUE(controls_->can_detach_);
+    EXPECT_TRUE(controls_->detach_requested_.IsSignaled());
+    controls_->detached_.Signal();
   }
 
-  void WaitForWorkToRun() {
-    work_processed_.Wait();
-  }
-
-  void WaitForDetachRequest() {
-    detach_requested_.Wait();
-  }
-
-  void WaitForDetach() { detached_.Wait(); }
-
-  void ResetState() {
-    work_requested_ = false;
-    work_processed_.Reset();
-    detach_requested_.Reset();
-  }
-
-  void set_can_detach(bool can_detach) { can_detach_ = can_detach; }
+  // ControllableDetachDelegate:
+  scoped_refptr<Controls> controls() { return controls_; }
 
  private:
+  scoped_refptr<Sequence> work_sequence_;
   TaskTracker* const task_tracker_;
-  bool work_requested_ = false;
-  bool can_detach_ = false;
-  WaitableEvent work_processed_;
-  WaitableEvent detach_requested_;
-  WaitableEvent detached_;
+  scoped_refptr<Controls> controls_;
 
   DISALLOW_COPY_AND_ASSIGN(ControllableDetachDelegate);
 };
 
+class MockedControllableDetachDelegate : public ControllableDetachDelegate {
+ public:
+  MockedControllableDetachDelegate(TaskTracker* task_tracker)
+      : ControllableDetachDelegate(task_tracker){};
+  ~MockedControllableDetachDelegate() = default;
+
+  // SchedulerWorker::Delegate:
+  MOCK_METHOD1(OnMainEntry, void(SchedulerWorker* worker));
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(MockedControllableDetachDelegate);
+};
+
 }  // namespace
 
 TEST(TaskSchedulerWorkerTest, WorkerDetaches) {
   TaskTracker task_tracker;
   // Will be owned by SchedulerWorker.
-  ControllableDetachDelegate* delegate =
-      new StrictMock<ControllableDetachDelegate>(&task_tracker);
-  delegate->set_can_detach(true);
+  MockedControllableDetachDelegate* delegate =
+      new StrictMock<MockedControllableDetachDelegate>(&task_tracker);
+  scoped_refptr<ControllableDetachDelegate::Controls> controls =
+      delegate->controls();
+  controls->set_can_detach(true);
   EXPECT_CALL(*delegate, OnMainEntry(_));
-  std::unique_ptr<SchedulerWorker> worker =
-      SchedulerWorker::Create(
-          ThreadPriority::NORMAL, WrapUnique(delegate), &task_tracker,
-          SchedulerWorker::InitialState::ALIVE);
+  scoped_refptr<SchedulerWorker> worker = SchedulerWorker::Create(
+      ThreadPriority::NORMAL, WrapUnique(delegate), &task_tracker,
+      SchedulerWorker::InitialState::ALIVE);
   worker->WakeUp();
-  delegate->WaitForWorkToRun();
+  controls->WaitForWorkToRun();
   Mock::VerifyAndClear(delegate);
-  delegate->WaitForDetachRequest();
-  delegate->WaitForDetach();
+  controls->WaitForDetachRequest();
+  controls->WaitForDetach();
   ASSERT_FALSE(worker->ThreadAliveForTesting());
 }
 
+TEST(TaskSchedulerWorkerTest, WorkerCleanupBeforeDetach) {
+  TaskTracker task_tracker;
+  // Will be owned by SchedulerWorker.
+  // No mock here as that's reasonably covered by other tests and the delegate
+  // may destroy on a different thread. Mocks aren't designed with that in mind.
+  std::unique_ptr<ControllableDetachDelegate> delegate =
+      MakeUnique<ControllableDetachDelegate>(&task_tracker);
+  scoped_refptr<ControllableDetachDelegate::Controls> controls =
+      delegate->controls();
+
+  controls->set_can_detach(true);
+  controls->MakeCanDetachBlock();
+
+  scoped_refptr<SchedulerWorker> worker = SchedulerWorker::Create(
+      ThreadPriority::NORMAL, std::move(delegate), &task_tracker,
+      SchedulerWorker::InitialState::ALIVE);
+  worker->WakeUp();
+
+  controls->WaitForDetachRequest();
+  worker->Cleanup();
+  worker = nullptr;
+  controls->UnblockCanDetach();
+  controls->WaitForDelegateDestroy();
+}
+
+TEST(TaskSchedulerWorkerTest, WorkerCleanupAfterDetach) {
+  TaskTracker task_tracker;
+  // Will be owned by SchedulerWorker.
+  // No mock here as that's reasonably covered by other tests and the delegate
+  // may destroy on a different thread. Mocks aren't designed with that in mind.
+  std::unique_ptr<ControllableDetachDelegate> delegate =
+      MakeUnique<ControllableDetachDelegate>(&task_tracker);
+  scoped_refptr<ControllableDetachDelegate::Controls> controls =
+      delegate->controls();
+
+  controls->set_can_detach(true);
+
+  scoped_refptr<SchedulerWorker> worker = SchedulerWorker::Create(
+      ThreadPriority::NORMAL, std::move(delegate), &task_tracker,
+      SchedulerWorker::InitialState::ALIVE);
+  worker->WakeUp();
+
+  controls->WaitForDetach();
+  worker->Cleanup();
+  worker = nullptr;
+  controls->WaitForDelegateDestroy();
+}
+
+TEST(TaskSchedulerWorkerTest, WorkerCleanupDuringWork) {
+  TaskTracker task_tracker;
+  // Will be owned by SchedulerWorker.
+  // No mock here as that's reasonably covered by other tests and the delegate
+  // may destroy on a different thread. Mocks aren't designed with that in mind.
+  std::unique_ptr<ControllableDetachDelegate> delegate =
+      MakeUnique<ControllableDetachDelegate>(&task_tracker);
+  scoped_refptr<ControllableDetachDelegate::Controls> controls =
+      delegate->controls();
+
+  controls->HaveWorkBlock();
+
+  scoped_refptr<SchedulerWorker> worker = SchedulerWorker::Create(
+      ThreadPriority::NORMAL, std::move(delegate), &task_tracker,
+      SchedulerWorker::InitialState::ALIVE);
+  worker->WakeUp();
+
+  controls->WaitForWorkToRun();
+  worker->Cleanup();
+  worker = nullptr;
+  controls->UnblockWork();
+  controls->WaitForDelegateDestroy();
+}
+
+TEST(TaskSchedulerWorkerTest, WorkerCleanupDuringWait) {
+  TaskTracker task_tracker;
+  // Will be owned by SchedulerWorker.
+  // No mock here as that's reasonably covered by other tests and the delegate
+  // may destroy on a different thread. Mocks aren't designed with that in mind.
+  std::unique_ptr<ControllableDetachDelegate> delegate =
+      MakeUnique<ControllableDetachDelegate>(&task_tracker);
+  scoped_refptr<ControllableDetachDelegate::Controls> controls =
+      delegate->controls();
+
+  scoped_refptr<SchedulerWorker> worker = SchedulerWorker::Create(
+      ThreadPriority::NORMAL, std::move(delegate), &task_tracker,
+      SchedulerWorker::InitialState::ALIVE);
+  worker->WakeUp();
+
+  controls->WaitForDetachRequest();
+  worker->Cleanup();
+  worker = nullptr;
+  controls->WaitForDelegateDestroy();
+}
+
+TEST(TaskSchedulerWorkerTest, WorkerCleanupDuringShutdown) {
+  TaskTracker task_tracker;
+  // Will be owned by SchedulerWorker.
+  // No mock here as that's reasonably covered by other tests and the delegate
+  // may destroy on a different thread. Mocks aren't designed with that in mind.
+  std::unique_ptr<ControllableDetachDelegate> delegate =
+      MakeUnique<ControllableDetachDelegate>(&task_tracker);
+  scoped_refptr<ControllableDetachDelegate::Controls> controls =
+      delegate->controls();
+
+  controls->HaveWorkBlock();
+
+  scoped_refptr<SchedulerWorker> worker = SchedulerWorker::Create(
+      ThreadPriority::NORMAL, std::move(delegate), &task_tracker,
+      SchedulerWorker::InitialState::ALIVE);
+  worker->WakeUp();
+
+  controls->WaitForWorkToRun();
+  task_tracker.Shutdown();
+  worker->Cleanup();
+  worker = nullptr;
+  controls->UnblockWork();
+  controls->WaitForDelegateDestroy();
+}
+
+namespace {
+
+class CallJoinFromDifferentThread : public SimpleThread {
+ public:
+  CallJoinFromDifferentThread(SchedulerWorker* worker_to_join)
+      : SimpleThread("SchedulerWorkerJoinThread"),
+        worker_to_join_(worker_to_join),
+        run_started_event_(WaitableEvent::ResetPolicy::MANUAL,
+                           WaitableEvent::InitialState::NOT_SIGNALED) {}
+
+  ~CallJoinFromDifferentThread() override = default;
+
+  void Run() override {
+    run_started_event_.Signal();
+    worker_to_join_->JoinForTesting();
+  }
+
+  void WaitForRunToStart() { run_started_event_.Wait(); }
+
+ private:
+  SchedulerWorker* const worker_to_join_;
+  WaitableEvent run_started_event_;
+  DISALLOW_COPY_AND_ASSIGN(CallJoinFromDifferentThread);
+};
+
+}  // namespace
+
+TEST(TaskSchedulerWorkerTest, WorkerCleanupDuringJoin) {
+  TaskTracker task_tracker;
+  // Will be owned by SchedulerWorker.
+  // No mock here as that's reasonably covered by other tests and the
+  // delegate may destroy on a different thread. Mocks aren't designed with that
+  // in mind.
+  std::unique_ptr<ControllableDetachDelegate> delegate =
+      MakeUnique<ControllableDetachDelegate>(&task_tracker);
+  scoped_refptr<ControllableDetachDelegate::Controls> controls =
+      delegate->controls();
+
+  controls->HaveWorkBlock();
+
+  scoped_refptr<SchedulerWorker> worker = SchedulerWorker::Create(
+      ThreadPriority::NORMAL, std::move(delegate), &task_tracker,
+      SchedulerWorker::InitialState::ALIVE);
+  worker->WakeUp();
+
+  controls->WaitForWorkToRun();
+  CallJoinFromDifferentThread join_from_different_thread(worker.get());
+  join_from_different_thread.Start();
+  join_from_different_thread.WaitForRunToStart();
+  // Sleep here to give the other thread a chance to call JoinForTesting().
+  // Receiving a signal that Run() was called doesn't mean JoinForTesting() was
+  // necessarily called, and we can't signal after JoinForTesting() as
+  // JoinForTesting() blocks until we call UnblockWork().
+  PlatformThread::Sleep(TestTimeouts::tiny_timeout());
+  worker->Cleanup();
+  worker = nullptr;
+  controls->UnblockWork();
+  controls->WaitForDelegateDestroy();
+  join_from_different_thread.Join();
+}
+
 TEST(TaskSchedulerWorkerTest, WorkerDetachesAndWakes) {
   TaskTracker task_tracker;
   // Will be owned by SchedulerWorker.
-  ControllableDetachDelegate* delegate =
-      new StrictMock<ControllableDetachDelegate>(&task_tracker);
-  delegate->set_can_detach(true);
+  MockedControllableDetachDelegate* delegate =
+      new StrictMock<MockedControllableDetachDelegate>(&task_tracker);
+  scoped_refptr<ControllableDetachDelegate::Controls> controls =
+      delegate->controls();
+
+  controls->set_can_detach(true);
   EXPECT_CALL(*delegate, OnMainEntry(_));
-  std::unique_ptr<SchedulerWorker> worker =
-      SchedulerWorker::Create(
-          ThreadPriority::NORMAL, WrapUnique(delegate), &task_tracker,
-          SchedulerWorker::InitialState::ALIVE);
+  scoped_refptr<SchedulerWorker> worker = SchedulerWorker::Create(
+      ThreadPriority::NORMAL, WrapUnique(delegate), &task_tracker,
+      SchedulerWorker::InitialState::ALIVE);
   worker->WakeUp();
-  delegate->WaitForWorkToRun();
+  controls->WaitForWorkToRun();
   Mock::VerifyAndClear(delegate);
-  delegate->WaitForDetachRequest();
-  delegate->WaitForDetach();
+  controls->WaitForDetachRequest();
+  controls->WaitForDetach();
   ASSERT_FALSE(worker->ThreadAliveForTesting());
 
-  delegate->ResetState();
-  delegate->set_can_detach(false);
+  controls->ResetState();
+  controls->set_can_detach(false);
   // Expect OnMainEntry() to be called when SchedulerWorker recreates its
   // thread.
   EXPECT_CALL(*delegate, OnMainEntry(worker.get()));
   worker->WakeUp();
-  delegate->WaitForWorkToRun();
+  controls->WaitForWorkToRun();
   Mock::VerifyAndClear(delegate);
-  delegate->WaitForDetachRequest();
-  delegate->WaitForDetach();
+  controls->WaitForDetachRequest();
+  controls->WaitForDetach();
   ASSERT_TRUE(worker->ThreadAliveForTesting());
   worker->JoinForTesting();
 }
@@ -486,18 +728,19 @@
 TEST(TaskSchedulerWorkerTest, CreateDetached) {
   TaskTracker task_tracker;
   // Will be owned by SchedulerWorker.
-  ControllableDetachDelegate* delegate =
-      new StrictMock<ControllableDetachDelegate>(&task_tracker);
-  std::unique_ptr<SchedulerWorker> worker =
-      SchedulerWorker::Create(
-          ThreadPriority::NORMAL, WrapUnique(delegate), &task_tracker,
-          SchedulerWorker::InitialState::DETACHED);
+  MockedControllableDetachDelegate* delegate =
+      new StrictMock<MockedControllableDetachDelegate>(&task_tracker);
+  scoped_refptr<ControllableDetachDelegate::Controls> controls =
+      delegate->controls();
+  scoped_refptr<SchedulerWorker> worker = SchedulerWorker::Create(
+      ThreadPriority::NORMAL, WrapUnique(delegate), &task_tracker,
+      SchedulerWorker::InitialState::DETACHED);
   ASSERT_FALSE(worker->ThreadAliveForTesting());
   EXPECT_CALL(*delegate, OnMainEntry(worker.get()));
   worker->WakeUp();
-  delegate->WaitForWorkToRun();
+  controls->WaitForWorkToRun();
   Mock::VerifyAndClear(delegate);
-  delegate->WaitForDetachRequest();
+  controls->WaitForDetachRequest();
   ASSERT_TRUE(worker->ThreadAliveForTesting());
   worker->JoinForTesting();
 }
@@ -560,7 +803,7 @@
           ? ThreadPriority::BACKGROUND
           : ThreadPriority::NORMAL);
 
-  std::unique_ptr<SchedulerWorker> worker = SchedulerWorker::Create(
+  scoped_refptr<SchedulerWorker> worker = SchedulerWorker::Create(
       ThreadPriority::BACKGROUND, std::move(delegate), &task_tracker,
       SchedulerWorker::InitialState::ALIVE);
 
@@ -587,7 +830,7 @@
   delegate_raw->SetExpectedThreadPriority(ThreadPriority::NORMAL);
 
   // Create a DETACHED thread.
-  std::unique_ptr<SchedulerWorker> worker = SchedulerWorker::Create(
+  scoped_refptr<SchedulerWorker> worker = SchedulerWorker::Create(
       ThreadPriority::BACKGROUND, std::move(delegate), &task_tracker,
       SchedulerWorker::InitialState::DETACHED);
 
diff --git a/base/threading/platform_thread_win.cc b/base/threading/platform_thread_win.cc
index 2644eee..e271fe8 100644
--- a/base/threading/platform_thread_win.cc
+++ b/base/threading/platform_thread_win.cc
@@ -10,6 +10,7 @@
 #include "base/debug/alias.h"
 #include "base/debug/profiler.h"
 #include "base/logging.h"
+#include "base/strings/utf_string_conversions.h"
 #include "base/threading/thread_id_name_manager.h"
 #include "base/threading/thread_restrictions.h"
 #include "base/tracked_objects.h"
@@ -30,6 +31,10 @@
   DWORD dwFlags;  // Reserved for future use, must be zero.
 } THREADNAME_INFO;
 
+// The SetThreadDescription API was brought in version 1607 of Windows 10.
+typedef HRESULT(WINAPI* SetThreadDescription)(HANDLE hThread,
+                                              PCWSTR lpThreadDescription);
+
 // This function has try handling, so it is separated out of its caller.
 void SetNameInternal(PlatformThreadId thread_id, const char* name) {
   THREADNAME_INFO info;
@@ -172,6 +177,15 @@
   if (name != "BrokerEvent")
     tracked_objects::ThreadData::InitializeThreadContext(name);
 
+  // The SetThreadDescription API works even if no debugger is attached.
+  auto set_thread_description_func =
+      reinterpret_cast<SetThreadDescription>(::GetProcAddress(
+          ::GetModuleHandle(L"Kernel32.dll"), "SetThreadDescription"));
+  if (set_thread_description_func) {
+    set_thread_description_func(::GetCurrentThread(),
+                                base::UTF8ToWide(name).c_str());
+  }
+
   // The debugger needs to be around to catch the name in the exception.  If
   // there isn't a debugger, we are just needlessly throwing an exception.
   // If this image file is instrumented, we raise the exception anyway
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/ui/DownloadManagerUi.java b/chrome/android/java/src/org/chromium/chrome/browser/download/ui/DownloadManagerUi.java
index 358e0855..291908a 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/ui/DownloadManagerUi.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/ui/DownloadManagerUi.java
@@ -426,8 +426,6 @@
 
         mHistoryAdapter.markItemsForDeletion(itemsToDelete);
 
-        dismissUndoDeletionSnackbars();
-
         boolean singleItemDeleted = selectedItems.size() == 1;
         String snackbarText = singleItemDeleted ? selectedItems.get(0).getDisplayFileName() :
                 String.format(Locale.getDefault(), "%d", selectedItems.size());
diff --git a/chrome/app/settings_strings.grdp b/chrome/app/settings_strings.grdp
index 99a1b38..2965899 100644
--- a/chrome/app/settings_strings.grdp
+++ b/chrome/app/settings_strings.grdp
@@ -1273,6 +1273,15 @@
     <message name="IDS_SETTINGS_INTERNET_KNOWN_NETWORKS_MENU_FORGET" desc="Settings > Internet > Known networks > Dropdown menu item to forget a known network.">
       Forget
     </message>
+    <message name="IDS_SETTINGS_INTERNET_TOGGLE_MOBILE_ACCESSIBILITY_LABEL" desc="Accessibility only label for mobile data enable/disable toggle .">
+      Mobile data enable
+    </message>
+    <message name="IDS_SETTINGS_INTERNET_TOGGLE_WIFI_ACCESSIBILITY_LABEL" desc="Accessibility only label for WiFi enable/disable toggle .">
+      Wi-Fi enable
+    </message>
+    <message name="IDS_SETTINGS_INTERNET_TOGGLE_WIMAX_ACCESSIBILITY_LABEL" desc="Accessibility only label for WiMAX enable/disable toggle .">
+      WiMAX enable
+    </message>
 
      <!-- Strings translating ONC (Open Network Configuration) properties -->
      <!-- and translatable values. See onc_spec.html for more information. -->
diff --git a/chrome/browser/android/vr_shell/easing.cc b/chrome/browser/android/vr_shell/easing.cc
index 8138c6dd..137bcb5f 100644
--- a/chrome/browser/android/vr_shell/easing.cc
+++ b/chrome/browser/android/vr_shell/easing.cc
@@ -33,6 +33,15 @@
   return 1.0 - pow(1.0 - state, power_);
 }
 
+EaseInOut::EaseInOut(double power) : ease_in_(power) {}
+double EaseInOut::CalculateValueImpl(double state) {
+  if (state < 0.5) {
+    return ease_in_.CalculateValueImpl(state * 2) / 2;
+  } else {
+    return 1.0 - ease_in_.CalculateValueImpl((1.0 - state) * 2) / 2;
+  }
+}
+
 double Linear::CalculateValueImpl(double state) {
   return state;
 }
diff --git a/chrome/browser/android/vr_shell/easing.h b/chrome/browser/android/vr_shell/easing.h
index 2ef0617cc..0698154 100644
--- a/chrome/browser/android/vr_shell/easing.h
+++ b/chrome/browser/android/vr_shell/easing.h
@@ -16,6 +16,7 @@
   CUBICBEZIER,
   EASEIN,
   EASEOUT,
+  EASEINOUT,
 };
 
 // Abstract base class for custom interpolators, mapping linear input between
@@ -78,6 +79,17 @@
   DISALLOW_COPY_AND_ASSIGN(EaseOut);
 };
 
+// Starts with EaseIn and finishes with EaseOut.
+class EaseInOut : public Easing {
+ public:
+  explicit EaseInOut(double power);
+  double CalculateValueImpl(double input) override;
+
+ private:
+  EaseIn ease_in_;
+  DISALLOW_COPY_AND_ASSIGN(EaseInOut);
+};
+
 }  // namespace easing
 }  // namespace vr_shell
 
diff --git a/chrome/browser/android/vr_shell/ui_scene.cc b/chrome/browser/android/vr_shell/ui_scene.cc
index 99d0dab..62ebe82 100644
--- a/chrome/browser/android/vr_shell/ui_scene.cc
+++ b/chrome/browser/android/vr_shell/ui_scene.cc
@@ -7,6 +7,7 @@
 #include <string>
 #include <utility>
 
+#include "base/memory/ptr_util.h"
 #include "base/values.h"
 #include "chrome/browser/android/vr_shell/animation.h"
 #include "chrome/browser/android/vr_shell/easing.h"
@@ -105,7 +106,7 @@
 
   switch (easingType) {
     case easing::EasingType::LINEAR: {
-      result.reset(new easing::Linear());
+      result = base::MakeUnique<easing::Linear>();
       break;
     }
     case easing::EasingType::CUBICBEZIER: {
@@ -114,19 +115,25 @@
       CHECK(dict.GetDouble("p1y", &p1y));
       CHECK(dict.GetDouble("p2x", &p2x));
       CHECK(dict.GetDouble("p2y", &p2y));
-      result.reset(new easing::CubicBezier(p1x, p1y, p2x, p2y));
+      result = base::MakeUnique<easing::CubicBezier>(p1x, p1y, p2x, p2y);
       break;
     }
     case easing::EasingType::EASEIN: {
       double pow;
       CHECK(dict.GetDouble("pow", &pow));
-      result.reset(new easing::EaseIn(pow));
+      result = base::MakeUnique<easing::EaseIn>(pow);
       break;
     }
     case easing::EasingType::EASEOUT: {
       double pow;
       CHECK(dict.GetDouble("pow", &pow));
-      result.reset(new easing::EaseOut(pow));
+      result = base::MakeUnique<easing::EaseOut>(pow);
+      break;
+    }
+    case easing::EasingType::EASEINOUT: {
+      double pow;
+      CHECK(dict.GetDouble("pow", &pow));
+      result = base::MakeUnique<easing::EaseInOut>(pow);
       break;
     }
   }
@@ -184,7 +191,7 @@
   CHECK(ParseInt(dict, "id", &id));
   CHECK_EQ(GetUiElementById(id), nullptr);
 
-  std::unique_ptr<ContentRectangle> element(new ContentRectangle);
+  auto element = base::MakeUnique<ContentRectangle>();
   element->id = id;
 
   ApplyDictToElement(dict, element.get());
@@ -259,9 +266,9 @@
 
   ContentRectangle* element = GetUiElementById(element_id);
   CHECK_NE(element, nullptr);
-  element->animations.emplace_back(std::unique_ptr<Animation>(
-      new Animation(animation_id, static_cast<Animation::Property>(property),
-                    std::move(easing), from, to, start, duration)));
+  element->animations.emplace_back(base::MakeUnique<Animation>(
+      animation_id, static_cast<Animation::Property>(property),
+      std::move(easing), from, to, start, duration));
 }
 
 void UiScene::RemoveAnimation(int element_id, int animation_id) {
diff --git a/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate_unittest.cc b/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate_unittest.cc
index 70a3d60..2eb290a 100644
--- a/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate_unittest.cc
+++ b/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate_unittest.cc
@@ -581,7 +581,7 @@
                     ContentSettingsType permission,
                     ContentSetting expected_setting) {
     EXPECT_EQ(expected_setting,
-              autoblocker_->GetEmbargoResult(permission, url).content_setting);
+              autoblocker_->GetEmbargoResult(url, permission).content_setting);
   }
 
  private:
diff --git a/chrome/browser/chromeos/settings/device_settings_test_helper.cc b/chrome/browser/chromeos/settings/device_settings_test_helper.cc
index 6ce8971..e81af3b 100644
--- a/chrome/browser/chromeos/settings/device_settings_test_helper.cc
+++ b/chrome/browser/chromeos/settings/device_settings_test_helper.cc
@@ -92,62 +92,11 @@
   return false;
 }
 
-void DeviceSettingsTestHelper::Init(dbus::Bus* bus) {}
-
-void DeviceSettingsTestHelper::SetStubDelegate(
-    SessionManagerClient::StubDelegate* delegate) {}
-
-void DeviceSettingsTestHelper::AddObserver(Observer* observer) {}
-
-void DeviceSettingsTestHelper::RemoveObserver(Observer* observer) {}
-
-bool DeviceSettingsTestHelper::HasObserver(const Observer* observer) const {
-  return false;
-}
-
-bool DeviceSettingsTestHelper::IsScreenLocked() const { return false; }
-
-void DeviceSettingsTestHelper::EmitLoginPromptVisible() {}
-
-void DeviceSettingsTestHelper::RestartJob(
-    int socket_fd,
-    const std::vector<std::string>& argv,
-    const VoidDBusMethodCallback& callback) {}
-
-void DeviceSettingsTestHelper::StartSession(
-    const cryptohome::Identification& cryptohome_id) {}
-
-void DeviceSettingsTestHelper::StopSession() {}
-
-void DeviceSettingsTestHelper::NotifySupervisedUserCreationStarted() {}
-
-void DeviceSettingsTestHelper::NotifySupervisedUserCreationFinished() {}
-
-void DeviceSettingsTestHelper::StartDeviceWipe() {}
-
-void DeviceSettingsTestHelper::RequestLockScreen() {}
-
-void DeviceSettingsTestHelper::NotifyLockScreenShown() {}
-
-void DeviceSettingsTestHelper::NotifyLockScreenDismissed() {}
-
-void DeviceSettingsTestHelper::RetrieveActiveSessions(
-      const ActiveSessionsCallback& callback) {}
-
 void DeviceSettingsTestHelper::RetrieveDevicePolicy(
     const RetrievePolicyCallback& callback) {
   device_policy_.retrieve_callbacks_.push_back(callback);
 }
 
-void DeviceSettingsTestHelper::RetrievePolicyForUser(
-    const cryptohome::Identification& cryptohome_id,
-    const RetrievePolicyCallback& callback) {}
-
-std::string DeviceSettingsTestHelper::BlockingRetrievePolicyForUser(
-    const cryptohome::Identification& cryptohome_id) {
-  return "";
-}
-
 void DeviceSettingsTestHelper::RetrieveDeviceLocalAccountPolicy(
     const std::string& account_id,
     const RetrievePolicyCallback& callback) {
@@ -162,11 +111,6 @@
   device_policy_.store_callbacks_.push_back(callback);
 }
 
-void DeviceSettingsTestHelper::StorePolicyForUser(
-    const cryptohome::Identification& cryptohome_id,
-    const std::string& policy_blob,
-    const StorePolicyCallback& callback) {}
-
 void DeviceSettingsTestHelper::StoreDeviceLocalAccountPolicy(
     const std::string& account_id,
     const std::string& policy_blob,
@@ -175,42 +119,6 @@
   device_local_account_policy_[account_id].store_callbacks_.push_back(callback);
 }
 
-bool DeviceSettingsTestHelper::SupportsRestartToApplyUserFlags() const {
-  return false;
-}
-
-void DeviceSettingsTestHelper::SetFlagsForUser(
-    const cryptohome::Identification& cryptohome_id,
-    const std::vector<std::string>& flags) {}
-
-void DeviceSettingsTestHelper::GetServerBackedStateKeys(
-    const StateKeysCallback& callback) {}
-
-void DeviceSettingsTestHelper::CheckArcAvailability(
-    const ArcCallback& callback) {}
-
-void DeviceSettingsTestHelper::StartArcInstance(
-    const cryptohome::Identification& cryptohome_id,
-    bool disable_boot_completed_broadcast,
-    const StartArcInstanceCallback& callback) {}
-
-void DeviceSettingsTestHelper::StopArcInstance(const ArcCallback& callback) {}
-
-void DeviceSettingsTestHelper::SetArcCpuRestriction(
-    login_manager::ContainerCpuRestrictionState restriction_state,
-    const ArcCallback& callback) {}
-
-void DeviceSettingsTestHelper::EmitArcBooted(
-    const cryptohome::Identification& cryptohome_id,
-    const ArcCallback& callback) {}
-
-void DeviceSettingsTestHelper::GetArcStartTime(
-    const GetArcStartTimeCallback& callback) {}
-
-void DeviceSettingsTestHelper::RemoveArcData(
-    const cryptohome::Identification& cryptohome_id,
-    const ArcCallback& callback) {}
-
 DeviceSettingsTestHelper::PolicyState::PolicyState()
     : store_result_(true) {}
 
diff --git a/chrome/browser/chromeos/settings/device_settings_test_helper.h b/chrome/browser/chromeos/settings/device_settings_test_helper.h
index 30a54160..d0fbe1d 100644
--- a/chrome/browser/chromeos/settings/device_settings_test_helper.h
+++ b/chrome/browser/chromeos/settings/device_settings_test_helper.h
@@ -19,7 +19,7 @@
 #include "chrome/browser/chromeos/login/users/scoped_user_manager_enabler.h"
 #include "chrome/browser/chromeos/policy/device_policy_builder.h"
 #include "chrome/browser/chromeos/settings/device_settings_service.h"
-#include "chromeos/dbus/session_manager_client.h"
+#include "chromeos/dbus/mock_session_manager_client.h"
 #include "components/ownership/mock_owner_key_util.h"
 #include "content/public/test/test_browser_thread_bundle.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -35,7 +35,7 @@
 // for the SessionManagerClient pointer. The helper records calls made by
 // DeviceSettingsService. The test can then verify state, after which it should
 // call one of the Flush() variants that will resume processing.
-class DeviceSettingsTestHelper : public SessionManagerClient {
+class DeviceSettingsTestHelper : public MockSessionManagerClient {
  public:
   // Wraps a device settings service instance for testing.
   DeviceSettingsTestHelper();
@@ -81,60 +81,16 @@
   }
 
   // SessionManagerClient:
-  void Init(dbus::Bus* bus) override;
-  void SetStubDelegate(SessionManagerClient::StubDelegate* delegate) override;
-  void AddObserver(Observer* observer) override;
-  void RemoveObserver(Observer* observer) override;
-  bool HasObserver(const Observer* observer) const override;
-  bool IsScreenLocked() const override;
-  void EmitLoginPromptVisible() override;
-  void RestartJob(int socket_fd,
-                  const std::vector<std::string>& argv,
-                  const VoidDBusMethodCallback& callback) override;
-  void StartSession(const cryptohome::Identification& cryptohome_id) override;
-  void StopSession() override;
-  void NotifySupervisedUserCreationStarted() override;
-  void NotifySupervisedUserCreationFinished() override;
-  void StartDeviceWipe() override;
-  void RequestLockScreen() override;
-  void NotifyLockScreenShown() override;
-  void NotifyLockScreenDismissed() override;
-  void RetrieveActiveSessions(const ActiveSessionsCallback& callback) override;
   void RetrieveDevicePolicy(const RetrievePolicyCallback& callback) override;
-  void RetrievePolicyForUser(const cryptohome::Identification& cryptohome_id,
-                             const RetrievePolicyCallback& callback) override;
-  std::string BlockingRetrievePolicyForUser(
-      const cryptohome::Identification& cryptohome_id) override;
   void RetrieveDeviceLocalAccountPolicy(
       const std::string& account_id,
       const RetrievePolicyCallback& callback) override;
   void StoreDevicePolicy(const std::string& policy_blob,
                          const StorePolicyCallback& callback) override;
-  void StorePolicyForUser(const cryptohome::Identification& cryptohome_id,
-                          const std::string& policy_blob,
-                          const StorePolicyCallback& callback) override;
   void StoreDeviceLocalAccountPolicy(
       const std::string& account_id,
       const std::string& policy_blob,
       const StorePolicyCallback& callback) override;
-  bool SupportsRestartToApplyUserFlags() const override;
-  void SetFlagsForUser(const cryptohome::Identification& cryptohome_id,
-                       const std::vector<std::string>& flags) override;
-  void GetServerBackedStateKeys(const StateKeysCallback& callback) override;
-
-  void CheckArcAvailability(const ArcCallback& callback) override;
-  void StartArcInstance(const cryptohome::Identification& cryptohome_id,
-                        bool disable_boot_completed_broadcast,
-                        const StartArcInstanceCallback& callback) override;
-  void StopArcInstance(const ArcCallback& callback) override;
-  void SetArcCpuRestriction(
-      login_manager::ContainerCpuRestrictionState restriction_state,
-      const ArcCallback& callback) override;
-  void EmitArcBooted(const cryptohome::Identification& cryptohome_id,
-                     const ArcCallback& callback) override;
-  void GetArcStartTime(const GetArcStartTimeCallback& callback) override;
-  void RemoveArcData(const cryptohome::Identification& cryptohome_id,
-                     const ArcCallback& callback) override;
 
  private:
   struct PolicyState {
diff --git a/chrome/browser/permissions/permission_blacklist_client.cc b/chrome/browser/permissions/permission_blacklist_client.cc
index c57000b..73efc3e 100644
--- a/chrome/browser/permissions/permission_blacklist_client.cc
+++ b/chrome/browser/permissions/permission_blacklist_client.cc
@@ -19,24 +19,23 @@
 
 // static
 void PermissionBlacklistClient::CheckSafeBrowsingBlacklist(
-    scoped_refptr<safe_browsing::SafeBrowsingDatabaseManager> db_manager,
-    ContentSettingsType content_settings_type,
-    const GURL& request_origin,
     content::WebContents* web_contents,
+    scoped_refptr<safe_browsing::SafeBrowsingDatabaseManager> db_manager,
+    const GURL& request_origin,
+    ContentSettingsType content_settings_type,
     int timeout,
     base::Callback<void(bool)> callback) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
 
-  new PermissionBlacklistClient(db_manager, content_settings_type,
-                                request_origin, web_contents, timeout,
-                                callback);
+  new PermissionBlacklistClient(web_contents, db_manager, request_origin,
+                                content_settings_type, timeout, callback);
 }
 
 PermissionBlacklistClient::PermissionBlacklistClient(
-    scoped_refptr<safe_browsing::SafeBrowsingDatabaseManager> db_manager,
-    ContentSettingsType content_settings_type,
-    const GURL& request_origin,
     content::WebContents* web_contents,
+    scoped_refptr<safe_browsing::SafeBrowsingDatabaseManager> db_manager,
+    const GURL& request_origin,
+    ContentSettingsType content_settings_type,
     int timeout,
     base::Callback<void(bool)> callback)
     : content::WebContentsObserver(web_contents),
diff --git a/chrome/browser/permissions/permission_blacklist_client.h b/chrome/browser/permissions/permission_blacklist_client.h
index 87f7333..f0e253854 100644
--- a/chrome/browser/permissions/permission_blacklist_client.h
+++ b/chrome/browser/permissions/permission_blacklist_client.h
@@ -37,10 +37,10 @@
   // callback is run, the profile associated with |web_contents| is guaranteed
   // to be alive.
   static void CheckSafeBrowsingBlacklist(
-      scoped_refptr<safe_browsing::SafeBrowsingDatabaseManager> db_manager,
-      ContentSettingsType content_settings_type,
-      const GURL& request_origin,
       content::WebContents* web_contents,
+      scoped_refptr<safe_browsing::SafeBrowsingDatabaseManager> db_manager,
+      const GURL& request_origin,
+      ContentSettingsType content_settings_type,
       int timeout,
       base::Callback<void(bool)> callback);
 
@@ -48,10 +48,10 @@
   friend class base::RefCountedThreadSafe<PermissionBlacklistClient>;
 
   PermissionBlacklistClient(
-      scoped_refptr<safe_browsing::SafeBrowsingDatabaseManager> db_manager,
-      ContentSettingsType content_settings_type,
-      const GURL& request_origin,
       content::WebContents* web_contents,
+      scoped_refptr<safe_browsing::SafeBrowsingDatabaseManager> db_manager,
+      const GURL& request_origin,
+      ContentSettingsType content_settings_type,
       int timeout,
       base::Callback<void(bool)> callback);
 
diff --git a/chrome/browser/permissions/permission_context_base.cc b/chrome/browser/permissions/permission_context_base.cc
index de06305..006faa07 100644
--- a/chrome/browser/permissions/permission_context_base.cc
+++ b/chrome/browser/permissions/permission_context_base.cc
@@ -119,11 +119,13 @@
 
   // Asynchronously check whether the origin should be blocked from making this
   // permission request, e.g. it may be on the Safe Browsing API blacklist.
-  PermissionDecisionAutoBlocker::GetForProfile(profile_)->UpdateEmbargoedStatus(
-      content_settings_type_, requesting_origin, web_contents,
-      base::Bind(&PermissionContextBase::ContinueRequestPermission,
-                 weak_factory_.GetWeakPtr(), web_contents, id,
-                 requesting_origin, embedding_origin, user_gesture, callback));
+  PermissionDecisionAutoBlocker::GetForProfile(profile_)
+      ->CheckSafeBrowsingBlacklist(
+          web_contents, requesting_origin, content_settings_type_,
+          base::Bind(&PermissionContextBase::ContinueRequestPermission,
+                     weak_factory_.GetWeakPtr(), web_contents, id,
+                     requesting_origin, embedding_origin, user_gesture,
+                     callback));
 }
 
 void PermissionContextBase::ContinueRequestPermission(
@@ -182,7 +184,7 @@
   if (content_setting == CONTENT_SETTING_ASK) {
     PermissionResult result =
         PermissionDecisionAutoBlocker::GetForProfile(profile_)
-            ->GetEmbargoResult(content_settings_type_, requesting_origin);
+            ->GetEmbargoResult(requesting_origin, content_settings_type_);
     DCHECK(result.content_setting == CONTENT_SETTING_ASK ||
            result.content_setting == CONTENT_SETTING_BLOCK);
     return result;
diff --git a/chrome/browser/permissions/permission_context_base_unittest.cc b/chrome/browser/permissions/permission_context_base_unittest.cc
index a5c0a6c..8479c4f 100644
--- a/chrome/browser/permissions/permission_context_base_unittest.cc
+++ b/chrome/browser/permissions/permission_context_base_unittest.cc
@@ -185,9 +185,7 @@
     tab_context_updated_ = true;
   }
 
-  bool IsRestrictedToSecureOrigins() const override {
-    return false;
-  }
+  bool IsRestrictedToSecureOrigins() const override { return false; }
 
  private:
   std::vector<ContentSetting> decisions_;
@@ -238,11 +236,11 @@
            response == CONTENT_SETTING_BLOCK ||
            response == CONTENT_SETTING_ASK);
 #if defined(OS_ANDROID)
-    PermissionAction decision = DISMISSED;
+    PermissionAction decision = PermissionAction::DISMISSED;
     if (response == CONTENT_SETTING_ALLOW)
-      decision = GRANTED;
+      decision = PermissionAction::GRANTED;
     else if (response == CONTENT_SETTING_BLOCK)
-      decision = DENIED;
+      decision = PermissionAction::DENIED;
     context->GetInfoBarController()->OnPermissionSet(
         id, url, url, false /* user_gesture */, persist, decision);
 #else
@@ -275,15 +273,13 @@
 
     const PermissionRequestID id(
         web_contents()->GetRenderProcessHost()->GetID(),
-        web_contents()->GetMainFrame()->GetRoutingID(),
-        -1);
+        web_contents()->GetMainFrame()->GetRoutingID(), -1);
     permission_context.SetRespondPermissionCallback(
         base::Bind(&PermissionContextBaseTests::RespondToPermission,
                    base::Unretained(this), &permission_context, id, url,
                    persist, decision));
     permission_context.RequestPermission(
-        web_contents(),
-        id, url, true /* user_gesture */,
+        web_contents(), id, url, true /* user_gesture */,
         base::Bind(&TestPermissionContext::TrackPermissionDecision,
                    base::Unretained(&permission_context)));
     ASSERT_EQ(1u, permission_context.decisions().size());
@@ -319,9 +315,10 @@
 
     histograms.ExpectUniqueSample(
         "Permissions.AutoBlocker.EmbargoPromptSuppression",
-        PermissionEmbargoStatus::NOT_EMBARGOED, 1);
-    histograms.ExpectUniqueSample("Permissions.AutoBlocker.EmbargoStatus",
-                                  PermissionEmbargoStatus::NOT_EMBARGOED, 1);
+        static_cast<int>(PermissionEmbargoStatus::NOT_EMBARGOED), 1);
+    histograms.ExpectUniqueSample(
+        "Permissions.AutoBlocker.EmbargoStatus",
+        static_cast<int>(PermissionEmbargoStatus::NOT_EMBARGOED), 1);
   }
 
   void DismissMultipleTimesAndExpectBlock(
@@ -347,7 +344,7 @@
       permission_context.RequestPermission(
           web_contents(), id, url, true /* user_gesture */,
           base::Bind(&TestPermissionContext::TrackPermissionDecision,
-                    base::Unretained(&permission_context)));
+                     base::Unretained(&permission_context)));
       histograms.ExpectTotalCount(
           "Permissions.Prompt.Dismissed.PriorDismissCount." +
               PermissionUtil::GetPermissionString(content_settings_type),
@@ -373,14 +370,14 @@
 
       histograms.ExpectUniqueSample(
           "Permissions.AutoBlocker.EmbargoPromptSuppression",
-          PermissionEmbargoStatus::NOT_EMBARGOED, i + 1);
+          static_cast<int>(PermissionEmbargoStatus::NOT_EMBARGOED), i + 1);
       if (i < 2) {
         EXPECT_EQ(PermissionStatusSource::UNSPECIFIED, result.source);
         EXPECT_EQ(CONTENT_SETTING_ASK, result.content_setting);
 #if !defined(OS_ANDROID)
-        histograms.ExpectUniqueSample("Permissions.AutoBlocker.EmbargoStatus",
-                                      PermissionEmbargoStatus::NOT_EMBARGOED,
-                                      i + 1);
+        histograms.ExpectUniqueSample(
+            "Permissions.AutoBlocker.EmbargoStatus",
+            static_cast<int>(PermissionEmbargoStatus::NOT_EMBARGOED), i + 1);
 #endif
       } else {
         EXPECT_EQ(PermissionStatusSource::MULTIPLE_DISMISSALS, result.source);
@@ -388,7 +385,7 @@
 #if !defined(OS_ANDROID)
         histograms.ExpectBucketCount(
             "Permissions.AutoBlocker.EmbargoStatus",
-            PermissionEmbargoStatus::REPEATED_DISMISSALS, 1);
+            static_cast<int>(PermissionEmbargoStatus::REPEATED_DISMISSALS), 1);
 #endif
       }
 
@@ -417,7 +414,7 @@
     EXPECT_EQ(PermissionStatusSource::MULTIPLE_DISMISSALS, result.source);
     histograms.ExpectBucketCount(
         "Permissions.AutoBlocker.EmbargoPromptSuppression",
-        PermissionEmbargoStatus::REPEATED_DISMISSALS, 1);
+        static_cast<int>(PermissionEmbargoStatus::REPEATED_DISMISSALS), 1);
   }
 
   void TestBlockOnSeveralDismissals_TestContent() {
@@ -441,15 +438,14 @@
       permission_context.RequestPermission(
           web_contents(), id, url, true /* user_gesture */,
           base::Bind(&TestPermissionContext::TrackPermissionDecision,
-                    base::Unretained(&permission_context)));
+                     base::Unretained(&permission_context)));
       histograms.ExpectTotalCount(
-          "Permissions.Prompt.Dismissed.PriorDismissCount.Geolocation",
-          i + 1);
+          "Permissions.Prompt.Dismissed.PriorDismissCount.Geolocation", i + 1);
       histograms.ExpectBucketCount(
           "Permissions.Prompt.Dismissed.PriorDismissCount.Geolocation", i, 1);
       histograms.ExpectUniqueSample(
           "Permissions.AutoBlocker.EmbargoPromptSuppression",
-          PermissionEmbargoStatus::NOT_EMBARGOED, i + 1);
+          static_cast<int>(PermissionEmbargoStatus::NOT_EMBARGOED), i + 1);
 
 // On Android, repeatedly requesting and deciding permissions has the side
 // effect of overcounting any metrics recorded in the PermissionInfoBarDelegate
@@ -458,9 +454,9 @@
 // bit in PermissionInfoBarDelegate. When PermissionQueueController is deleted
 // all OS_ANDROID ifdefs in this test can be removed.
 #if !defined(OS_ANDROID)
-      histograms.ExpectUniqueSample("Permissions.AutoBlocker.EmbargoStatus",
-                                    PermissionEmbargoStatus::NOT_EMBARGOED,
-                                    i + 1);
+      histograms.ExpectUniqueSample(
+          "Permissions.AutoBlocker.EmbargoStatus",
+          static_cast<int>(PermissionEmbargoStatus::NOT_EMBARGOED), i + 1);
 #endif
 
       ASSERT_EQ(1u, permission_context.decisions().size());
@@ -483,10 +479,10 @@
         base::FeatureList::IsEnabled(features::kBlockPromptsIfDismissedOften));
 
     // Sanity check independence per permission type by checking two of them.
-    DismissMultipleTimesAndExpectBlock(url,
-                                       CONTENT_SETTINGS_TYPE_GEOLOCATION, 3);
-    DismissMultipleTimesAndExpectBlock(url,
-                                       CONTENT_SETTINGS_TYPE_NOTIFICATIONS, 3);
+    DismissMultipleTimesAndExpectBlock(url, CONTENT_SETTINGS_TYPE_GEOLOCATION,
+                                       3);
+    DismissMultipleTimesAndExpectBlock(url, CONTENT_SETTINGS_TYPE_NOTIFICATIONS,
+                                       3);
   }
 
   void TestVariationBlockOnSeveralDismissals_TestContent() {
@@ -500,8 +496,8 @@
         kPromptTrialName, kPromptGroupName);
     std::map<std::string, std::string> params;
     params[PermissionDecisionAutoBlocker::kPromptDismissCountKey] = "5";
-    ASSERT_TRUE(variations::AssociateVariationParams(
-        kPromptTrialName, kPromptGroupName, params));
+    ASSERT_TRUE(variations::AssociateVariationParams(kPromptTrialName,
+                                                     kPromptGroupName, params));
 
     std::unique_ptr<base::FeatureList> feature_list =
         base::MakeUnique<base::FeatureList>();
@@ -551,7 +547,7 @@
           "Permissions.Prompt.Dismissed.PriorDismissCount.MidiSysEx", i, 1);
       histograms.ExpectUniqueSample(
           "Permissions.AutoBlocker.EmbargoPromptSuppression",
-          PermissionEmbargoStatus::NOT_EMBARGOED, i + 1);
+          static_cast<int>(PermissionEmbargoStatus::NOT_EMBARGOED), i + 1);
 
 // On Android, repeatedly requesting and deciding permissions has the side
 // effect of overcounting any metrics recorded in the PermissionInfoBarDelegate
@@ -567,9 +563,9 @@
         EXPECT_EQ(CONTENT_SETTING_ASK, result.content_setting);
         EXPECT_EQ(PermissionStatusSource::UNSPECIFIED, result.source);
 #if !defined(OS_ANDROID)
-        histograms.ExpectUniqueSample("Permissions.AutoBlocker.EmbargoStatus",
-                                      PermissionEmbargoStatus::NOT_EMBARGOED,
-                                      i + 1);
+        histograms.ExpectUniqueSample(
+            "Permissions.AutoBlocker.EmbargoStatus",
+            static_cast<int>(PermissionEmbargoStatus::NOT_EMBARGOED), i + 1);
 #endif
       } else {
         EXPECT_EQ(CONTENT_SETTING_BLOCK, result.content_setting);
@@ -577,7 +573,7 @@
 #if !defined(OS_ANDROID)
         histograms.ExpectBucketCount(
             "Permissions.AutoBlocker.EmbargoStatus",
-            PermissionEmbargoStatus::REPEATED_DISMISSALS, 1);
+            static_cast<int>(PermissionEmbargoStatus::REPEATED_DISMISSALS), 1);
 #endif
       }
     }
@@ -601,11 +597,9 @@
 
     const PermissionRequestID id(
         web_contents()->GetRenderProcessHost()->GetID(),
-        web_contents()->GetMainFrame()->GetRoutingID(),
-        -1);
+        web_contents()->GetMainFrame()->GetRoutingID(), -1);
     permission_context.RequestPermission(
-        web_contents(),
-        id, url, true /* user_gesture */,
+        web_contents(), id, url, true /* user_gesture */,
         base::Bind(&TestPermissionContext::TrackPermissionDecision,
                    base::Unretained(&permission_context)));
 
@@ -626,16 +620,14 @@
 
     const PermissionRequestID id(
         web_contents()->GetRenderProcessHost()->GetID(),
-        web_contents()->GetMainFrame()->GetRoutingID(),
-        -1);
+        web_contents()->GetMainFrame()->GetRoutingID(), -1);
     permission_context.SetRespondPermissionCallback(
         base::Bind(&PermissionContextBaseTests::RespondToPermission,
                    base::Unretained(this), &permission_context, id, url, true,
                    CONTENT_SETTING_ALLOW));
 
     permission_context.RequestPermission(
-        web_contents(),
-        id, url, true /* user_gesture */,
+        web_contents(), id, url, true /* user_gesture */,
         base::Bind(&TestPermissionContext::TrackPermissionDecision,
                    base::Unretained(&permission_context)));
 
@@ -665,9 +657,9 @@
     std::map<std::string, std::string> params;
     params[PermissionUtil::GetPermissionString(content_settings_type)] =
         kPermissionsKillSwitchBlockedValue;
-    variations::AssociateVariationParams(
-        kPermissionsKillSwitchFieldStudy, kPermissionsKillSwitchTestGroup,
-        params);
+    variations::AssociateVariationParams(kPermissionsKillSwitchFieldStudy,
+                                         kPermissionsKillSwitchTestGroup,
+                                         params);
     base::FieldTrialList::CreateFieldTrial(kPermissionsKillSwitchFieldStudy,
                                            kPermissionsKillSwitchTestGroup);
     EXPECT_TRUE(permission_context.IsPermissionKillSwitchOn());
@@ -761,9 +753,9 @@
     }
     histograms.ExpectUniqueSample(
         "Permissions.AutoBlocker.EmbargoPromptSuppression",
-        expected_embargo_reason, 1);
+        static_cast<int>(expected_embargo_reason), 1);
     histograms.ExpectUniqueSample("Permissions.AutoBlocker.EmbargoStatus",
-                                  expected_embargo_reason, 1);
+                                  static_cast<int>(expected_embargo_reason), 1);
   }
 
  private:
@@ -903,9 +895,8 @@
   std::set<std::string> blacklisted_permissions{"GEOLOCATION"};
   db_manager->BlacklistUrlPermissions(url, blacklisted_permissions);
   TestPermissionsBlacklisting(
-      CONTENT_SETTINGS_TYPE_GEOLOCATION,
-      db_manager, url, 2000 /* timeout */, CONTENT_SETTING_BLOCK,
-      PermissionEmbargoStatus::PERMISSIONS_BLACKLISTING);
+      CONTENT_SETTINGS_TYPE_GEOLOCATION, db_manager, url, 2000 /* timeout */,
+      CONTENT_SETTING_BLOCK, PermissionEmbargoStatus::PERMISSIONS_BLACKLISTING);
 }
 
 // Tests that a URL that is blacklisted for one permission can still request
diff --git a/chrome/browser/permissions/permission_decision_auto_blocker.cc b/chrome/browser/permissions/permission_decision_auto_blocker.cc
index 52df410..f6edf74c 100644
--- a/chrome/browser/permissions/permission_decision_auto_blocker.cc
+++ b/chrome/browser/permissions/permission_decision_auto_blocker.cc
@@ -168,73 +168,6 @@
   return PermissionDecisionAutoBlocker::Factory::GetForProfile(profile);
 }
 
-PermissionDecisionAutoBlocker::PermissionDecisionAutoBlocker(Profile* profile)
-    : profile_(profile),
-      db_manager_(nullptr),
-      safe_browsing_timeout_(kCheckUrlTimeoutMs),
-      clock_(new base::DefaultClock()) {
-  safe_browsing::SafeBrowsingService* sb_service =
-      g_browser_process->safe_browsing_service();
-  if (sb_service)
-    db_manager_ = sb_service->database_manager();
-}
-
-PermissionDecisionAutoBlocker::~PermissionDecisionAutoBlocker() {}
-
-void PermissionDecisionAutoBlocker::RemoveCountsByUrl(
-    base::Callback<bool(const GURL& url)> filter) {
-  HostContentSettingsMap* map =
-      HostContentSettingsMapFactory::GetForProfile(profile_);
-
-  std::unique_ptr<ContentSettingsForOneType> settings(
-      new ContentSettingsForOneType);
-  map->GetSettingsForOneType(CONTENT_SETTINGS_TYPE_PERMISSION_AUTOBLOCKER_DATA,
-                             std::string(), settings.get());
-
-  for (const auto& site : *settings) {
-    GURL origin(site.primary_pattern.ToString());
-
-    if (origin.is_valid() && filter.Run(origin)) {
-      map->SetWebsiteSettingDefaultScope(
-          origin, GURL(), CONTENT_SETTINGS_TYPE_PERMISSION_AUTOBLOCKER_DATA,
-          std::string(), nullptr);
-    }
-  }
-}
-
-int PermissionDecisionAutoBlocker::GetDismissCount(
-    const GURL& url,
-    ContentSettingsType permission) {
-  return GetActionCount(url, permission, kPromptDismissCountKey, profile_);
-}
-
-int PermissionDecisionAutoBlocker::GetIgnoreCount(
-    const GURL& url,
-    ContentSettingsType permission) {
-  return GetActionCount(url, permission, kPromptIgnoreCountKey, profile_);
-}
-
-bool PermissionDecisionAutoBlocker::RecordDismissAndEmbargo(
-    const GURL& url,
-    ContentSettingsType permission) {
-  int current_dismissal_count = RecordActionInWebsiteSettings(
-      url, permission, kPromptDismissCountKey, profile_);
-
-  if (base::FeatureList::IsEnabled(features::kBlockPromptsIfDismissedOften) &&
-      current_dismissal_count >= g_prompt_dismissals_before_block) {
-    PlaceUnderEmbargo(permission, url, kPermissionDismissalEmbargoKey);
-    return true;
-  }
-  return false;
-}
-
-int PermissionDecisionAutoBlocker::RecordIgnore(
-    const GURL& url,
-    ContentSettingsType permission) {
-  return RecordActionInWebsiteSettings(url, permission, kPromptIgnoreCountKey,
-                                       profile_);
-}
-
 // static
 void PermissionDecisionAutoBlocker::UpdateFromVariations() {
   int prompt_dismissals = -1;
@@ -264,13 +197,13 @@
   }
 }
 
-void PermissionDecisionAutoBlocker::UpdateEmbargoedStatus(
-    ContentSettingsType permission,
-    const GURL& request_origin,
+void PermissionDecisionAutoBlocker::CheckSafeBrowsingBlacklist(
     content::WebContents* web_contents,
+    const GURL& request_origin,
+    ContentSettingsType permission,
     base::Callback<void(bool)> callback) {
   DCHECK_EQ(CONTENT_SETTING_ASK,
-            GetEmbargoResult(permission, request_origin).content_setting);
+            GetEmbargoResult(request_origin, permission).content_setting);
 
   if (base::FeatureList::IsEnabled(features::kPermissionsBlacklist) &&
       db_manager_) {
@@ -278,10 +211,10 @@
     // destroyed before a result is received. In that case this object will have
     // been destroyed by that point.
     PermissionBlacklistClient::CheckSafeBrowsingBlacklist(
-        db_manager_, permission, request_origin, web_contents,
+        web_contents, db_manager_, request_origin, permission,
         safe_browsing_timeout_,
         base::Bind(&PermissionDecisionAutoBlocker::CheckSafeBrowsingResult,
-                   base::Unretained(this), permission, request_origin,
+                   base::Unretained(this), request_origin, permission,
                    callback));
     return;
   }
@@ -290,8 +223,8 @@
 }
 
 PermissionResult PermissionDecisionAutoBlocker::GetEmbargoResult(
-    ContentSettingsType permission,
-    const GURL& request_origin) {
+    const GURL& request_origin,
+    ContentSettingsType permission) {
   HostContentSettingsMap* map =
       HostContentSettingsMapFactory::GetForProfile(profile_);
   std::unique_ptr<base::DictionaryValue> dict =
@@ -327,23 +260,90 @@
                           PermissionStatusSource::UNSPECIFIED);
 }
 
+int PermissionDecisionAutoBlocker::GetDismissCount(
+    const GURL& url,
+    ContentSettingsType permission) {
+  return GetActionCount(url, permission, kPromptDismissCountKey, profile_);
+}
+
+int PermissionDecisionAutoBlocker::GetIgnoreCount(
+    const GURL& url,
+    ContentSettingsType permission) {
+  return GetActionCount(url, permission, kPromptIgnoreCountKey, profile_);
+}
+
+bool PermissionDecisionAutoBlocker::RecordDismissAndEmbargo(
+    const GURL& url,
+    ContentSettingsType permission) {
+  int current_dismissal_count = RecordActionInWebsiteSettings(
+      url, permission, kPromptDismissCountKey, profile_);
+
+  if (base::FeatureList::IsEnabled(features::kBlockPromptsIfDismissedOften) &&
+      current_dismissal_count >= g_prompt_dismissals_before_block) {
+    PlaceUnderEmbargo(url, permission, kPermissionDismissalEmbargoKey);
+    return true;
+  }
+  return false;
+}
+
+int PermissionDecisionAutoBlocker::RecordIgnore(
+    const GURL& url,
+    ContentSettingsType permission) {
+  return RecordActionInWebsiteSettings(url, permission, kPromptIgnoreCountKey,
+                                       profile_);
+}
+
+void PermissionDecisionAutoBlocker::RemoveCountsByUrl(
+    base::Callback<bool(const GURL& url)> filter) {
+  HostContentSettingsMap* map =
+      HostContentSettingsMapFactory::GetForProfile(profile_);
+
+  std::unique_ptr<ContentSettingsForOneType> settings(
+      new ContentSettingsForOneType);
+  map->GetSettingsForOneType(CONTENT_SETTINGS_TYPE_PERMISSION_AUTOBLOCKER_DATA,
+                             std::string(), settings.get());
+
+  for (const auto& site : *settings) {
+    GURL origin(site.primary_pattern.ToString());
+
+    if (origin.is_valid() && filter.Run(origin)) {
+      map->SetWebsiteSettingDefaultScope(
+          origin, GURL(), CONTENT_SETTINGS_TYPE_PERMISSION_AUTOBLOCKER_DATA,
+          std::string(), nullptr);
+    }
+  }
+}
+
+PermissionDecisionAutoBlocker::PermissionDecisionAutoBlocker(Profile* profile)
+    : profile_(profile),
+      db_manager_(nullptr),
+      safe_browsing_timeout_(kCheckUrlTimeoutMs),
+      clock_(new base::DefaultClock()) {
+  safe_browsing::SafeBrowsingService* sb_service =
+      g_browser_process->safe_browsing_service();
+  if (sb_service)
+    db_manager_ = sb_service->database_manager();
+}
+
+PermissionDecisionAutoBlocker::~PermissionDecisionAutoBlocker() {}
+
 void PermissionDecisionAutoBlocker::CheckSafeBrowsingResult(
-    ContentSettingsType permission,
     const GURL& request_origin,
+    ContentSettingsType permission,
     base::Callback<void(bool)> callback,
     bool should_be_embargoed) {
   if (should_be_embargoed) {
     // Requesting site is blacklisted for this permission, update the content
     // setting to place it under embargo.
-    PlaceUnderEmbargo(permission, request_origin,
+    PlaceUnderEmbargo(request_origin, permission,
                       kPermissionBlacklistEmbargoKey);
   }
   callback.Run(should_be_embargoed /* permission blocked */);
 }
 
 void PermissionDecisionAutoBlocker::PlaceUnderEmbargo(
-    ContentSettingsType permission,
     const GURL& request_origin,
+    ContentSettingsType permission,
     const char* key) {
   HostContentSettingsMap* map =
       HostContentSettingsMapFactory::GetForProfile(profile_);
diff --git a/chrome/browser/permissions/permission_decision_auto_blocker.h b/chrome/browser/permissions/permission_decision_auto_blocker.h
index d35e6d7..47eba8f 100644
--- a/chrome/browser/permissions/permission_decision_auto_blocker.h
+++ b/chrome/browser/permissions/permission_decision_auto_blocker.h
@@ -58,8 +58,22 @@
 
   static PermissionDecisionAutoBlocker* GetForProfile(Profile* profile);
 
-  // Removes any recorded counts for urls which match |filter|.
-  void RemoveCountsByUrl(base::Callback<bool(const GURL& url)> filter);
+  // Updates the threshold to start blocking prompts from the field trial.
+  static void UpdateFromVariations();
+
+  // Makes an asynchronous call to Safe Browsing to check the API blacklist.
+  // Places the (|request_origin|, |permission|) pair under embargo if they are
+  // on the blacklist.
+  void CheckSafeBrowsingBlacklist(content::WebContents* web_contents,
+                                  const GURL& request_origin,
+                                  ContentSettingsType permission,
+                                  base::Callback<void(bool)> callback);
+
+  // Checks the status of the content setting to determine if |request_origin|
+  // is under embargo for |permission|. This checks both embargo for Permissions
+  // Blacklisting and repeated dismissals.
+  PermissionResult GetEmbargoResult(const GURL& request_origin,
+                                    ContentSettingsType permission);
 
   // Returns the current number of dismisses recorded for |permission| type at
   // |url|.
@@ -78,21 +92,8 @@
   // Records that an ignore of a prompt for |permission| was made.
   int RecordIgnore(const GURL& url, ContentSettingsType permission);
 
-  // Updates the threshold to start blocking prompts from the field trial.
-  static void UpdateFromVariations();
-
-  // Updates whether |request_origin| should be under embargo for |permission|.
-  // Makes an asynchronous call to Safe Browsing to check the API blacklist.
-  void UpdateEmbargoedStatus(ContentSettingsType permission,
-                             const GURL& request_origin,
-                             content::WebContents* web_contents,
-                             base::Callback<void(bool)> callback);
-
-  // Checks the status of the content setting to determine if |request_origin|
-  // is under embargo for |permission|. This checks both embargo for Permissions
-  // Blacklisting and repeated dismissals.
-  PermissionResult GetEmbargoResult(ContentSettingsType permission,
-                                    const GURL& request_origin);
+  // Removes any recorded counts for urls which match |filter|.
+  void RemoveCountsByUrl(base::Callback<bool(const GURL& url)> filter);
 
  private:
   friend class PermissionContextBaseTests;
@@ -103,13 +104,13 @@
 
   // Get the result of the Safe Browsing check, if |should_be_embargoed| is true
   // then |request_origin| will be placed under embargo for that |permission|.
-  void CheckSafeBrowsingResult(ContentSettingsType permission,
-                               const GURL& request_origin,
+  void CheckSafeBrowsingResult(const GURL& request_origin,
+                               ContentSettingsType permission,
                                base::Callback<void(bool)> callback,
                                bool should_be_embargoed);
 
-  void PlaceUnderEmbargo(ContentSettingsType permission,
-                         const GURL& request_origin,
+  void PlaceUnderEmbargo(const GURL& request_origin,
+                         ContentSettingsType permission,
                          const char* key);
 
   void SetSafeBrowsingDatabaseManagerAndTimeoutForTesting(
diff --git a/chrome/browser/permissions/permission_decision_auto_blocker_unittest.cc b/chrome/browser/permissions/permission_decision_auto_blocker_unittest.cc
index 739d12e..e62eebb 100644
--- a/chrome/browser/permissions/permission_decision_auto_blocker_unittest.cc
+++ b/chrome/browser/permissions/permission_decision_auto_blocker_unittest.cc
@@ -106,10 +106,11 @@
                                                                      timeout);
   }
 
-  void UpdateEmbargoedStatus(ContentSettingsType permission, const GURL& url) {
+  void CheckSafeBrowsingBlacklist(const GURL& url,
+                                  ContentSettingsType permission) {
     base::RunLoop run_loop;
-    autoblocker_->UpdateEmbargoedStatus(
-        permission, url, nullptr,
+    autoblocker_->CheckSafeBrowsingBlacklist(
+        nullptr, url, permission,
         base::Bind(&PermissionDecisionAutoBlockerUnitTest::SetLastEmbargoStatus,
                    base::Unretained(this), run_loop.QuitClosure()));
     run_loop.Run();
@@ -118,10 +119,10 @@
   // Manually placing an (origin, permission) pair under embargo for
   // blacklisting. To embargo on dismissals, RecordDismissAndEmbargo can be
   // used.
-  void PlaceUnderBlacklistEmbargo(ContentSettingsType permission,
-                                  const GURL& url) {
+  void PlaceUnderBlacklistEmbargo(const GURL& url,
+                                  ContentSettingsType permission) {
     autoblocker_->PlaceUnderEmbargo(
-        permission, url,
+        url, permission,
         PermissionDecisionAutoBlocker::kPermissionBlacklistEmbargoKey);
   }
 
@@ -275,11 +276,12 @@
   SetSafeBrowsingDatabaseManagerAndTimeoutForTesting(db_manager,
                                                      2000 /* timeout in ms */);
 
-  UpdateEmbargoedStatus(CONTENT_SETTINGS_TYPE_GEOLOCATION, url);
+  CheckSafeBrowsingBlacklist(url, CONTENT_SETTINGS_TYPE_GEOLOCATION);
   EXPECT_TRUE(callback_was_run());
   EXPECT_TRUE(last_embargoed_status());
-  histograms.ExpectUniqueSample("Permissions.AutoBlocker.SafeBrowsingResponse",
-                                SafeBrowsingResponse::BLACKLISTED, 1);
+  histograms.ExpectUniqueSample(
+      "Permissions.AutoBlocker.SafeBrowsingResponse",
+      static_cast<int>(SafeBrowsingResponse::BLACKLISTED), 1);
   histograms.ExpectTotalCount(
       "Permissions.AutoBlocker.SafeBrowsingResponseTime", 1);
 }
@@ -299,10 +301,11 @@
   SetSafeBrowsingDatabaseManagerAndTimeoutForTesting(db_manager,
                                                      0 /* timeout in ms */);
 
-  UpdateEmbargoedStatus(CONTENT_SETTINGS_TYPE_NOTIFICATIONS, url);
+  CheckSafeBrowsingBlacklist(url, CONTENT_SETTINGS_TYPE_NOTIFICATIONS);
   EXPECT_FALSE(last_embargoed_status());
-  histograms.ExpectUniqueSample("Permissions.AutoBlocker.SafeBrowsingResponse",
-                                SafeBrowsingResponse::NOT_BLACKLISTED, 1);
+  histograms.ExpectUniqueSample(
+      "Permissions.AutoBlocker.SafeBrowsingResponse",
+      static_cast<int>(SafeBrowsingResponse::NOT_BLACKLISTED), 1);
   histograms.ExpectTotalCount(
       "Permissions.AutoBlocker.SafeBrowsingResponseTime", 1);
 }
@@ -315,27 +318,27 @@
 
   // Check the default state.
   PermissionResult result =
-      autoblocker()->GetEmbargoResult(CONTENT_SETTINGS_TYPE_GEOLOCATION, url);
+      autoblocker()->GetEmbargoResult(url, CONTENT_SETTINGS_TYPE_GEOLOCATION);
   EXPECT_EQ(CONTENT_SETTING_ASK, result.content_setting);
   EXPECT_EQ(PermissionStatusSource::UNSPECIFIED, result.source);
 
   // Place under embargo and verify.
-  PlaceUnderBlacklistEmbargo(CONTENT_SETTINGS_TYPE_GEOLOCATION, url);
+  PlaceUnderBlacklistEmbargo(url, CONTENT_SETTINGS_TYPE_GEOLOCATION);
   result =
-      autoblocker()->GetEmbargoResult(CONTENT_SETTINGS_TYPE_GEOLOCATION, url);
+      autoblocker()->GetEmbargoResult(url, CONTENT_SETTINGS_TYPE_GEOLOCATION);
   EXPECT_EQ(CONTENT_SETTING_BLOCK, result.content_setting);
   EXPECT_EQ(PermissionStatusSource::SAFE_BROWSING_BLACKLIST, result.source);
 
   // Check that the origin is not under embargo for a different permission.
   result =
-      autoblocker()->GetEmbargoResult(CONTENT_SETTINGS_TYPE_NOTIFICATIONS, url);
+      autoblocker()->GetEmbargoResult(url, CONTENT_SETTINGS_TYPE_NOTIFICATIONS);
   EXPECT_EQ(CONTENT_SETTING_ASK, result.content_setting);
   EXPECT_EQ(PermissionStatusSource::UNSPECIFIED, result.source);
 
   // Confirm embargo status during the embargo period.
   clock()->Advance(base::TimeDelta::FromDays(5));
   result =
-      autoblocker()->GetEmbargoResult(CONTENT_SETTINGS_TYPE_GEOLOCATION, url);
+      autoblocker()->GetEmbargoResult(url, CONTENT_SETTINGS_TYPE_GEOLOCATION);
   EXPECT_EQ(CONTENT_SETTING_BLOCK, result.content_setting);
   EXPECT_EQ(PermissionStatusSource::SAFE_BROWSING_BLACKLIST, result.source);
 
@@ -344,22 +347,22 @@
   // when removing the date stored as a double from the permission dictionary.
   clock()->Advance(base::TimeDelta::FromHours(3 * 24 + 1));
   result =
-      autoblocker()->GetEmbargoResult(CONTENT_SETTINGS_TYPE_GEOLOCATION, url);
+      autoblocker()->GetEmbargoResult(url, CONTENT_SETTINGS_TYPE_GEOLOCATION);
   EXPECT_EQ(CONTENT_SETTING_ASK, result.content_setting);
   EXPECT_EQ(PermissionStatusSource::UNSPECIFIED, result.source);
 
   // Check embargo is lifted well after the expiry day.
   clock()->Advance(base::TimeDelta::FromDays(1));
   result =
-      autoblocker()->GetEmbargoResult(CONTENT_SETTINGS_TYPE_GEOLOCATION, url);
+      autoblocker()->GetEmbargoResult(url, CONTENT_SETTINGS_TYPE_GEOLOCATION);
   EXPECT_EQ(CONTENT_SETTING_ASK, result.content_setting);
   EXPECT_EQ(PermissionStatusSource::UNSPECIFIED, result.source);
 
   // Place under embargo again and verify the embargo status.
-  PlaceUnderBlacklistEmbargo(CONTENT_SETTINGS_TYPE_NOTIFICATIONS, url);
+  PlaceUnderBlacklistEmbargo(url, CONTENT_SETTINGS_TYPE_NOTIFICATIONS);
   clock()->Advance(base::TimeDelta::FromDays(1));
   result =
-      autoblocker()->GetEmbargoResult(CONTENT_SETTINGS_TYPE_NOTIFICATIONS, url);
+      autoblocker()->GetEmbargoResult(url, CONTENT_SETTINGS_TYPE_NOTIFICATIONS);
   EXPECT_EQ(CONTENT_SETTING_BLOCK, result.content_setting);
   EXPECT_EQ(PermissionStatusSource::SAFE_BROWSING_BLACKLIST, result.source);
 }
@@ -382,7 +385,7 @@
 
   // A request with < 3 prior dismisses should not be automatically blocked.
   PermissionResult result =
-      autoblocker()->GetEmbargoResult(CONTENT_SETTINGS_TYPE_GEOLOCATION, url);
+      autoblocker()->GetEmbargoResult(url, CONTENT_SETTINGS_TYPE_GEOLOCATION);
   EXPECT_EQ(CONTENT_SETTING_ASK, result.content_setting);
   EXPECT_EQ(PermissionStatusSource::UNSPECIFIED, result.source);
 
@@ -390,7 +393,7 @@
   EXPECT_TRUE(autoblocker()->RecordDismissAndEmbargo(
       url, CONTENT_SETTINGS_TYPE_GEOLOCATION));
   result =
-      autoblocker()->GetEmbargoResult(CONTENT_SETTINGS_TYPE_GEOLOCATION, url);
+      autoblocker()->GetEmbargoResult(url, CONTENT_SETTINGS_TYPE_GEOLOCATION);
   EXPECT_EQ(CONTENT_SETTING_BLOCK, result.content_setting);
   EXPECT_EQ(PermissionStatusSource::MULTIPLE_DISMISSALS, result.source);
 
@@ -402,7 +405,7 @@
   // request won't be automatically blocked.
   clock()->Advance(base::TimeDelta::FromDays(8));
   result =
-      autoblocker()->GetEmbargoResult(CONTENT_SETTINGS_TYPE_GEOLOCATION, url);
+      autoblocker()->GetEmbargoResult(url, CONTENT_SETTINGS_TYPE_GEOLOCATION);
   EXPECT_EQ(CONTENT_SETTING_ASK, result.content_setting);
   EXPECT_EQ(PermissionStatusSource::UNSPECIFIED, result.source);
 
@@ -410,7 +413,7 @@
   EXPECT_TRUE(autoblocker()->RecordDismissAndEmbargo(
       url, CONTENT_SETTINGS_TYPE_GEOLOCATION));
   result =
-      autoblocker()->GetEmbargoResult(CONTENT_SETTINGS_TYPE_GEOLOCATION, url);
+      autoblocker()->GetEmbargoResult(url, CONTENT_SETTINGS_TYPE_GEOLOCATION);
   EXPECT_EQ(CONTENT_SETTING_BLOCK, result.content_setting);
   EXPECT_EQ(PermissionStatusSource::MULTIPLE_DISMISSALS, result.source);
 
@@ -418,7 +421,7 @@
   // request is let through.
   clock()->Advance(base::TimeDelta::FromDays(8));
   result =
-      autoblocker()->GetEmbargoResult(CONTENT_SETTINGS_TYPE_GEOLOCATION, url);
+      autoblocker()->GetEmbargoResult(url, CONTENT_SETTINGS_TYPE_GEOLOCATION);
   EXPECT_EQ(CONTENT_SETTING_ASK, result.content_setting);
   EXPECT_EQ(PermissionStatusSource::UNSPECIFIED, result.source);
 
@@ -426,7 +429,7 @@
   EXPECT_TRUE(autoblocker()->RecordDismissAndEmbargo(
       url, CONTENT_SETTINGS_TYPE_GEOLOCATION));
   result =
-      autoblocker()->GetEmbargoResult(CONTENT_SETTINGS_TYPE_GEOLOCATION, url);
+      autoblocker()->GetEmbargoResult(url, CONTENT_SETTINGS_TYPE_GEOLOCATION);
   EXPECT_EQ(CONTENT_SETTING_BLOCK, result.content_setting);
   EXPECT_EQ(PermissionStatusSource::MULTIPLE_DISMISSALS, result.source);
   histograms.ExpectTotalCount("Permissions.AutoBlocker.SafeBrowsingResponse",
@@ -441,10 +444,10 @@
   clock()->SetNow(base::Time::Now());
 
   // Place under blacklist embargo and check the status.
-  PlaceUnderBlacklistEmbargo(CONTENT_SETTINGS_TYPE_GEOLOCATION, url);
+  PlaceUnderBlacklistEmbargo(url, CONTENT_SETTINGS_TYPE_GEOLOCATION);
   clock()->Advance(base::TimeDelta::FromDays(5));
   PermissionResult result =
-      autoblocker()->GetEmbargoResult(CONTENT_SETTINGS_TYPE_GEOLOCATION, url);
+      autoblocker()->GetEmbargoResult(url, CONTENT_SETTINGS_TYPE_GEOLOCATION);
   EXPECT_EQ(CONTENT_SETTING_BLOCK, result.content_setting);
   EXPECT_EQ(PermissionStatusSource::SAFE_BROWSING_BLACKLIST, result.source);
 
@@ -460,7 +463,7 @@
   // and check that dismissal embargo is still set.
   clock()->Advance(base::TimeDelta::FromDays(3));
   result =
-      autoblocker()->GetEmbargoResult(CONTENT_SETTINGS_TYPE_GEOLOCATION, url);
+      autoblocker()->GetEmbargoResult(url, CONTENT_SETTINGS_TYPE_GEOLOCATION);
   EXPECT_EQ(CONTENT_SETTING_BLOCK, result.content_setting);
   EXPECT_EQ(PermissionStatusSource::MULTIPLE_DISMISSALS, result.source);
 }
@@ -478,17 +481,18 @@
   SetSafeBrowsingDatabaseManagerAndTimeoutForTesting(db_manager,
                                                      0 /* timeout in ms */);
 
-  UpdateEmbargoedStatus(CONTENT_SETTINGS_TYPE_GEOLOCATION, url);
+  CheckSafeBrowsingBlacklist(url, CONTENT_SETTINGS_TYPE_GEOLOCATION);
   EXPECT_TRUE(callback_was_run());
   EXPECT_FALSE(last_embargoed_status());
 
   PermissionResult result =
-      autoblocker()->GetEmbargoResult(CONTENT_SETTINGS_TYPE_GEOLOCATION, url);
+      autoblocker()->GetEmbargoResult(url, CONTENT_SETTINGS_TYPE_GEOLOCATION);
   EXPECT_EQ(CONTENT_SETTING_ASK, result.content_setting);
   EXPECT_EQ(PermissionStatusSource::UNSPECIFIED, result.source);
 
   histograms.ExpectUniqueSample("Permissions.AutoBlocker.SafeBrowsingResponse",
-                                SafeBrowsingResponse::TIMEOUT, 1);
+                                static_cast<int>(SafeBrowsingResponse::TIMEOUT),
+                                1);
   histograms.ExpectTotalCount(
       "Permissions.AutoBlocker.SafeBrowsingResponseTime", 1);
   db_manager->SetPerformCallback(true);
@@ -496,18 +500,19 @@
                                                      2000 /* timeout in ms */);
 
   clock()->Advance(base::TimeDelta::FromDays(1));
-  UpdateEmbargoedStatus(CONTENT_SETTINGS_TYPE_GEOLOCATION, url);
+  CheckSafeBrowsingBlacklist(url, CONTENT_SETTINGS_TYPE_GEOLOCATION);
   EXPECT_TRUE(callback_was_run());
   EXPECT_TRUE(last_embargoed_status());
   histograms.ExpectTotalCount("Permissions.AutoBlocker.SafeBrowsingResponse",
                               2);
   histograms.ExpectTotalCount(
       "Permissions.AutoBlocker.SafeBrowsingResponseTime", 2);
-  histograms.ExpectBucketCount("Permissions.AutoBlocker.SafeBrowsingResponse",
-                               SafeBrowsingResponse::BLACKLISTED, 1);
+  histograms.ExpectBucketCount(
+      "Permissions.AutoBlocker.SafeBrowsingResponse",
+      static_cast<int>(SafeBrowsingResponse::BLACKLISTED), 1);
   clock()->Advance(base::TimeDelta::FromDays(1));
   result =
-      autoblocker()->GetEmbargoResult(CONTENT_SETTINGS_TYPE_GEOLOCATION, url);
+      autoblocker()->GetEmbargoResult(url, CONTENT_SETTINGS_TYPE_GEOLOCATION);
   EXPECT_EQ(CONTENT_SETTING_BLOCK, result.content_setting);
   EXPECT_EQ(PermissionStatusSource::SAFE_BROWSING_BLACKLIST, result.source);
 }
@@ -595,7 +600,7 @@
   db_manager->BlacklistUrlPermissions(url, blacklisted_permissions);
   SetSafeBrowsingDatabaseManagerAndTimeoutForTesting(db_manager,
                                                      2000 /* timeout in ms */);
-  UpdateEmbargoedStatus(CONTENT_SETTINGS_TYPE_GEOLOCATION, url);
+  CheckSafeBrowsingBlacklist(url, CONTENT_SETTINGS_TYPE_GEOLOCATION);
   EXPECT_TRUE(callback_was_run());
   EXPECT_FALSE(last_embargoed_status());
 }
@@ -613,8 +618,9 @@
   SetSafeBrowsingDatabaseManagerAndTimeoutForTesting(db_manager,
                                                      0 /* timeout in ms */);
 
-  UpdateEmbargoedStatus(CONTENT_SETTINGS_TYPE_NOTIFICATIONS, url);
+  CheckSafeBrowsingBlacklist(url, CONTENT_SETTINGS_TYPE_NOTIFICATIONS);
   EXPECT_FALSE(last_embargoed_status());
-  histograms.ExpectUniqueSample("Permissions.AutoBlocker.SafeBrowsingResponse",
-                                SafeBrowsingResponse::NOT_BLACKLISTED, 1);
+  histograms.ExpectUniqueSample(
+      "Permissions.AutoBlocker.SafeBrowsingResponse",
+      static_cast<int>(SafeBrowsingResponse::NOT_BLACKLISTED), 1);
 }
diff --git a/chrome/browser/permissions/permission_infobar_delegate.cc b/chrome/browser/permissions/permission_infobar_delegate.cc
index 95f748a2..d217feb8 100644
--- a/chrome/browser/permissions/permission_infobar_delegate.cc
+++ b/chrome/browser/permissions/permission_infobar_delegate.cc
@@ -103,7 +103,7 @@
         content_settings_type_, persist_);
   }
 
-  SetPermission(update_content_setting, GRANTED);
+  SetPermission(update_content_setting, PermissionAction::GRANTED);
   return true;
 }
 
@@ -115,12 +115,12 @@
         content_settings_type_, persist_);
   }
 
-  SetPermission(update_content_setting, DENIED);
+  SetPermission(update_content_setting, PermissionAction::DENIED);
   return true;
 }
 
 void PermissionInfoBarDelegate::InfoBarDismissed() {
-  SetPermission(false, DISMISSED);
+  SetPermission(false, PermissionAction::DISMISSED);
 }
 
 base::string16 PermissionInfoBarDelegate::GetButtonLabel(
diff --git a/chrome/browser/permissions/permission_queue_controller.cc b/chrome/browser/permissions/permission_queue_controller.cc
index 8f095ecb..8df4af4f 100644
--- a/chrome/browser/permissions/permission_queue_controller.cc
+++ b/chrome/browser/permissions/permission_queue_controller.cc
@@ -225,19 +225,19 @@
       PermissionEmbargoStatus::NOT_EMBARGOED;
 
   switch (decision) {
-    case GRANTED:
+    case PermissionAction::GRANTED:
       PermissionUmaUtil::PermissionGranted(content_settings_type_, gesture_type,
                                            requesting_frame, profile_);
       PermissionUmaUtil::RecordPermissionPromptAccepted(request_type,
                                                         gesture_type);
       break;
-    case DENIED:
+    case PermissionAction::DENIED:
       PermissionUmaUtil::PermissionDenied(content_settings_type_, gesture_type,
                                           requesting_frame, profile_);
       PermissionUmaUtil::RecordPermissionPromptDenied(request_type,
                                                       gesture_type);
       break;
-    case DISMISSED:
+    case PermissionAction::DISMISSED:
       PermissionUmaUtil::PermissionDismissed(
           content_settings_type_, gesture_type, requesting_frame, profile_);
       if (PermissionDecisionAutoBlocker::GetForProfile(profile_)
@@ -296,12 +296,12 @@
   // for block and { false, DISMISSED } for dismissed. The tuple being
   // { update_content_setting, decision }.
   ContentSetting content_setting = CONTENT_SETTING_DEFAULT;
-  if (decision == GRANTED)
+  if (decision == PermissionAction::GRANTED)
     content_setting = CONTENT_SETTING_ALLOW;
-  else if (decision == DENIED)
+  else if (decision == PermissionAction::DENIED)
     content_setting = CONTENT_SETTING_BLOCK;
   else
-    DCHECK_EQ(DISMISSED, decision);
+    DCHECK_EQ(PermissionAction::DISMISSED, decision);
 
   // Send out the permission notifications.
   for (PendingInfobarRequests::iterator i = requests_to_notify.begin();
@@ -432,7 +432,8 @@
     const GURL& requesting_frame,
     const GURL& embedder,
     PermissionAction decision) {
-  DCHECK(decision == GRANTED || decision == DENIED);
+  DCHECK(decision == PermissionAction::GRANTED ||
+         decision == PermissionAction::DENIED);
   if (requesting_frame.GetOrigin().SchemeIsFile()) {
     // Chrome can be launched with --disable-web-security which allows
     // geolocation requests from file:// URLs. We don't want to store these
@@ -440,8 +441,9 @@
     return;
   }
 
-  ContentSetting content_setting =
-      (decision == GRANTED) ? CONTENT_SETTING_ALLOW : CONTENT_SETTING_BLOCK;
+  ContentSetting content_setting = (decision == PermissionAction::GRANTED)
+                                       ? CONTENT_SETTING_ALLOW
+                                       : CONTENT_SETTING_BLOCK;
 
   // TODO(timloh): Remove this logic when push and notification permissions
   // are reconciled, see crbug.com/563297.
diff --git a/chrome/browser/permissions/permission_uma_util.cc b/chrome/browser/permissions/permission_uma_util.cc
index b7db409..2fc1ce9 100644
--- a/chrome/browser/permissions/permission_uma_util.cc
+++ b/chrome/browser/permissions/permission_uma_util.cc
@@ -31,15 +31,16 @@
 
 // UMA keys need to be statically initialized so plain function would not
 // work. Use macros instead.
-#define PERMISSION_ACTION_UMA(secure_origin, permission, permission_secure, \
-                              permission_insecure, action)                  \
-  UMA_HISTOGRAM_ENUMERATION(permission, action, PERMISSION_ACTION_NUM);     \
-  if (secure_origin) {                                                      \
-    UMA_HISTOGRAM_ENUMERATION(permission_secure, action,                    \
-                              PERMISSION_ACTION_NUM);                       \
-  } else {                                                                  \
-    UMA_HISTOGRAM_ENUMERATION(permission_insecure, action,                  \
-                              PERMISSION_ACTION_NUM);                       \
+#define PERMISSION_ACTION_UMA(secure_origin, permission, permission_secure,  \
+                              permission_insecure, action)                   \
+  UMA_HISTOGRAM_ENUMERATION(permission, static_cast<int>(action),            \
+                            static_cast<int>(PermissionAction::NUM));        \
+  if (secure_origin) {                                                       \
+    UMA_HISTOGRAM_ENUMERATION(permission_secure, static_cast<int>(action),   \
+                              static_cast<int>(PermissionAction::NUM));      \
+  } else {                                                                   \
+    UMA_HISTOGRAM_ENUMERATION(permission_insecure, static_cast<int>(action), \
+                              static_cast<int>(PermissionAction::NUM));      \
   }
 
 #define PERMISSION_BUBBLE_TYPE_UMA(metric_name, permission_bubble_type) \
@@ -67,19 +68,19 @@
                                   PermissionAction action) {
   std::string action_str;
   switch (action) {
-    case GRANTED:
+    case PermissionAction::GRANTED:
       action_str = "Granted";
       break;
-    case DENIED:
+    case PermissionAction::DENIED:
       action_str = "Denied";
       break;
-    case DISMISSED:
+    case PermissionAction::DISMISSED:
       action_str = "Dismissed";
       break;
-    case IGNORED:
+    case PermissionAction::IGNORED:
       action_str = "Ignored";
       break;
-    case REVOKED:
+    case PermissionAction::REVOKED:
       action_str = "Revoked";
       break;
     default:
@@ -279,8 +280,9 @@
     Profile* profile) {
   PermissionDecisionAutoBlocker* autoblocker =
       PermissionDecisionAutoBlocker::GetForProfile(profile);
-  RecordPermissionAction(permission, GRANTED, PermissionSourceUI::PROMPT,
-                         gesture_type, requesting_origin, profile);
+  RecordPermissionAction(permission, PermissionAction::GRANTED,
+                         PermissionSourceUI::PROMPT, gesture_type,
+                         requesting_origin, profile);
   RecordPermissionPromptPriorCount(
       permission, kPermissionsPromptAcceptedPriorDismissCountPrefix,
       autoblocker->GetDismissCount(requesting_origin, permission));
@@ -296,8 +298,9 @@
     Profile* profile) {
   PermissionDecisionAutoBlocker* autoblocker =
       PermissionDecisionAutoBlocker::GetForProfile(profile);
-  RecordPermissionAction(permission, DENIED, PermissionSourceUI::PROMPT,
-                         gesture_type, requesting_origin, profile);
+  RecordPermissionAction(permission, PermissionAction::DENIED,
+                         PermissionSourceUI::PROMPT, gesture_type,
+                         requesting_origin, profile);
   RecordPermissionPromptPriorCount(
       permission, kPermissionsPromptDeniedPriorDismissCountPrefix,
       autoblocker->GetDismissCount(requesting_origin, permission));
@@ -313,8 +316,9 @@
     Profile* profile) {
   PermissionDecisionAutoBlocker* autoblocker =
       PermissionDecisionAutoBlocker::GetForProfile(profile);
-  RecordPermissionAction(permission, DISMISSED, PermissionSourceUI::PROMPT,
-                         gesture_type, requesting_origin, profile);
+  RecordPermissionAction(permission, PermissionAction::DISMISSED,
+                         PermissionSourceUI::PROMPT, gesture_type,
+                         requesting_origin, profile);
   RecordPermissionPromptPriorCount(
       permission, kPermissionsPromptDismissedPriorDismissCountPrefix,
       autoblocker->GetDismissCount(requesting_origin, permission));
@@ -330,8 +334,9 @@
     Profile* profile) {
   PermissionDecisionAutoBlocker* autoblocker =
       PermissionDecisionAutoBlocker::GetForProfile(profile);
-  RecordPermissionAction(permission, IGNORED, PermissionSourceUI::PROMPT,
-                         gesture_type, requesting_origin, profile);
+  RecordPermissionAction(permission, PermissionAction::IGNORED,
+                         PermissionSourceUI::PROMPT, gesture_type,
+                         requesting_origin, profile);
   RecordPermissionPromptPriorCount(
       permission, kPermissionsPromptIgnoredPriorDismissCountPrefix,
       autoblocker->GetDismissCount(requesting_origin, permission));
@@ -357,7 +362,7 @@
       permission == CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA) {
     // An unknown gesture type is passed in since gesture type is only
     // applicable in prompt UIs where revocations are not possible.
-    RecordPermissionAction(permission, REVOKED, source_ui,
+    RecordPermissionAction(permission, PermissionAction::REVOKED, source_ui,
                            PermissionRequestGestureType::UNKNOWN,
                            revoked_origin, profile);
   }
@@ -366,8 +371,8 @@
 void PermissionUmaUtil::RecordEmbargoPromptSuppression(
     PermissionEmbargoStatus embargo_status) {
   UMA_HISTOGRAM_ENUMERATION("Permissions.AutoBlocker.EmbargoPromptSuppression",
-                            embargo_status,
-                            PermissionEmbargoStatus::STATUS_NUM);
+                            static_cast<int>(embargo_status),
+                            static_cast<int>(PermissionEmbargoStatus::NUM));
 }
 
 void PermissionUmaUtil::RecordEmbargoPromptSuppressionFromSource(
@@ -394,8 +399,8 @@
 void PermissionUmaUtil::RecordEmbargoStatus(
     PermissionEmbargoStatus embargo_status) {
   UMA_HISTOGRAM_ENUMERATION("Permissions.AutoBlocker.EmbargoStatus",
-                            embargo_status,
-                            PermissionEmbargoStatus::STATUS_NUM);
+                            static_cast<int>(embargo_status),
+                            static_cast<int>(PermissionEmbargoStatus::NUM));
 }
 
 void PermissionUmaUtil::RecordSafeBrowsingResponse(
@@ -404,7 +409,8 @@
   UMA_HISTOGRAM_TIMES("Permissions.AutoBlocker.SafeBrowsingResponseTime",
                       response_time);
   UMA_HISTOGRAM_ENUMERATION("Permissions.AutoBlocker.SafeBrowsingResponse",
-                            response, SafeBrowsingResponse::RESPONSE_NUM);
+                            static_cast<int>(response),
+                            static_cast<int>(SafeBrowsingResponse::NUM));
 }
 
 void PermissionUmaUtil::PermissionPromptShown(
@@ -726,12 +732,14 @@
     case CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC:
       // Media permissions are disabled on insecure origins, so there's no
       // need to record metrics for secure/insecue.
-      UMA_HISTOGRAM_ENUMERATION("Permissions.Action.AudioCapture", action,
-                                PERMISSION_ACTION_NUM);
+      UMA_HISTOGRAM_ENUMERATION("Permissions.Action.AudioCapture",
+                                static_cast<int>(action),
+                                static_cast<int>(PermissionAction::NUM));
       break;
     case CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA:
-      UMA_HISTOGRAM_ENUMERATION("Permissions.Action.VideoCapture", action,
-                                PERMISSION_ACTION_NUM);
+      UMA_HISTOGRAM_ENUMERATION("Permissions.Action.VideoCapture",
+                                static_cast<int>(action),
+                                static_cast<int>(PermissionAction::NUM));
       break;
     case CONTENT_SETTINGS_TYPE_PLUGINS:
       PERMISSION_ACTION_UMA(secure_origin, "Permissions.Action.Flash",
diff --git a/chrome/browser/permissions/permission_uma_util.h b/chrome/browser/permissions/permission_uma_util.h
index de92418b..a8e67aa 100644
--- a/chrome/browser/permissions/permission_uma_util.h
+++ b/chrome/browser/permissions/permission_uma_util.h
@@ -27,7 +27,7 @@
   PAGE_ACTION = 3,
 
   // Always keep this at the end.
-  SOURCE_UI_NUM,
+  NUM,
 };
 
 // This should stay in sync with the PersistDecision enum in the permission
@@ -38,24 +38,24 @@
   NOT_PERSISTED = 2,
 };
 
-// Any new values should be inserted immediately prior to RESPONSE_NUM.
-enum SafeBrowsingResponse {
+// Any new values should be inserted immediately prior to NUM.
+enum class SafeBrowsingResponse {
   NOT_BLACKLISTED = 0,
   TIMEOUT = 1,
   BLACKLISTED = 2,
 
   // Always keep this at the end.
-  RESPONSE_NUM,
+  NUM,
 };
 
-// Any new values should be inserted immediately prior to STATUS_NUM.
-enum PermissionEmbargoStatus {
+// Any new values should be inserted immediately prior to NUM.
+enum class PermissionEmbargoStatus {
   NOT_EMBARGOED = 0,
   PERMISSIONS_BLACKLISTING = 1,
   REPEATED_DISMISSALS = 2,
 
   // Keep this at the end.
-  STATUS_NUM,
+  NUM,
 };
 
 // A bundle for the information sent in a PermissionReport.
diff --git a/chrome/browser/permissions/permission_util.h b/chrome/browser/permissions/permission_util.h
index 3f7c14c..d746ca9 100644
--- a/chrome/browser/permissions/permission_util.h
+++ b/chrome/browser/permissions/permission_util.h
@@ -22,7 +22,7 @@
 enum class PermissionSourceUI;
 
 // This enum backs a UMA histogram, so it must be treated as append-only.
-enum PermissionAction {
+enum class PermissionAction {
   GRANTED = 0,
   DENIED = 1,
   DISMISSED = 2,
@@ -32,7 +32,7 @@
   REQUESTED = 6,
 
   // Always keep this at the end.
-  PERMISSION_ACTION_NUM,
+  NUM,
 };
 
 // Identifies the source or reason for a permission status being returned. This
diff --git a/chrome/browser/permissions/permission_util_unittest.cc b/chrome/browser/permissions/permission_util_unittest.cc
index b3b944c4..c551e31 100644
--- a/chrome/browser/permissions/permission_util_unittest.cc
+++ b/chrome/browser/permissions/permission_util_unittest.cc
@@ -39,7 +39,7 @@
                                        CONTENT_SETTING_BLOCK);
   }
   histograms.ExpectBucketCount("Permissions.Action.Geolocation",
-                               PermissionAction::REVOKED, 1);
+                               static_cast<int>(PermissionAction::REVOKED), 1);
 
   // Block->Allow does not trigger a revocation.
   {
@@ -49,7 +49,7 @@
                                        CONTENT_SETTING_ALLOW);
   }
   histograms.ExpectBucketCount("Permissions.Action.Geolocation",
-                               PermissionAction::REVOKED, 1);
+                               static_cast<int>(PermissionAction::REVOKED), 1);
 
   // Allow->Default triggers a revocation when default is 'ask'.
   map->SetDefaultContentSetting(type, CONTENT_SETTING_ASK);
@@ -60,7 +60,7 @@
                                        CONTENT_SETTING_DEFAULT);
   }
   histograms.ExpectBucketCount("Permissions.Action.Geolocation",
-                               PermissionAction::REVOKED, 2);
+                               static_cast<int>(PermissionAction::REVOKED), 2);
 
   // Allow->Default does not trigger a revocation when default is 'allow'.
   map->SetDefaultContentSetting(type, CONTENT_SETTING_ALLOW);
@@ -71,7 +71,7 @@
                                        CONTENT_SETTING_DEFAULT);
   }
   histograms.ExpectBucketCount("Permissions.Action.Geolocation",
-                               PermissionAction::REVOKED, 2);
+                               static_cast<int>(PermissionAction::REVOKED), 2);
 
   // Allow->Block with url pattern string triggers a revocation.
   map->SetContentSettingDefaultScope(host, host, type, std::string(),
@@ -83,7 +83,7 @@
                                       std::string(), CONTENT_SETTING_BLOCK);
   }
   histograms.ExpectBucketCount("Permissions.Action.Geolocation",
-                               PermissionAction::REVOKED, 3);
+                               static_cast<int>(PermissionAction::REVOKED), 3);
 
   // Allow->Block with non url pattern string does not trigger a revocation.
   map->SetContentSettingDefaultScope(host, host, type, std::string(),
@@ -97,5 +97,5 @@
                                       CONTENT_SETTING_BLOCK);
   }
   histograms.ExpectBucketCount("Permissions.Action.Geolocation",
-                               PermissionAction::REVOKED, 3);
+                               static_cast<int>(PermissionAction::REVOKED), 3);
 }
diff --git a/chrome/browser/resources/settings/internet_page/compiled_resources2.gyp b/chrome/browser/resources/settings/internet_page/compiled_resources2.gyp
index 44831d3..6dcca85 100644
--- a/chrome/browser/resources/settings/internet_page/compiled_resources2.gyp
+++ b/chrome/browser/resources/settings/internet_page/compiled_resources2.gyp
@@ -124,6 +124,7 @@
         '<(DEPTH)/third_party/polymer/v1_0/components-chromium/iron-a11y-keys-behavior/compiled_resources2.gyp:iron-a11y-keys-behavior-extracted',
         '<(DEPTH)/ui/webui/resources/cr_elements/network/compiled_resources2.gyp:cr_onc_types',
         '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:assert',
+        '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:i18n_behavior',
         '<(INTERFACES_GYP):networking_private_interface',
       ],
       'includes': ['../../../../../third_party/closure_compiler/compile_js2.gypi'],
diff --git a/chrome/browser/resources/settings/internet_page/network_summary_item.html b/chrome/browser/resources/settings/internet_page/network_summary_item.html
index 85e79395..ee3e90f 100644
--- a/chrome/browser/resources/settings/internet_page/network_summary_item.html
+++ b/chrome/browser/resources/settings/internet_page/network_summary_item.html
@@ -3,6 +3,7 @@
 <link rel="import" href="chrome://resources/cr_elements/network/cr_network_list_item.html">
 <link rel="import" href="chrome://resources/html/action_link.html">
 <link rel="import" href="chrome://resources/html/action_link_css.html">
+<link rel="import" href="chrome://resources/html/i18n_behavior.html">
 <link rel="import" href="chrome://resources/html/polymer.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/iron-a11y-keys-behavior/iron-a11y-keys-behavior.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/iron-collapse/iron-collapse.html">
@@ -56,8 +57,7 @@
     <div class="settings-box two-line" actionable on-tap="onDetailsTap_">
       <div id="details"
           no-flex$="[[showSimInfo_(deviceState)]]">
-        <cr-network-list-item item="[[activeNetworkState]]" class="flex"
-            item-name="{{itemName}}">
+        <cr-network-list-item item="[[activeNetworkState]]" class="flex">
         </cr-network-list-item>
         <paper-spinner active="[[scanningIsActive_(deviceState, expanded_)]]"
             hidden$="[[!scanningIsVisible_(deviceState)]]">
@@ -90,7 +90,7 @@
       <template is="dom-if" if="[[enableToggleIsVisible_(deviceState)]]">
         <div class="secondary-action">
           <paper-toggle-button  id="deviceEnabledButton"
-              aria-label$="[[itemName]]"
+              aria-label$="[[getToggleA11yString_(deviceState)]]"
               checked="[[deviceIsEnabled_(deviceState)]]"
               enabled="[[enableToggleIsEnabled_(deviceState)]]"
               on-tap="onDeviceEnabledTap_">
diff --git a/chrome/browser/resources/settings/internet_page/network_summary_item.js b/chrome/browser/resources/settings/internet_page/network_summary_item.js
index 796ab65..a478660 100644
--- a/chrome/browser/resources/settings/internet_page/network_summary_item.js
+++ b/chrome/browser/resources/settings/internet_page/network_summary_item.js
@@ -13,7 +13,7 @@
 Polymer({
   is: 'network-summary-item',
 
-  behaviors: [Polymer.IronA11yKeysBehavior],
+  behaviors: [Polymer.IronA11yKeysBehavior, I18nBehavior],
 
   properties: {
     /**
@@ -31,9 +31,6 @@
      */
     activeNetworkState: Object,
 
-    /** String for a11y purposes. */
-    itemName: String,
-
     /**
      * List of all network state data for the network type.
      * @type {!Array<!CrOnc.NetworkStateProperties>}
@@ -238,6 +235,26 @@
   },
 
   /**
+   * @param {!DeviceStateProperties} deviceState
+   * @return {string}
+   * @private
+   */
+  getToggleA11yString_: function(deviceState) {
+    if (!this.enableToggleIsVisible_(deviceState))
+      return '';
+    switch (deviceState.Type) {
+      case CrOnc.Type.CELLULAR:
+        return this.i18n('internetToggleMobileA11yLabel');
+      case CrOnc.Type.WI_FI:
+        return this.i18n('internetToggleWiFiA11yLabel');
+      case CrOnc.Type.WI_MAX:
+        return this.i18n('internetToggleWiMAXA11yLabel');
+    }
+    assertNotReached();
+    return '';
+  },
+
+  /**
    * @return {boolean} Whether or not to show the UI to expand the list.
    * @private
    */
diff --git a/chrome/browser/resources/vr_shell/vr_shell_ui.js b/chrome/browser/resources/vr_shell/vr_shell_ui.js
index 12c5f8c..a6eedc1 100644
--- a/chrome/browser/resources/vr_shell/vr_shell_ui.js
+++ b/chrome/browser/resources/vr_shell/vr_shell_ui.js
@@ -109,9 +109,11 @@
       let anim;
       anim = new api.Animation(this.elementId, ANIM_DURATION);
       anim.setTranslation(0, y, -distance);
+      anim.setEasing(new api.InOutEasing());
       ui.addAnimation(anim);
       anim = new api.Animation(this.elementId, ANIM_DURATION);
       anim.setSize(height * this.SCREEN_RATIO, height);
+      anim.setEasing(new api.InOutEasing());
       ui.addAnimation(anim);
 
       ui.setBackgroundDistance(distance * this.BACKGROUND_DISTANCE_MULTIPLIER);
diff --git a/chrome/browser/resources/vr_shell/vr_shell_ui_api.js b/chrome/browser/resources/vr_shell/vr_shell_ui_api.js
index b3306be..48d2f26 100644
--- a/chrome/browser/resources/vr_shell/vr_shell_ui_api.js
+++ b/chrome/browser/resources/vr_shell/vr_shell_ui_api.js
@@ -366,7 +366,8 @@
   'LINEAR': 0,
   'CUBICBEZIER': 1,
   'EASEIN': 2,
-  'EASEOUT': 3
+  'EASEOUT': 3,
+  'EASEINOUT': 4
 };
 
 /** @const */ var DEFAULT_EASING_POW = 2;
@@ -419,6 +420,13 @@
   }
 }
 
+api.InOutEasing = class extends api.Easing {
+  constructor(pow = DEFAULT_EASING_POW) {
+    super(api.EasingType.EASEINOUT);
+    this.pow = pow;
+  }
+}
+
 /**
  * Base animation class. An animation can vary only one object property.
  * @struct
diff --git a/chrome/browser/safe_browsing/permission_reporter.cc b/chrome/browser/safe_browsing/permission_reporter.cc
index 6af846e6..ba00b2f 100644
--- a/chrome/browser/safe_browsing/permission_reporter.cc
+++ b/chrome/browser/safe_browsing/permission_reporter.cc
@@ -57,20 +57,20 @@
 
 PermissionReport::Action PermissionActionForReport(PermissionAction action) {
   switch (action) {
-    case GRANTED:
+    case PermissionAction::GRANTED:
       return PermissionReport::GRANTED;
-    case DENIED:
+    case PermissionAction::DENIED:
       return PermissionReport::DENIED;
-    case DISMISSED:
+    case PermissionAction::DISMISSED:
       return PermissionReport::DISMISSED;
-    case IGNORED:
+    case PermissionAction::IGNORED:
       return PermissionReport::IGNORED;
-    case REVOKED:
+    case PermissionAction::REVOKED:
       return PermissionReport::REVOKED;
-    case REENABLED:
-    case REQUESTED:
+    case PermissionAction::REENABLED:
+    case PermissionAction::REQUESTED:
       return PermissionReport::ACTION_UNSPECIFIED;
-    case PERMISSION_ACTION_NUM:
+    case PermissionAction::NUM:
       break;
   }
 
@@ -88,7 +88,7 @@
       return PermissionReport::SITE_SETTINGS;
     case PermissionSourceUI::PAGE_ACTION:
       return PermissionReport::PAGE_ACTION;
-    case PermissionSourceUI::SOURCE_UI_NUM:
+    case PermissionSourceUI::NUM:
       break;
   }
 
diff --git a/chrome/browser/safe_browsing/permission_reporter_unittest.cc b/chrome/browser/safe_browsing/permission_reporter_unittest.cc
index fd4996d3..e767369a57 100644
--- a/chrome/browser/safe_browsing/permission_reporter_unittest.cc
+++ b/chrome/browser/safe_browsing/permission_reporter_unittest.cc
@@ -32,7 +32,7 @@
     CONTENT_SETTINGS_TYPE_GEOLOCATION;
 const ContentSettingsType kDummyPermissionTwo =
     CONTENT_SETTINGS_TYPE_NOTIFICATIONS;
-const PermissionAction kDummyAction = GRANTED;
+const PermissionAction kDummyAction = PermissionAction::GRANTED;
 const PermissionSourceUI kDummySourceUI = PermissionSourceUI::PROMPT;
 const PermissionRequestGestureType kDummyGestureType =
     PermissionRequestGestureType::GESTURE;
diff --git a/chrome/browser/ui/search/search_ipc_router.cc b/chrome/browser/ui/search/search_ipc_router.cc
index e5e2ac7..6d44095 100644
--- a/chrome/browser/ui/search/search_ipc_router.cc
+++ b/chrome/browser/ui/search/search_ipc_router.cc
@@ -7,6 +7,8 @@
 #include <utility>
 
 #include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/search/instant_service.h"
+#include "chrome/browser/search/instant_service_factory.h"
 #include "chrome/browser/search/search.h"
 #include "chrome/common/render_messages.h"
 #include "components/search/search.h"
@@ -19,67 +21,65 @@
 
 namespace {
 
-bool IsRenderedInInstantProcess(content::WebContents* web_contents) {
-  // TODO(tibell): Instead of rejecting messages check at connection time if we
-  // want to talk to the render frame or not. Later, in an out-of-process iframe
-  // world, the IsRenderedInInstantProcess check will have to be done, as it's
-  // based on the RenderView.
-  Profile* profile =
-      Profile::FromBrowserContext(web_contents->GetBrowserContext());
-  return search::IsRenderedInInstantProcess(web_contents, profile);
+bool IsInInstantProcess(content::RenderFrameHost* render_frame) {
+  content::RenderProcessHost* process_host = render_frame->GetProcess();
+  const InstantService* instant_service = InstantServiceFactory::GetForProfile(
+      Profile::FromBrowserContext(process_host->GetBrowserContext()));
+  if (!instant_service)
+    return false;
+
+  return instant_service->IsInstantProcess(process_host->GetID());
 }
 
 class SearchBoxClientFactoryImpl
-    : public SearchIPCRouter::SearchBoxClientFactory {
+    : public SearchIPCRouter::SearchBoxClientFactory,
+      public chrome::mojom::EmbeddedSearchConnector {
  public:
-  // The |web_contents| must outlive this object.
-  explicit SearchBoxClientFactoryImpl(content::WebContents* web_contents)
-      : web_contents_(web_contents),
-        last_connected_rfh_(
-            std::make_pair(content::ChildProcessHost::kInvalidUniqueID,
-                           MSG_ROUTING_NONE)) {}
-  chrome::mojom::SearchBox* GetSearchBox() override;
+  // |web_contents| and |binding| must outlive this object.
+  SearchBoxClientFactoryImpl(
+      content::WebContents* web_contents,
+      mojo::AssociatedBinding<chrome::mojom::Instant>* binding)
+      : client_binding_(binding), factory_bindings_(web_contents, this) {
+    DCHECK(web_contents);
+    DCHECK(binding);
+    // Before we are connected to a frame we throw away all messages.
+    mojo::GetIsolatedProxy(&search_box_);
+  }
+
+  chrome::mojom::SearchBox* GetSearchBox() override {
+    return search_box_.get();
+  }
 
  private:
-  void OnConnectionError();
+  void Connect(chrome::mojom::InstantAssociatedRequest request,
+               chrome::mojom::SearchBoxAssociatedPtrInfo client) override;
 
-  content::WebContents* web_contents_;
+  // An interface used to push updates to the frame that connected to us. Before
+  // we've been connected to a frame, messages sent on this interface go into
+  // the void.
   chrome::mojom::SearchBoxAssociatedPtr search_box_;
 
-  // The proccess ID and routing ID of the last connected main frame. May be
-  // (ChildProcessHost::kInvalidUniqueID, MSG_ROUTING_NONE) if we haven't
-  // connected yet.
-  std::pair<int, int> last_connected_rfh_;
+  // Used to bind incoming interface requests to the implementation, which lives
+  // in SearchIPCRouter.
+  mojo::AssociatedBinding<chrome::mojom::Instant>* client_binding_;
+
+  // Binding used to listen to connection requests.
+  content::WebContentsFrameBindingSet<chrome::mojom::EmbeddedSearchConnector>
+      factory_bindings_;
 
   DISALLOW_COPY_AND_ASSIGN(SearchBoxClientFactoryImpl);
 };
 
-chrome::mojom::SearchBox* SearchBoxClientFactoryImpl::GetSearchBox() {
-  content::RenderFrameHost* frame = web_contents_->GetMainFrame();
-  auto id = std::make_pair(frame->GetProcess()->GetID(), frame->GetRoutingID());
-  // Avoid reconnecting repeatedly to the same RenderFrameHost for performance
-  // reasons.
-  if (id != last_connected_rfh_) {
-    if (IsRenderedInInstantProcess(web_contents_)) {
-      frame->GetRemoteAssociatedInterfaces()->GetInterface(&search_box_);
-      search_box_.set_connection_error_handler(
-          base::Bind(&SearchBoxClientFactoryImpl::OnConnectionError,
-                     base::Unretained(this)));
-    } else {
-      // Renderer is not an instant process. We'll create a connection that
-      // drops all messages.
-      mojo::GetIsolatedProxy(&search_box_);
-    }
-    last_connected_rfh_ = id;
+void SearchBoxClientFactoryImpl::Connect(
+    chrome::mojom::InstantAssociatedRequest request,
+    chrome::mojom::SearchBoxAssociatedPtrInfo client) {
+  content::RenderFrameHost* frame = factory_bindings_.GetCurrentTargetFrame();
+  const bool is_main_frame = frame->GetParent() == nullptr;
+  if (!IsInInstantProcess(frame) || !is_main_frame) {
+    return;
   }
-  return search_box_.get();
-}
-
-void SearchBoxClientFactoryImpl::OnConnectionError() {
-  search_box_.reset();
-  last_connected_rfh_ = std::make_pair(
-      content::ChildProcessHost::kInvalidUniqueID,
-      MSG_ROUTING_NONE);
+  client_binding_->Bind(std::move(request));
+  search_box_.Bind(std::move(client));
 }
 
 }  // namespace
@@ -92,14 +92,15 @@
       policy_(std::move(policy)),
       commit_counter_(0),
       is_active_tab_(false),
-      bindings_(web_contents, this),
-      search_box_client_factory_(new SearchBoxClientFactoryImpl(web_contents)) {
+      binding_(this),
+      search_box_client_factory_(
+          new SearchBoxClientFactoryImpl(web_contents, &binding_)) {
   DCHECK(web_contents);
   DCHECK(delegate);
   DCHECK(policy_.get());
 }
 
-SearchIPCRouter::~SearchIPCRouter() {}
+SearchIPCRouter::~SearchIPCRouter() = default;
 
 void SearchIPCRouter::OnNavigationEntryCommitted() {
   ++commit_counter_;
@@ -183,8 +184,6 @@
 
 void SearchIPCRouter::InstantSupportDetermined(int page_seq_no,
                                                bool instant_support) {
-  if (!IsRenderedInInstantProcess(web_contents()))
-    return;
   if (page_seq_no != commit_counter_)
     return;
 
@@ -192,8 +191,6 @@
 }
 
 void SearchIPCRouter::FocusOmnibox(int page_seq_no, OmniboxFocusState state) {
-  if (!IsRenderedInInstantProcess(web_contents()))
-    return;
   if (page_seq_no != commit_counter_)
     return;
 
@@ -205,8 +202,6 @@
 }
 
 void SearchIPCRouter::DeleteMostVisitedItem(int page_seq_no, const GURL& url) {
-  if (!IsRenderedInInstantProcess(web_contents()))
-    return;
   if (page_seq_no != commit_counter_)
     return;
 
@@ -219,8 +214,6 @@
 
 void SearchIPCRouter::UndoMostVisitedDeletion(int page_seq_no,
                                               const GURL& url) {
-  if (!IsRenderedInInstantProcess(web_contents()))
-    return;
   if (page_seq_no != commit_counter_)
     return;
 
@@ -232,8 +225,6 @@
 }
 
 void SearchIPCRouter::UndoAllMostVisitedDeletions(int page_seq_no) {
-  if (!IsRenderedInInstantProcess(web_contents()))
-    return;
   if (page_seq_no != commit_counter_)
     return;
 
@@ -247,8 +238,6 @@
 void SearchIPCRouter::LogEvent(int page_seq_no,
                                NTPLoggingEventType event,
                                base::TimeDelta time) {
-  if (!IsRenderedInInstantProcess(web_contents()))
-    return;
   if (page_seq_no != commit_counter_)
     return;
 
@@ -263,8 +252,6 @@
     int page_seq_no,
     int position,
     ntp_tiles::NTPTileSource tile_source) {
-  if (!IsRenderedInInstantProcess(web_contents()))
-    return;
   if (page_seq_no != commit_counter_)
     return;
 
@@ -280,8 +267,6 @@
     int page_seq_no,
     int position,
     ntp_tiles::NTPTileSource tile_source) {
-  if (!IsRenderedInInstantProcess(web_contents()))
-    return;
   if (page_seq_no != commit_counter_)
     return;
 
@@ -295,8 +280,6 @@
 
 void SearchIPCRouter::PasteAndOpenDropdown(int page_seq_no,
                                            const base::string16& text) {
-  if (!IsRenderedInInstantProcess(web_contents()))
-    return;
   if (page_seq_no != commit_counter_)
     return;
 
@@ -309,8 +292,6 @@
 
 void SearchIPCRouter::ChromeIdentityCheck(int page_seq_no,
                                           const base::string16& identity) {
-  if (!IsRenderedInInstantProcess(web_contents()))
-    return;
   if (page_seq_no != commit_counter_)
     return;
 
@@ -322,8 +303,6 @@
 }
 
 void SearchIPCRouter::HistorySyncCheck(int page_seq_no) {
-  if (!IsRenderedInInstantProcess(web_contents()))
-    return;
   if (page_seq_no != commit_counter_)
     return;
 
diff --git a/chrome/browser/ui/search/search_ipc_router.h b/chrome/browser/ui/search/search_ipc_router.h
index dd66614..62bbfb1 100644
--- a/chrome/browser/ui/search/search_ipc_router.h
+++ b/chrome/browser/ui/search/search_ipc_router.h
@@ -237,7 +237,10 @@
   // Set to true, when the tab corresponding to |this| instance is active.
   bool is_active_tab_;
 
-  content::WebContentsFrameBindingSet<chrome::mojom::Instant> bindings_;
+  // Binding for the connected main frame. We only allow one frame to connect at
+  // the moment, but this could be extended to a map of connected frames, if
+  // desired.
+  mojo::AssociatedBinding<chrome::mojom::Instant> binding_;
 
   std::unique_ptr<SearchBoxClientFactory> search_box_client_factory_;
 
diff --git a/chrome/browser/ui/search/search_ipc_router_unittest.cc b/chrome/browser/ui/search/search_ipc_router_unittest.cc
index cc5e03b..ebbc772b 100644
--- a/chrome/browser/ui/search/search_ipc_router_unittest.cc
+++ b/chrome/browser/ui/search/search_ipc_router_unittest.cc
@@ -183,21 +183,6 @@
   MockSearchBox mock_search_box_;
 };
 
-TEST_F(SearchIPCRouterTest, IgnoreMessagesFromNonInstantRenderers) {
-  NavigateAndCommitActiveTab(GURL("file://foo/bar"));
-  SetupMockDelegateAndPolicy();
-  EXPECT_CALL(*mock_delegate(), FocusOmnibox(OMNIBOX_FOCUS_VISIBLE)).Times(0);
-  content::WebContents* contents = web_contents();
-  bool is_active_tab = IsActiveTab(contents);
-  EXPECT_TRUE(is_active_tab);
-
-  MockSearchIPCRouterPolicy* policy = GetSearchIPCRouterPolicy();
-  EXPECT_CALL(*policy, ShouldProcessFocusOmnibox(is_active_tab)).Times(0);
-
-  GetSearchIPCRouter().FocusOmnibox(GetSearchIPCRouterSeqNo(),
-                                    OMNIBOX_FOCUS_VISIBLE);
-}
-
 TEST_F(SearchIPCRouterTest, ProcessFocusOmniboxMsg) {
   NavigateAndCommitActiveTab(GURL(chrome::kChromeSearchLocalNtpUrl));
   SetupMockDelegateAndPolicy();
@@ -434,47 +419,6 @@
   GetSearchIPCRouter().UndoAllMostVisitedDeletions(GetSearchIPCRouterSeqNo());
 }
 
-TEST_F(SearchIPCRouterTest, IgnoreMessageIfThePageIsNotActive) {
-  NavigateAndCommitActiveTab(GURL(chrome::kChromeSearchLocalNtpUrl));
-  SetupMockDelegateAndPolicy();
-  MockSearchIPCRouterPolicy* policy = GetSearchIPCRouterPolicy();
-  int page_seq_no = GetSearchIPCRouterSeqNo();
-
-  content::WebContents* contents = web_contents();
-  bool is_active_tab = IsActiveTab(contents);
-  GURL item_url("www.foo.com");
-
-  // Navigate away from the NTP. Afterwards, all messages should be ignored.
-  NavigateAndCommitActiveTab(item_url);
-
-  EXPECT_CALL(*mock_delegate(), OnDeleteMostVisitedItem(item_url)).Times(0);
-  EXPECT_CALL(*policy, ShouldProcessDeleteMostVisitedItem()).Times(0);
-  GetSearchIPCRouter().DeleteMostVisitedItem(page_seq_no, item_url);
-
-  EXPECT_CALL(*mock_delegate(), OnUndoMostVisitedDeletion(item_url)).Times(0);
-  EXPECT_CALL(*policy, ShouldProcessUndoMostVisitedDeletion()).Times(0);
-  GetSearchIPCRouter().UndoMostVisitedDeletion(page_seq_no, item_url);
-
-  EXPECT_CALL(*mock_delegate(), OnUndoAllMostVisitedDeletions()).Times(0);
-  EXPECT_CALL(*policy, ShouldProcessUndoAllMostVisitedDeletions()).Times(0);
-  GetSearchIPCRouter().UndoAllMostVisitedDeletions(page_seq_no);
-
-  EXPECT_CALL(*mock_delegate(), FocusOmnibox(OMNIBOX_FOCUS_VISIBLE)).Times(0);
-  EXPECT_CALL(*policy, ShouldProcessFocusOmnibox(is_active_tab)).Times(0);
-  GetSearchIPCRouter().FocusOmnibox(page_seq_no, OMNIBOX_FOCUS_VISIBLE);
-
-  base::TimeDelta delta = base::TimeDelta::FromMilliseconds(123);
-  EXPECT_CALL(*mock_delegate(), OnLogEvent(NTP_ALL_TILES_LOADED, delta))
-      .Times(0);
-  EXPECT_CALL(*policy, ShouldProcessLogEvent()).Times(0);
-  GetSearchIPCRouter().LogEvent(page_seq_no, NTP_ALL_TILES_LOADED, delta);
-
-  base::string16 text;
-  EXPECT_CALL(*mock_delegate(), PasteIntoOmnibox(text)).Times(0);
-  EXPECT_CALL(*policy, ShouldProcessPasteIntoOmnibox(is_active_tab)).Times(0);
-  GetSearchIPCRouter().PasteAndOpenDropdown(page_seq_no, text);
-}
-
 TEST_F(SearchIPCRouterTest, ProcessPasteAndOpenDropdownMsg) {
   NavigateAndCommitActiveTab(GURL(chrome::kChromeSearchLocalNtpUrl));
   SetupMockDelegateAndPolicy();
diff --git a/chrome/browser/ui/views/profiles/profile_chooser_view.cc b/chrome/browser/ui/views/profiles/profile_chooser_view.cc
index 62702ba..8af320e 100644
--- a/chrome/browser/ui/views/profiles/profile_chooser_view.cc
+++ b/chrome/browser/ui/views/profiles/profile_chooser_view.cc
@@ -195,6 +195,7 @@
 #endif
         title_(nullptr),
         subtitle_(nullptr) {
+    DCHECK(profile_chooser_view);
     SetImageLabelSpacing(switches::IsMaterialDesignUserMenu()
                              ? (kMaterialMenuEdgeMargin - 2)
                              : views::kItemLabelSpacing);
@@ -2133,9 +2134,8 @@
   int available_width = width - 2 * views::kButtonHEdgeMarginNew -
                         kDeleteButtonWidth - warning_button_width;
   views::LabelButton* email_button = new BackgroundColorHoverButton(
-      reauth_required ? this : NULL,
-      base::UTF8ToUTF16(email),
-      warning_default_image);
+      this, base::UTF8ToUTF16(email), warning_default_image);
+  email_button->SetEnabled(reauth_required);
   email_button->SetElideBehavior(gfx::ELIDE_EMAIL);
   email_button->SetMinSize(gfx::Size(0, kButtonHeight));
   email_button->SetMaxSize(gfx::Size(available_width, kButtonHeight));
diff --git a/chrome/browser/ui/webui/settings/md_settings_localized_strings_provider.cc b/chrome/browser/ui/webui/settings/md_settings_localized_strings_provider.cc
index 30cbca72..49aea1b4 100644
--- a/chrome/browser/ui/webui/settings/md_settings_localized_strings_provider.cc
+++ b/chrome/browser/ui/webui/settings/md_settings_localized_strings_provider.cc
@@ -834,6 +834,12 @@
       {"internetDetailPageTitle", IDS_SETTINGS_INTERNET_DETAIL},
       {"internetKnownNetworksPageTitle", IDS_SETTINGS_INTERNET_KNOWN_NETWORKS},
       {"internetPageTitle", IDS_SETTINGS_INTERNET},
+      {"internetToggleMobileA11yLabel",
+       IDS_SETTINGS_INTERNET_TOGGLE_MOBILE_ACCESSIBILITY_LABEL},
+      {"internetToggleWiFiA11yLabel",
+       IDS_SETTINGS_INTERNET_TOGGLE_WIFI_ACCESSIBILITY_LABEL},
+      {"internetToggleWiMAXA11yLabel",
+       IDS_SETTINGS_INTERNET_TOGGLE_WIMAX_ACCESSIBILITY_LABEL},
       {"knownNetworksAll", IDS_SETTINGS_INTERNET_KNOWN_NETWORKS_ALL},
       {"knownNetworksButton", IDS_SETTINGS_INTERNET_KNOWN_NETWORKS_BUTTON},
       {"knownNetworksMessage", IDS_SETTINGS_INTERNET_KNOWN_NETWORKS_MESSAGE},
diff --git a/chrome/common/instant.mojom b/chrome/common/instant.mojom
index a9f0fdd..287c846 100644
--- a/chrome/common/instant.mojom
+++ b/chrome/common/instant.mojom
@@ -17,6 +17,21 @@
 [Native]
 enum NTPTileSource;
 
+// Interface used to connect to the embedded search interface. This is a
+// separate interface such that a reverse connection (|client| below) can be
+// passed when connecting.
+interface EmbeddedSearchConnector {
+  // Connect to the interface. |instant| is the connection which the client will
+  // use to query the embedded search interface. |client| is the connection used
+  // by the embedded search interface implementation to push browser state
+  // updates to the client.
+  Connect(associated Instant& instant, associated SearchBox client);
+};
+
+// TODO(tibell): Rename this to EmbeddedSearch.
+//
+// Browser interface to support embedded search. Render frames connect to this
+// interface to query browser data, such as the most visited pages.
 interface Instant {
   // Tells InstantExtended whether the embedded search API is supported.
   // See http://dev.chromium.org/embeddedsearch
@@ -77,6 +92,10 @@
 [Native]
 struct ThemeBackgroundInfo;
 
+// TODO(tibell): Rename this to EmbeddedSearchClient.
+//
+// Renderer interface used by the browser to push updates to the client. For
+// example, the browser will tell the frame if the omnibox got focus.
 interface SearchBox {
   SetPageSequenceNumber(int32 page_seq_no);
 
@@ -96,6 +115,8 @@
 
   ThemeChanged(ThemeBackgroundInfo value);
 
+  // TODO(treib): Remove the *Result methods and instead add return values to
+  // the correspnding methods in the Instant interface above.
   HistorySyncCheckResult(bool sync_history);
 
   ChromeIdentityCheckResult(mojo.common.mojom.String16 identity,
diff --git a/chrome/renderer/searchbox/searchbox.cc b/chrome/renderer/searchbox/searchbox.cc
index 2b898d8..256a26a 100644
--- a/chrome/renderer/searchbox/searchbox.cc
+++ b/chrome/renderer/searchbox/searchbox.cc
@@ -244,10 +244,13 @@
       most_visited_items_cache_(kMaxInstantMostVisitedItemCacheSize),
       query_(),
       binding_(this) {
-  render_frame->GetRemoteAssociatedInterfaces()->GetInterface(
-      &instant_service_);
-  render_frame->GetAssociatedInterfaceRegistry()->AddInterface(
-      base::Bind(&SearchBox::Bind, base::Unretained(this)));
+  // Connect to the embedded search interface in the browser.
+  chrome::mojom::EmbeddedSearchConnectorAssociatedPtr connector;
+  render_frame->GetRemoteAssociatedInterfaces()->GetInterface(&connector);
+  chrome::mojom::SearchBoxAssociatedPtrInfo search_box;
+  binding_.Bind(&search_box);
+  connector->Connect(mojo::MakeRequest(&instant_service_),
+                     std::move(search_box));
 }
 
 SearchBox::~SearchBox() {
diff --git a/chromeos/components/tether/BUILD.gn b/chromeos/components/tether/BUILD.gn
index 47f53248..6c4bc46 100644
--- a/chromeos/components/tether/BUILD.gn
+++ b/chromeos/components/tether/BUILD.gn
@@ -24,6 +24,8 @@
     "initializer.h",
     "local_device_data_provider.cc",
     "local_device_data_provider.h",
+    "message_transfer_operation.cc",
+    "message_transfer_operation.h",
     "message_wrapper.cc",
     "message_wrapper.h",
   ]
@@ -47,6 +49,8 @@
   testonly = true
 
   sources = [
+    "fake_ble_connection_manager.cc",
+    "fake_ble_connection_manager.h",
     "mock_local_device_data_provider.cc",
     "mock_local_device_data_provider.h",
   ]
@@ -72,6 +76,7 @@
     "ble_scanner_unittest.cc",
     "host_scan_scheduler_unittest.cc",
     "local_device_data_provider_unittest.cc",
+    "message_transfer_operation_unittest.cc",
     "message_wrapper_unittest.cc",
   ]
 
diff --git a/chromeos/components/tether/ble_connection_manager.cc b/chromeos/components/tether/ble_connection_manager.cc
index 0b6c4ce..b486079 100644
--- a/chromeos/components/tether/ble_connection_manager.cc
+++ b/chromeos/components/tether/ble_connection_manager.cc
@@ -197,17 +197,23 @@
       device_queue_(std::move(device_queue)),
       timer_factory_(std::move(timer_factory)),
       bluetooth_throttler_(bluetooth_throttler),
-      weak_ptr_factory_(this) {
-  ble_scanner_->AddObserver(this);
-}
+      has_registered_observer_(false),
+      weak_ptr_factory_(this) {}
 
 BleConnectionManager::~BleConnectionManager() {
-  ble_scanner_->RemoveObserver(this);
+  if (has_registered_observer_) {
+    ble_scanner_->RemoveObserver(this);
+  }
 }
 
 void BleConnectionManager::RegisterRemoteDevice(
     const cryptauth::RemoteDevice& remote_device,
     const MessageType& connection_reason) {
+  if (!has_registered_observer_) {
+    ble_scanner_->AddObserver(this);
+  }
+  has_registered_observer_ = true;
+
   PA_LOG(INFO) << "Registering device with ID "
                << remote_device.GetTruncatedDeviceIdForLogs() << " for reason "
                << MessageTypeToString(connection_reason);
@@ -277,6 +283,19 @@
   connection_metadata->SendMessage(message);
 }
 
+bool BleConnectionManager::GetStatusForDevice(
+    const cryptauth::RemoteDevice& remote_device,
+    cryptauth::SecureChannel::Status* status) const {
+  std::shared_ptr<ConnectionMetadata> connection_metadata =
+      GetConnectionMetadata(remote_device);
+  if (!connection_metadata) {
+    return false;
+  }
+
+  *status = connection_metadata->GetStatus();
+  return true;
+}
+
 void BleConnectionManager::AddObserver(Observer* observer) {
   observer_list_.AddObserver(observer);
 }
@@ -321,7 +340,7 @@
 
 std::shared_ptr<BleConnectionManager::ConnectionMetadata>
 BleConnectionManager::GetConnectionMetadata(
-    const cryptauth::RemoteDevice& remote_device) {
+    const cryptauth::RemoteDevice& remote_device) const {
   const auto map_iter = device_to_metadata_map_.find(remote_device);
   if (map_iter == device_to_metadata_map_.end()) {
     return nullptr;
diff --git a/chromeos/components/tether/ble_connection_manager.h b/chromeos/components/tether/ble_connection_manager.h
index 1d0e8bb..96a01b88 100644
--- a/chromeos/components/tether/ble_connection_manager.h
+++ b/chromeos/components/tether/ble_connection_manager.h
@@ -82,19 +82,28 @@
   // Registers |remote_device| for |connection_reason|. Once registered, this
   // instance will continue to attempt to connect and authenticate to that
   // device until the device is unregistered.
-  void RegisterRemoteDevice(const cryptauth::RemoteDevice& remote_device,
-                            const MessageType& connection_reason);
+  virtual void RegisterRemoteDevice(
+      const cryptauth::RemoteDevice& remote_device,
+      const MessageType& connection_reason);
 
   // Unregisters |remote_device| for |connection_reason|. Once registered, a
   // device will continue trying to connect until *ALL* of its
   // MessageTypes have been unregistered.
-  void UnregisterRemoteDevice(const cryptauth::RemoteDevice& remote_device,
-                              const MessageType& connection_reason);
+  virtual void UnregisterRemoteDevice(
+      const cryptauth::RemoteDevice& remote_device,
+      const MessageType& connection_reason);
 
   // Sends |message| to |remote_device|. This function can only be called if the
   // given device is authenticated.
-  void SendMessage(const cryptauth::RemoteDevice& remote_device,
-                   const std::string& message);
+  virtual void SendMessage(const cryptauth::RemoteDevice& remote_device,
+                           const std::string& message);
+
+  // Gets |remote_device|'s status and stores it to |status|, returning whether
+  // |remote_device| is registered. If this function returns |false|, no value
+  // is saved to |status|.
+  virtual bool GetStatusForDevice(
+      const cryptauth::RemoteDevice& remote_device,
+      cryptauth::SecureChannel::Status* status) const;
 
   void AddObserver(Observer* observer);
   void RemoveObserver(Observer* observer);
@@ -104,6 +113,14 @@
       const std::string& device_address,
       cryptauth::RemoteDevice remote_device) override;
 
+ protected:
+  void SendMessageReceivedEvent(const cryptauth::RemoteDevice& remote_device,
+                                const std::string& payload);
+  void SendSecureChannelStatusChangeEvent(
+      const cryptauth::RemoteDevice& remote_device,
+      const cryptauth::SecureChannel::Status& old_status,
+      const cryptauth::SecureChannel::Status& new_status);
+
  private:
   friend class BleConnectionManagerTest;
 
@@ -172,7 +189,7 @@
       cryptauth::BluetoothThrottler* bluetooth_throttler);
 
   std::shared_ptr<ConnectionMetadata> GetConnectionMetadata(
-      const cryptauth::RemoteDevice& remote_device);
+      const cryptauth::RemoteDevice& remote_device) const;
   std::shared_ptr<ConnectionMetadata> AddMetadataForDevice(
       const cryptauth::RemoteDevice& remote_device);
 
@@ -189,13 +206,6 @@
       const cryptauth::SecureChannel::Status& old_status,
       const cryptauth::SecureChannel::Status& new_status);
 
-  void SendMessageReceivedEvent(const cryptauth::RemoteDevice& remote_device,
-                                const std::string& payload);
-  void SendSecureChannelStatusChangeEvent(
-      const cryptauth::RemoteDevice& remote_device,
-      const cryptauth::SecureChannel::Status& old_status,
-      const cryptauth::SecureChannel::Status& new_status);
-
   std::unique_ptr<Delegate> delegate_;
   scoped_refptr<device::BluetoothAdapter> adapter_;
   std::unique_ptr<BleScanner> ble_scanner_;
@@ -204,6 +214,7 @@
   std::unique_ptr<TimerFactory> timer_factory_;
   cryptauth::BluetoothThrottler* bluetooth_throttler_;
 
+  bool has_registered_observer_;
   std::map<cryptauth::RemoteDevice, std::shared_ptr<ConnectionMetadata>>
       device_to_metadata_map_;
 
diff --git a/chromeos/components/tether/ble_connection_manager_unittest.cc b/chromeos/components/tether/ble_connection_manager_unittest.cc
index a3f40a1..6ce9b41a 100644
--- a/chromeos/components/tether/ble_connection_manager_unittest.cc
+++ b/chromeos/components/tether/ble_connection_manager_unittest.cc
@@ -718,6 +718,72 @@
   VerifyDeviceNotRegistered(test_devices_[0]);
 }
 
+TEST_F(BleConnectionManagerTest, TestGetStatusForDevice) {
+  EXPECT_CALL(*mock_ble_scanner_,
+              RegisterScanFilterForDevice(test_devices_[0]));
+  EXPECT_CALL(*mock_ble_advertiser_,
+              StartAdvertisingToDevice(test_devices_[0]));
+  EXPECT_CALL(*mock_ble_scanner_,
+              UnregisterScanFilterForDevice(test_devices_[0]));
+  EXPECT_CALL(*mock_ble_advertiser_, StopAdvertisingToDevice(test_devices_[0]));
+
+  cryptauth::SecureChannel::Status status;
+
+  // Should return false when the device has not yet been registered at all.
+  EXPECT_FALSE(manager_->GetStatusForDevice(test_devices_[0], &status));
+
+  manager_->RegisterRemoteDevice(test_devices_[0],
+                                 MessageType::TETHER_AVAILABILITY_REQUEST);
+  VerifyAdvertisingTimeoutSet(test_devices_[0]);
+  VerifyConnectionStateChanges(std::vector<SecureChannelStatusChange>{
+      {test_devices_[0], cryptauth::SecureChannel::Status::DISCONNECTED,
+       cryptauth::SecureChannel::Status::CONNECTING}});
+
+  // Should be CONNECTING at this point.
+  EXPECT_TRUE(manager_->GetStatusForDevice(test_devices_[0], &status));
+  EXPECT_EQ(cryptauth::SecureChannel::Status::CONNECTING, status);
+
+  fake_secure_channel_factory_->SetExpectedDeviceAddress(
+      std::string(kBluetoothAddress1));
+  mock_ble_scanner_->SimulateScanResults(std::string(kBluetoothAddress1),
+                                         test_devices_[0]);
+  FakeSecureChannel* channel = GetChannelForDevice(test_devices_[0]);
+
+  channel->ChangeStatus(cryptauth::SecureChannel::Status::CONNECTING);
+  EXPECT_TRUE(manager_->GetStatusForDevice(test_devices_[0], &status));
+  EXPECT_EQ(cryptauth::SecureChannel::Status::CONNECTING, status);
+
+  channel->ChangeStatus(cryptauth::SecureChannel::Status::CONNECTED);
+  VerifyConnectionStateChanges(std::vector<SecureChannelStatusChange>{
+      {test_devices_[0], cryptauth::SecureChannel::Status::CONNECTING,
+       cryptauth::SecureChannel::Status::CONNECTED}});
+  EXPECT_TRUE(manager_->GetStatusForDevice(test_devices_[0], &status));
+  EXPECT_EQ(cryptauth::SecureChannel::Status::CONNECTED, status);
+
+  channel->ChangeStatus(cryptauth::SecureChannel::Status::AUTHENTICATING);
+  VerifyConnectionStateChanges(std::vector<SecureChannelStatusChange>{
+      {test_devices_[0], cryptauth::SecureChannel::Status::CONNECTED,
+       cryptauth::SecureChannel::Status::AUTHENTICATING}});
+  EXPECT_TRUE(manager_->GetStatusForDevice(test_devices_[0], &status));
+  EXPECT_EQ(cryptauth::SecureChannel::Status::AUTHENTICATING, status);
+
+  channel->ChangeStatus(cryptauth::SecureChannel::Status::AUTHENTICATED);
+  VerifyConnectionStateChanges(std::vector<SecureChannelStatusChange>{
+      {test_devices_[0], cryptauth::SecureChannel::Status::AUTHENTICATING,
+       cryptauth::SecureChannel::Status::AUTHENTICATED}});
+  EXPECT_TRUE(manager_->GetStatusForDevice(test_devices_[0], &status));
+  EXPECT_EQ(cryptauth::SecureChannel::Status::AUTHENTICATED, status);
+
+  // Now, unregister the device and check that GetStatusForDevice() once again
+  // returns false.
+  manager_->UnregisterRemoteDevice(test_devices_[0],
+                                   MessageType::TETHER_AVAILABILITY_REQUEST);
+  VerifyConnectionStateChanges(std::vector<SecureChannelStatusChange>{
+      {test_devices_[0], cryptauth::SecureChannel::Status::AUTHENTICATED,
+       cryptauth::SecureChannel::Status::DISCONNECTED}});
+  EXPECT_FALSE(manager_->GetStatusForDevice(test_devices_[0], &status));
+}
+
 TEST_F(BleConnectionManagerTest,
        TestSuccessfulConnection_DisconnectsAfterConnection) {
   // A reconnection attempt is expected once the disconnection occurs, meaning
diff --git a/chromeos/components/tether/fake_ble_connection_manager.cc b/chromeos/components/tether/fake_ble_connection_manager.cc
new file mode 100644
index 0000000..17e68fc
--- /dev/null
+++ b/chromeos/components/tether/fake_ble_connection_manager.cc
@@ -0,0 +1,89 @@
+// 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 "chromeos/components/tether/fake_ble_connection_manager.h"
+
+namespace chromeos {
+
+namespace tether {
+
+FakeBleConnectionManager::StatusAndRegisteredMessageTypes::
+    StatusAndRegisteredMessageTypes()
+    : status(cryptauth::SecureChannel::Status::DISCONNECTED) {}
+
+FakeBleConnectionManager::StatusAndRegisteredMessageTypes::
+    StatusAndRegisteredMessageTypes(
+        const StatusAndRegisteredMessageTypes& other)
+    : status(other.status),
+      registered_message_types(other.registered_message_types) {}
+
+FakeBleConnectionManager::StatusAndRegisteredMessageTypes::
+    ~StatusAndRegisteredMessageTypes() {}
+
+FakeBleConnectionManager::FakeBleConnectionManager()
+    : BleConnectionManager(nullptr, nullptr, nullptr, nullptr, nullptr) {}
+
+FakeBleConnectionManager::~FakeBleConnectionManager() {}
+
+void FakeBleConnectionManager::SetDeviceStatus(
+    const cryptauth::RemoteDevice& remote_device,
+    const cryptauth::SecureChannel::Status& status) {
+  const auto iter = device_map_.find(remote_device);
+  DCHECK(iter != device_map_.end());
+
+  cryptauth::SecureChannel::Status old_status = iter->second.status;
+  if (old_status == status) {
+    // If the status has not changed, do not do anything.
+    return;
+  }
+
+  iter->second.status = status;
+  SendSecureChannelStatusChangeEvent(remote_device, old_status, status);
+}
+
+void FakeBleConnectionManager::ReceiveMessage(
+    const cryptauth::RemoteDevice& remote_device,
+    const std::string& payload) {
+  DCHECK(device_map_.find(remote_device) != device_map_.end());
+  SendMessageReceivedEvent(remote_device, payload);
+}
+
+void FakeBleConnectionManager::RegisterRemoteDevice(
+    const cryptauth::RemoteDevice& remote_device,
+    const MessageType& connection_reason) {
+  StatusAndRegisteredMessageTypes& value = device_map_[remote_device];
+  value.registered_message_types.insert(connection_reason);
+}
+
+void FakeBleConnectionManager::UnregisterRemoteDevice(
+    const cryptauth::RemoteDevice& remote_device,
+    const MessageType& connection_reason) {
+  StatusAndRegisteredMessageTypes& value = device_map_[remote_device];
+  value.registered_message_types.erase(connection_reason);
+  if (value.registered_message_types.empty()) {
+    device_map_.erase(remote_device);
+  }
+}
+
+void FakeBleConnectionManager::SendMessage(
+    const cryptauth::RemoteDevice& remote_device,
+    const std::string& message) {
+  sent_messages_.push_back({remote_device, message});
+}
+
+bool FakeBleConnectionManager::GetStatusForDevice(
+    const cryptauth::RemoteDevice& remote_device,
+    cryptauth::SecureChannel::Status* status) const {
+  const auto iter = device_map_.find(remote_device);
+  if (iter == device_map_.end()) {
+    return false;
+  }
+
+  *status = iter->second.status;
+  return true;
+}
+
+}  // namespace tether
+
+}  // namespace chromeos
diff --git a/chromeos/components/tether/fake_ble_connection_manager.h b/chromeos/components/tether/fake_ble_connection_manager.h
new file mode 100644
index 0000000..392f0217
--- /dev/null
+++ b/chromeos/components/tether/fake_ble_connection_manager.h
@@ -0,0 +1,69 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROMEOS_COMPONENTS_TETHER_FAKE_BLE_CONNECTION_MANAGER_H_
+#define CHROMEOS_COMPONENTS_TETHER_FAKE_BLE_CONNECTION_MANAGER_H_
+
+#include <map>
+#include <set>
+
+#include "base/macros.h"
+#include "chromeos/components/tether/ble_connection_manager.h"
+#include "components/cryptauth/remote_device.h"
+
+namespace chromeos {
+
+namespace tether {
+
+// Test double for BleConnectionManager.
+class FakeBleConnectionManager : public BleConnectionManager {
+ public:
+  FakeBleConnectionManager();
+  ~FakeBleConnectionManager() override;
+
+  struct SentMessage {
+    cryptauth::RemoteDevice remote_device;
+    std::string message;
+  };
+
+  void SetDeviceStatus(const cryptauth::RemoteDevice& remote_device,
+                       const cryptauth::SecureChannel::Status& status);
+  void ReceiveMessage(const cryptauth::RemoteDevice& remote_device,
+                      const std::string& payload);
+  std::vector<SentMessage>& sent_messages() { return sent_messages_; }
+
+  // BleConnectionManager:
+  void RegisterRemoteDevice(const cryptauth::RemoteDevice& remote_device,
+                            const MessageType& connection_reason) override;
+  void UnregisterRemoteDevice(const cryptauth::RemoteDevice& remote_device,
+                              const MessageType& connection_reason) override;
+  void SendMessage(const cryptauth::RemoteDevice& remote_device,
+                   const std::string& message) override;
+  bool GetStatusForDevice(
+      const cryptauth::RemoteDevice& remote_device,
+      cryptauth::SecureChannel::Status* status) const override;
+
+ private:
+  struct StatusAndRegisteredMessageTypes {
+    StatusAndRegisteredMessageTypes();
+    StatusAndRegisteredMessageTypes(
+        const StatusAndRegisteredMessageTypes& other);
+    ~StatusAndRegisteredMessageTypes();
+
+    cryptauth::SecureChannel::Status status;
+    std::set<MessageType> registered_message_types;
+  };
+
+  std::map<cryptauth::RemoteDevice, StatusAndRegisteredMessageTypes>
+      device_map_;
+  std::vector<SentMessage> sent_messages_;
+
+  DISALLOW_COPY_AND_ASSIGN(FakeBleConnectionManager);
+};
+
+}  // namespace tether
+
+}  // namespace chromeos
+
+#endif  // CHROMEOS_COMPONENTS_TETHER_FAKE_BLE_CONNECTION_MANAGER_H_
diff --git a/chromeos/components/tether/message_transfer_operation.cc b/chromeos/components/tether/message_transfer_operation.cc
new file mode 100644
index 0000000..1b174f4
--- /dev/null
+++ b/chromeos/components/tether/message_transfer_operation.cc
@@ -0,0 +1,144 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chromeos/components/tether/message_transfer_operation.h"
+
+#include "chromeos/components/tether/message_wrapper.h"
+#include "components/proximity_auth/logging/logging.h"
+
+namespace chromeos {
+
+namespace tether {
+
+namespace {
+
+std::set<cryptauth::RemoteDevice> VectorToSet(
+    const std::vector<cryptauth::RemoteDevice>& remote_devices) {
+  std::set<cryptauth::RemoteDevice> set;
+  for (const auto& remote_device : remote_devices) {
+    set.insert(remote_device);
+  }
+  return set;
+}
+
+}  // namespace
+
+// static
+uint32_t MessageTransferOperation::kMaxConnectionAttemptsPerDevice = 3;
+
+MessageTransferOperation::MessageTransferOperation(
+    const std::vector<cryptauth::RemoteDevice>& devices_to_connect,
+    BleConnectionManager* connection_manager)
+    : remote_devices_(VectorToSet(devices_to_connect)),
+      connection_manager_(connection_manager),
+      initialized_(false) {}
+
+MessageTransferOperation::~MessageTransferOperation() {
+  connection_manager_->RemoveObserver(this);
+}
+
+void MessageTransferOperation::Initialize() {
+  if (initialized_) {
+    return;
+  }
+  initialized_ = true;
+
+  connection_manager_->AddObserver(this);
+
+  MessageType message_type_for_connection = GetMessageTypeForConnection();
+  for (const auto& remote_device : remote_devices_) {
+    connection_manager_->RegisterRemoteDevice(remote_device,
+                                              message_type_for_connection);
+
+    cryptauth::SecureChannel::Status status;
+    DCHECK(connection_manager_->GetStatusForDevice(remote_device, &status));
+    if (status == cryptauth::SecureChannel::Status::AUTHENTICATED) {
+      OnDeviceAuthenticated(remote_device);
+    }
+  }
+}
+
+void MessageTransferOperation::OnSecureChannelStatusChanged(
+    const cryptauth::RemoteDevice& remote_device,
+    const cryptauth::SecureChannel::Status& old_status,
+    const cryptauth::SecureChannel::Status& new_status) {
+  if (std::find(remote_devices_.begin(), remote_devices_.end(),
+                remote_device) == remote_devices_.end()) {
+    // If the device whose status has changed does not correspond to any of the
+    // devices passed to this MessageTransferOperation instance, ignore the
+    // status change.
+    return;
+  }
+
+  if (new_status == cryptauth::SecureChannel::Status::AUTHENTICATED) {
+    OnDeviceAuthenticated(remote_device);
+  } else if (old_status == cryptauth::SecureChannel::Status::AUTHENTICATING) {
+    // If authentication fails, account details (e.g., BeaconSeeds) are not
+    // synced, and there is no way to continue. Unregister the device and give
+    // up.
+    UnregisterDevice(remote_device);
+  } else if (new_status == cryptauth::SecureChannel::Status::DISCONNECTED) {
+    uint32_t num_attempts_so_far;
+    if (remote_device_to_num_attempts_map_.find(remote_device) ==
+        remote_device_to_num_attempts_map_.end()) {
+      num_attempts_so_far = 0;
+    } else {
+      num_attempts_so_far = remote_device_to_num_attempts_map_[remote_device];
+    }
+
+    num_attempts_so_far++;
+    remote_device_to_num_attempts_map_[remote_device] = num_attempts_so_far;
+
+    PA_LOG(INFO) << "Connection attempt failed for device with ID "
+                 << remote_device.GetTruncatedDeviceIdForLogs() << ". "
+                 << "Number of failures so far: " << num_attempts_so_far;
+
+    if (num_attempts_so_far >= kMaxConnectionAttemptsPerDevice) {
+      PA_LOG(INFO) << "Connection retry limit reached for device with ID "
+                   << remote_device.GetTruncatedDeviceIdForLogs() << ". "
+                   << "Unregistering device.";
+
+      // If the number of failures so far is equal to the maximum allowed number
+      // of connection attempts, give up and unregister the device.
+      UnregisterDevice(remote_device);
+    }
+  }
+}
+
+void MessageTransferOperation::OnMessageReceived(
+    const cryptauth::RemoteDevice& remote_device,
+    const std::string& payload) {
+  if (std::find(remote_devices_.begin(), remote_devices_.end(),
+                remote_device) == remote_devices_.end()) {
+    // If the device from which the message has been received does not
+    // correspond to any of the devices passed to this MessageTransferOperation
+    // instance, ignore the message.
+    return;
+  }
+
+  std::unique_ptr<MessageWrapper> message_wrapper =
+      MessageWrapper::FromRawMessage(payload);
+  if (message_wrapper) {
+    OnMessageReceived(std::move(message_wrapper), remote_device);
+  }
+}
+
+void MessageTransferOperation::UnregisterDevice(
+    const cryptauth::RemoteDevice& remote_device) {
+  remote_device_to_num_attempts_map_.erase(remote_device);
+  remote_devices_.erase(remote_device);
+  connection_manager_->UnregisterRemoteDevice(remote_device,
+                                              GetMessageTypeForConnection());
+}
+
+void MessageTransferOperation::SendMessageToDevice(
+    const cryptauth::RemoteDevice& remote_device,
+    std::unique_ptr<MessageWrapper> message_wrapper) {
+  connection_manager_->SendMessage(remote_device,
+                                   message_wrapper->ToRawMessage());
+}
+
+}  // namespace tether
+
+}  // namespace chromeos
diff --git a/chromeos/components/tether/message_transfer_operation.h b/chromeos/components/tether/message_transfer_operation.h
new file mode 100644
index 0000000..02c08f0d
--- /dev/null
+++ b/chromeos/components/tether/message_transfer_operation.h
@@ -0,0 +1,86 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROMEOS_COMPONENTS_TETHER_MESSAGE_TRANSFER_OPERATION_H_
+#define CHROMEOS_COMPONENTS_TETHER_MESSAGE_TRANSFER_OPERATION_H_
+
+#include <map>
+#include <set>
+#include <vector>
+
+#include "base/macros.h"
+#include "chromeos/components/tether/ble_connection_manager.h"
+
+namespace chromeos {
+
+namespace tether {
+
+class MessageWrapper;
+
+// Abstract base class used for operations which send and/or receive messages
+// from remote devices.
+class MessageTransferOperation : public BleConnectionManager::Observer {
+ public:
+  MessageTransferOperation(
+      const std::vector<cryptauth::RemoteDevice>& devices_to_connect,
+      BleConnectionManager* connection_manager);
+  virtual ~MessageTransferOperation();
+
+  // Initializes the operation by registering devices with BleConnectionManager.
+  void Initialize();
+
+  // BleConnectionManager::Observer:
+  void OnSecureChannelStatusChanged(
+      const cryptauth::RemoteDevice& remote_device,
+      const cryptauth::SecureChannel::Status& old_status,
+      const cryptauth::SecureChannel::Status& new_status) override;
+  void OnMessageReceived(const cryptauth::RemoteDevice& remote_device,
+                         const std::string& payload) override;
+
+ protected:
+  // Unregisters |remote_device| for the MessageType returned by
+  // GetMessageTypeForConnection().
+  void UnregisterDevice(const cryptauth::RemoteDevice& remote_device);
+
+  // Sends |message_wrapper|'s message to |remote_device|.
+  void SendMessageToDevice(const cryptauth::RemoteDevice& remote_device,
+                           std::unique_ptr<MessageWrapper> message_wrapper);
+
+  // Callback executed whena device is authenticated (i.e., it is in a state
+  // which allows messages to be sent/received). Should be overridden by derived
+  // classes which intend to send a message to |remote_device| as soon as an
+  // authenticated channel has been established to that device.
+  virtual void OnDeviceAuthenticated(
+      const cryptauth::RemoteDevice& remote_device) {}
+
+  // Callback executed when a tether protocol message is received. Should be
+  // overriden by derived classes which intend to handle messages received from
+  // |remote_device|.
+  virtual void OnMessageReceived(
+      std::unique_ptr<MessageWrapper> message_wrapper,
+      const cryptauth::RemoteDevice& remote_device) {}
+
+  // Returns the type of message that this operation intends to send.
+  virtual MessageType GetMessageTypeForConnection() = 0;
+
+ private:
+  friend class MessageTransferOperationTest;
+
+  static uint32_t kMaxConnectionAttemptsPerDevice;
+
+  std::set<cryptauth::RemoteDevice> remote_devices_;
+  BleConnectionManager* connection_manager_;
+
+  bool initialized_;
+  std::map<cryptauth::RemoteDevice, uint32_t>
+      remote_device_to_num_attempts_map_;
+
+  DISALLOW_COPY_AND_ASSIGN(MessageTransferOperation);
+};
+
+}  // namespace tether
+
+}  // namespace chromeos
+
+#endif  // CHROMEOS_COMPONENTS_TETHER_MESSAGE_TRANSFER_OPERATION_H_
diff --git a/chromeos/components/tether/message_transfer_operation_unittest.cc b/chromeos/components/tether/message_transfer_operation_unittest.cc
new file mode 100644
index 0000000..efba646
--- /dev/null
+++ b/chromeos/components/tether/message_transfer_operation_unittest.cc
@@ -0,0 +1,421 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chromeos/components/tether/message_transfer_operation.h"
+
+#include "chromeos/components/tether/fake_ble_connection_manager.h"
+#include "chromeos/components/tether/message_wrapper.h"
+#include "components/cryptauth/remote_device_test_util.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace chromeos {
+
+namespace tether {
+
+namespace {
+
+// Arbitrarily chosen value. The MessageType used in this test does not matter
+// except that it must be consistent throughout the test.
+const MessageType kTestMessageType = MessageType::TETHER_AVAILABILITY_REQUEST;
+
+// A test double for MessageTransferOperation is needed because
+// MessageTransferOperation has pure virtual methods which must be overridden in
+// order to create a concrete instantiation of the class.
+class TestOperation : public MessageTransferOperation {
+ public:
+  TestOperation(const std::vector<cryptauth::RemoteDevice>& devices_to_connect,
+                BleConnectionManager* connection_manager)
+      : MessageTransferOperation(devices_to_connect, connection_manager) {}
+  ~TestOperation() override {}
+
+  bool HasDeviceAuthenticated(const cryptauth::RemoteDevice& remote_device) {
+    const auto iter = device_map_.find(remote_device);
+    if (iter == device_map_.end()) {
+      return false;
+    }
+
+    return iter->second.has_device_authenticated;
+  }
+
+  std::vector<std::shared_ptr<MessageWrapper>> GetReceivedMessages(
+      const cryptauth::RemoteDevice& remote_device) {
+    const auto iter = device_map_.find(remote_device);
+    if (iter == device_map_.end()) {
+      return std::vector<std::shared_ptr<MessageWrapper>>();
+    }
+
+    return iter->second.received_messages;
+  }
+
+  // MessageTransferOperation:
+  void OnDeviceAuthenticated(
+      const cryptauth::RemoteDevice& remote_device) override {
+    device_map_[remote_device].has_device_authenticated = true;
+  }
+
+  void OnMessageReceived(
+      std::unique_ptr<MessageWrapper> message_wrapper,
+      const cryptauth::RemoteDevice& remote_device) override {
+    device_map_[remote_device].received_messages.push_back(
+        std::move(message_wrapper));
+  }
+
+  MessageType GetMessageTypeForConnection() override {
+    return kTestMessageType;
+  }
+
+ private:
+  struct DeviceMapValue {
+    DeviceMapValue() {}
+    ~DeviceMapValue() {}
+
+    bool has_device_authenticated;
+    std::vector<std::shared_ptr<MessageWrapper>> received_messages;
+  };
+
+  std::map<cryptauth::RemoteDevice, DeviceMapValue> device_map_;
+};
+
+DeviceStatus CreateFakeDeviceStatus() {
+  WifiStatus wifi_status;
+  wifi_status.set_status_code(
+      WifiStatus_StatusCode::WifiStatus_StatusCode_CONNECTED);
+  wifi_status.set_ssid("Google A");
+
+  DeviceStatus device_status;
+  device_status.set_battery_percentage(75);
+  device_status.set_cell_provider("Google Fi");
+  device_status.set_connection_strength(4);
+  device_status.mutable_wifi_status()->CopyFrom(wifi_status);
+
+  return device_status;
+}
+
+TetherAvailabilityResponse CreateTetherAvailabilityResponse() {
+  TetherAvailabilityResponse response;
+  response.set_response_code(
+      TetherAvailabilityResponse_ResponseCode::
+          TetherAvailabilityResponse_ResponseCode_TETHER_AVAILABLE);
+  response.mutable_device_status()->CopyFrom(CreateFakeDeviceStatus());
+  return response;
+}
+
+}  // namespace
+
+class MessageTransferOperationTest : public testing::Test {
+ protected:
+  MessageTransferOperationTest()
+      : test_devices_(cryptauth::GenerateTestRemoteDevices(4)) {
+    // These tests are written under the assumption that there are a maximum of
+    // 3 connection attempts; they need to be edited if this value changes.
+    EXPECT_EQ(3u, MessageTransferOperation::kMaxConnectionAttemptsPerDevice);
+  }
+
+  void SetUp() override {
+    fake_ble_connection_manager_ = base::MakeUnique<FakeBleConnectionManager>();
+  }
+
+  void ConstructOperation(std::vector<cryptauth::RemoteDevice> remote_devices) {
+    operation_ = base::WrapUnique(
+        new TestOperation(remote_devices, fake_ble_connection_manager_.get()));
+  }
+
+  bool IsDeviceRegistered(const cryptauth::RemoteDevice& remote_device) const {
+    DCHECK(operation_);
+    return std::find(operation_->remote_devices_.begin(),
+                     operation_->remote_devices_.end(),
+                     remote_device) != operation_->remote_devices_.end();
+  }
+
+  const std::vector<cryptauth::RemoteDevice> test_devices_;
+
+  std::unique_ptr<FakeBleConnectionManager> fake_ble_connection_manager_;
+  std::unique_ptr<TestOperation> operation_;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(MessageTransferOperationTest);
+};
+
+TEST_F(MessageTransferOperationTest, TestCannotConnectAndReachesRetryLimit) {
+  ConstructOperation(std::vector<cryptauth::RemoteDevice>{test_devices_[0]});
+  operation_->Initialize();
+  EXPECT_TRUE(IsDeviceRegistered(test_devices_[0]));
+
+  // Try to connect and fail. The device should still be registered.
+  fake_ble_connection_manager_->SetDeviceStatus(
+      test_devices_[0], cryptauth::SecureChannel::Status::CONNECTING);
+  fake_ble_connection_manager_->SetDeviceStatus(
+      test_devices_[0], cryptauth::SecureChannel::Status::DISCONNECTED);
+  EXPECT_TRUE(IsDeviceRegistered(test_devices_[0]));
+
+  // Try and fail again. The device should still be registered.
+  fake_ble_connection_manager_->SetDeviceStatus(
+      test_devices_[0], cryptauth::SecureChannel::Status::CONNECTING);
+  fake_ble_connection_manager_->SetDeviceStatus(
+      test_devices_[0], cryptauth::SecureChannel::Status::DISCONNECTED);
+  EXPECT_TRUE(IsDeviceRegistered(test_devices_[0]));
+
+  // Try and fail a third time. The maximum number of failures has been reached,
+  // so the device should be unregistered.
+  fake_ble_connection_manager_->SetDeviceStatus(
+      test_devices_[0], cryptauth::SecureChannel::Status::CONNECTING);
+  fake_ble_connection_manager_->SetDeviceStatus(
+      test_devices_[0], cryptauth::SecureChannel::Status::DISCONNECTED);
+  EXPECT_FALSE(IsDeviceRegistered(test_devices_[0]));
+
+  EXPECT_FALSE(operation_->HasDeviceAuthenticated(test_devices_[0]));
+  EXPECT_TRUE(operation_->GetReceivedMessages(test_devices_[0]).empty());
+}
+
+TEST_F(MessageTransferOperationTest, TestFailsAuthentication) {
+  ConstructOperation(std::vector<cryptauth::RemoteDevice>{test_devices_[0]});
+  operation_->Initialize();
+  EXPECT_TRUE(IsDeviceRegistered(test_devices_[0]));
+
+  fake_ble_connection_manager_->SetDeviceStatus(
+      test_devices_[0], cryptauth::SecureChannel::Status::CONNECTING);
+  fake_ble_connection_manager_->SetDeviceStatus(
+      test_devices_[0], cryptauth::SecureChannel::Status::CONNECTED);
+  fake_ble_connection_manager_->SetDeviceStatus(
+      test_devices_[0], cryptauth::SecureChannel::Status::AUTHENTICATING);
+  fake_ble_connection_manager_->SetDeviceStatus(
+      test_devices_[0], cryptauth::SecureChannel::Status::DISCONNECTED);
+
+  // When authentication fails, we consider this a fatal error; the device
+  // should be unregistered.
+  EXPECT_FALSE(IsDeviceRegistered(test_devices_[0]));
+
+  EXPECT_FALSE(operation_->HasDeviceAuthenticated(test_devices_[0]));
+  EXPECT_TRUE(operation_->GetReceivedMessages(test_devices_[0]).empty());
+}
+
+TEST_F(MessageTransferOperationTest, TestFailsThenConnects) {
+  ConstructOperation(std::vector<cryptauth::RemoteDevice>{test_devices_[0]});
+  operation_->Initialize();
+  EXPECT_TRUE(IsDeviceRegistered(test_devices_[0]));
+
+  // Try to connect and fail. The device should still be registered.
+  fake_ble_connection_manager_->SetDeviceStatus(
+      test_devices_[0], cryptauth::SecureChannel::Status::CONNECTING);
+  fake_ble_connection_manager_->SetDeviceStatus(
+      test_devices_[0], cryptauth::SecureChannel::Status::DISCONNECTED);
+  EXPECT_TRUE(IsDeviceRegistered(test_devices_[0]));
+
+  // Try again and succeed.
+  fake_ble_connection_manager_->SetDeviceStatus(
+      test_devices_[0], cryptauth::SecureChannel::Status::CONNECTING);
+  fake_ble_connection_manager_->SetDeviceStatus(
+      test_devices_[0], cryptauth::SecureChannel::Status::CONNECTED);
+  fake_ble_connection_manager_->SetDeviceStatus(
+      test_devices_[0], cryptauth::SecureChannel::Status::AUTHENTICATING);
+  fake_ble_connection_manager_->SetDeviceStatus(
+      test_devices_[0], cryptauth::SecureChannel::Status::AUTHENTICATED);
+  EXPECT_TRUE(IsDeviceRegistered(test_devices_[0]));
+  EXPECT_TRUE(operation_->HasDeviceAuthenticated(test_devices_[0]));
+
+  EXPECT_TRUE(operation_->GetReceivedMessages(test_devices_[0]).empty());
+}
+
+TEST_F(MessageTransferOperationTest,
+       TestSuccessfulConnectionAndReceiveMessage) {
+  ConstructOperation(std::vector<cryptauth::RemoteDevice>{test_devices_[0]});
+  operation_->Initialize();
+  EXPECT_TRUE(IsDeviceRegistered(test_devices_[0]));
+
+  fake_ble_connection_manager_->SetDeviceStatus(
+      test_devices_[0], cryptauth::SecureChannel::Status::CONNECTING);
+  fake_ble_connection_manager_->SetDeviceStatus(
+      test_devices_[0], cryptauth::SecureChannel::Status::CONNECTED);
+  fake_ble_connection_manager_->SetDeviceStatus(
+      test_devices_[0], cryptauth::SecureChannel::Status::AUTHENTICATING);
+  fake_ble_connection_manager_->SetDeviceStatus(
+      test_devices_[0], cryptauth::SecureChannel::Status::AUTHENTICATED);
+  EXPECT_TRUE(IsDeviceRegistered(test_devices_[0]));
+  EXPECT_TRUE(operation_->HasDeviceAuthenticated(test_devices_[0]));
+
+  fake_ble_connection_manager_->ReceiveMessage(
+      test_devices_[0],
+      MessageWrapper(CreateTetherAvailabilityResponse()).ToRawMessage());
+
+  EXPECT_EQ(1u, operation_->GetReceivedMessages(test_devices_[0]).size());
+  std::shared_ptr<MessageWrapper> message =
+      operation_->GetReceivedMessages(test_devices_[0])[0];
+  EXPECT_EQ(MessageType::TETHER_AVAILABILITY_RESPONSE,
+            message->GetMessageType());
+  EXPECT_EQ(CreateTetherAvailabilityResponse().SerializeAsString(),
+            message->GetProto()->SerializeAsString());
+}
+
+TEST_F(MessageTransferOperationTest, TestRepeatedInputDevice) {
+  // Construct with two copies of the same device.
+  ConstructOperation(
+      std::vector<cryptauth::RemoteDevice>{test_devices_[0], test_devices_[0]});
+  operation_->Initialize();
+  EXPECT_TRUE(IsDeviceRegistered(test_devices_[0]));
+
+  fake_ble_connection_manager_->SetDeviceStatus(
+      test_devices_[0], cryptauth::SecureChannel::Status::CONNECTING);
+  fake_ble_connection_manager_->SetDeviceStatus(
+      test_devices_[0], cryptauth::SecureChannel::Status::CONNECTED);
+  fake_ble_connection_manager_->SetDeviceStatus(
+      test_devices_[0], cryptauth::SecureChannel::Status::AUTHENTICATING);
+  fake_ble_connection_manager_->SetDeviceStatus(
+      test_devices_[0], cryptauth::SecureChannel::Status::AUTHENTICATED);
+  EXPECT_TRUE(IsDeviceRegistered(test_devices_[0]));
+  EXPECT_TRUE(operation_->HasDeviceAuthenticated(test_devices_[0]));
+
+  fake_ble_connection_manager_->ReceiveMessage(
+      test_devices_[0],
+      MessageWrapper(CreateTetherAvailabilityResponse()).ToRawMessage());
+
+  // Should still have received only one message even though the device was
+  // repeated twice in the constructor.
+  EXPECT_EQ(1u, operation_->GetReceivedMessages(test_devices_[0]).size());
+  std::shared_ptr<MessageWrapper> message =
+      operation_->GetReceivedMessages(test_devices_[0])[0];
+  EXPECT_EQ(MessageType::TETHER_AVAILABILITY_RESPONSE,
+            message->GetMessageType());
+  EXPECT_EQ(CreateTetherAvailabilityResponse().SerializeAsString(),
+            message->GetProto()->SerializeAsString());
+}
+
+TEST_F(MessageTransferOperationTest, TestReceiveEventForOtherDevice) {
+  ConstructOperation(std::vector<cryptauth::RemoteDevice>{test_devices_[0]});
+  operation_->Initialize();
+  EXPECT_TRUE(IsDeviceRegistered(test_devices_[0]));
+
+  // Simulate the authentication of |test_devices_[1]|'s channel. Since the
+  // operation was only constructed with |test_devices_[0]|, this operation
+  // should not be affected.
+  fake_ble_connection_manager_->RegisterRemoteDevice(
+      test_devices_[1], MessageType::CONNECT_TETHERING_REQUEST);
+  fake_ble_connection_manager_->SetDeviceStatus(
+      test_devices_[1], cryptauth::SecureChannel::Status::CONNECTING);
+  fake_ble_connection_manager_->SetDeviceStatus(
+      test_devices_[1], cryptauth::SecureChannel::Status::CONNECTED);
+  fake_ble_connection_manager_->SetDeviceStatus(
+      test_devices_[1], cryptauth::SecureChannel::Status::AUTHENTICATING);
+  fake_ble_connection_manager_->SetDeviceStatus(
+      test_devices_[1], cryptauth::SecureChannel::Status::AUTHENTICATED);
+  EXPECT_TRUE(IsDeviceRegistered(test_devices_[0]));
+  EXPECT_FALSE(IsDeviceRegistered(test_devices_[1]));
+  EXPECT_FALSE(operation_->HasDeviceAuthenticated(test_devices_[0]));
+  EXPECT_FALSE(operation_->HasDeviceAuthenticated(test_devices_[1]));
+
+  // Now, receive a message for |test_devices[1]|. Likewise, this operation
+  // should not be affected.
+  fake_ble_connection_manager_->ReceiveMessage(
+      test_devices_[1],
+      MessageWrapper(CreateTetherAvailabilityResponse()).ToRawMessage());
+
+  EXPECT_FALSE(operation_->GetReceivedMessages(test_devices_[0]).size());
+}
+
+TEST_F(MessageTransferOperationTest,
+       TestAlreadyAuthenticatedBeforeInitialization) {
+  ConstructOperation(std::vector<cryptauth::RemoteDevice>{test_devices_[0]});
+
+  // Simulate the authentication of |test_devices_[0]|'s channel before
+  // initialization.
+  fake_ble_connection_manager_->RegisterRemoteDevice(
+      test_devices_[0], MessageType::CONNECT_TETHERING_REQUEST);
+  fake_ble_connection_manager_->SetDeviceStatus(
+      test_devices_[0], cryptauth::SecureChannel::Status::CONNECTING);
+  fake_ble_connection_manager_->SetDeviceStatus(
+      test_devices_[0], cryptauth::SecureChannel::Status::CONNECTED);
+  fake_ble_connection_manager_->SetDeviceStatus(
+      test_devices_[0], cryptauth::SecureChannel::Status::AUTHENTICATING);
+  fake_ble_connection_manager_->SetDeviceStatus(
+      test_devices_[0], cryptauth::SecureChannel::Status::AUTHENTICATED);
+
+  // Now initialize; the authentication handler should have been invoked.
+  operation_->Initialize();
+  EXPECT_TRUE(IsDeviceRegistered(test_devices_[0]));
+  EXPECT_TRUE(operation_->HasDeviceAuthenticated(test_devices_[0]));
+
+  // Receiving a message should work at this point.
+  fake_ble_connection_manager_->ReceiveMessage(
+      test_devices_[0],
+      MessageWrapper(CreateTetherAvailabilityResponse()).ToRawMessage());
+
+  EXPECT_EQ(1u, operation_->GetReceivedMessages(test_devices_[0]).size());
+  std::shared_ptr<MessageWrapper> message =
+      operation_->GetReceivedMessages(test_devices_[0])[0];
+  EXPECT_EQ(MessageType::TETHER_AVAILABILITY_RESPONSE,
+            message->GetMessageType());
+  EXPECT_EQ(CreateTetherAvailabilityResponse().SerializeAsString(),
+            message->GetProto()->SerializeAsString());
+}
+
+TEST_F(MessageTransferOperationTest, MultipleDevices) {
+  ConstructOperation(test_devices_);
+  operation_->Initialize();
+  EXPECT_TRUE(IsDeviceRegistered(test_devices_[0]));
+  EXPECT_TRUE(IsDeviceRegistered(test_devices_[1]));
+  EXPECT_TRUE(IsDeviceRegistered(test_devices_[2]));
+  EXPECT_TRUE(IsDeviceRegistered(test_devices_[3]));
+
+  // Authenticate |test_devices_[0]|'s channel.
+  fake_ble_connection_manager_->RegisterRemoteDevice(
+      test_devices_[0], MessageType::CONNECT_TETHERING_REQUEST);
+  fake_ble_connection_manager_->SetDeviceStatus(
+      test_devices_[0], cryptauth::SecureChannel::Status::CONNECTING);
+  fake_ble_connection_manager_->SetDeviceStatus(
+      test_devices_[0], cryptauth::SecureChannel::Status::CONNECTED);
+  fake_ble_connection_manager_->SetDeviceStatus(
+      test_devices_[0], cryptauth::SecureChannel::Status::AUTHENTICATING);
+  fake_ble_connection_manager_->SetDeviceStatus(
+      test_devices_[0], cryptauth::SecureChannel::Status::AUTHENTICATED);
+  EXPECT_TRUE(operation_->HasDeviceAuthenticated(test_devices_[0]));
+  EXPECT_TRUE(IsDeviceRegistered(test_devices_[0]));
+
+  // Fail 3 times to connect to |test_devices_[1]|.
+  fake_ble_connection_manager_->SetDeviceStatus(
+      test_devices_[1], cryptauth::SecureChannel::Status::CONNECTING);
+  fake_ble_connection_manager_->SetDeviceStatus(
+      test_devices_[1], cryptauth::SecureChannel::Status::DISCONNECTED);
+  fake_ble_connection_manager_->SetDeviceStatus(
+      test_devices_[1], cryptauth::SecureChannel::Status::CONNECTING);
+  fake_ble_connection_manager_->SetDeviceStatus(
+      test_devices_[1], cryptauth::SecureChannel::Status::DISCONNECTED);
+  fake_ble_connection_manager_->SetDeviceStatus(
+      test_devices_[1], cryptauth::SecureChannel::Status::CONNECTING);
+  fake_ble_connection_manager_->SetDeviceStatus(
+      test_devices_[1], cryptauth::SecureChannel::Status::DISCONNECTED);
+  EXPECT_FALSE(operation_->HasDeviceAuthenticated(test_devices_[1]));
+  EXPECT_FALSE(IsDeviceRegistered(test_devices_[1]));
+
+  // Authenticate |test_devices_[2]|'s channel.
+  fake_ble_connection_manager_->RegisterRemoteDevice(
+      test_devices_[2], MessageType::CONNECT_TETHERING_REQUEST);
+  fake_ble_connection_manager_->SetDeviceStatus(
+      test_devices_[2], cryptauth::SecureChannel::Status::CONNECTING);
+  fake_ble_connection_manager_->SetDeviceStatus(
+      test_devices_[2], cryptauth::SecureChannel::Status::CONNECTED);
+  fake_ble_connection_manager_->SetDeviceStatus(
+      test_devices_[2], cryptauth::SecureChannel::Status::AUTHENTICATING);
+  fake_ble_connection_manager_->SetDeviceStatus(
+      test_devices_[2], cryptauth::SecureChannel::Status::AUTHENTICATED);
+  EXPECT_TRUE(operation_->HasDeviceAuthenticated(test_devices_[2]));
+  EXPECT_TRUE(IsDeviceRegistered(test_devices_[2]));
+
+  // Fail to authenticate |test_devices_[3]|'s channel.
+  fake_ble_connection_manager_->RegisterRemoteDevice(
+      test_devices_[3], MessageType::CONNECT_TETHERING_REQUEST);
+  fake_ble_connection_manager_->SetDeviceStatus(
+      test_devices_[3], cryptauth::SecureChannel::Status::CONNECTING);
+  fake_ble_connection_manager_->SetDeviceStatus(
+      test_devices_[3], cryptauth::SecureChannel::Status::CONNECTED);
+  fake_ble_connection_manager_->SetDeviceStatus(
+      test_devices_[3], cryptauth::SecureChannel::Status::AUTHENTICATING);
+  fake_ble_connection_manager_->SetDeviceStatus(
+      test_devices_[3], cryptauth::SecureChannel::Status::DISCONNECTED);
+  EXPECT_FALSE(operation_->HasDeviceAuthenticated(test_devices_[3]));
+  EXPECT_FALSE(IsDeviceRegistered(test_devices_[3]));
+}
+
+}  // namespace tether
+
+}  // namespace chromeos
diff --git a/components/printing/renderer/print_web_view_helper.cc b/components/printing/renderer/print_web_view_helper.cc
index d54fff87..173fb4381 100644
--- a/components/printing/renderer/print_web_view_helper.cc
+++ b/components/printing/renderer/print_web_view_helper.cc
@@ -1260,11 +1260,22 @@
     if (source_frame->getPrintPresetOptionsForPlugin(source_node,
                                                      &preset_options)) {
       if (preset_options.isPageSizeUniform) {
+        // Figure out if the sizes have the same orientation
+        bool is_printable_area_landscape = printable_area_in_points.width() >
+                                           printable_area_in_points.height();
+        bool is_preset_landscape = preset_options.uniformPageSize.width >
+                                   preset_options.uniformPageSize.height;
+        bool rotate = is_printable_area_landscape != is_preset_landscape;
+        // Match orientation for computing scaling
+        double printable_width = rotate ? printable_area_in_points.height()
+                                        : printable_area_in_points.width();
+        double printable_height = rotate ? printable_area_in_points.width()
+                                         : printable_area_in_points.height();
         double scale_width =
-            static_cast<double>(printable_area_in_points.width()) /
+            printable_width /
             static_cast<double>(preset_options.uniformPageSize.width);
         double scale_height =
-            static_cast<double>(printable_area_in_points.height()) /
+            printable_height /
             static_cast<double>(preset_options.uniformPageSize.height);
         fit_to_page_scale_factor = std::min(scale_width, scale_height);
       } else {
diff --git a/components/sync/engine_impl/commit_util.cc b/components/sync/engine_impl/commit_util.cc
index c8ddfc96..ccb6200 100644
--- a/components/sync/engine_impl/commit_util.cc
+++ b/components/sync/engine_impl/commit_util.cc
@@ -8,7 +8,6 @@
 #include <string>
 #include <vector>
 
-#include "base/debug/dump_without_crashing.h"
 #include "base/strings/string_util.h"
 #include "components/sync/base/attachment_id_proto.h"
 #include "components/sync/base/time.h"
@@ -177,10 +176,6 @@
           meta_entry.GetUniquePosition().ToInt64());
       meta_entry.GetUniquePosition().ToProto(
           sync_entry->mutable_unique_position());
-      if (!meta_entry.GetUniquePosition().IsValid()) {
-        // Should never upload invalid unique position for bookmark to server.
-        base::debug::DumpWithoutCrashing();
-      }
     }
     // Always send specifics for bookmarks.
     SetEntrySpecifics(meta_entry, sync_entry);
diff --git a/content/browser/blob_storage/blob_storage_browsertest.cc b/content/browser/blob_storage/blob_storage_browsertest.cc
index 2073443b..b51989b 100644
--- a/content/browser/blob_storage/blob_storage_browsertest.cc
+++ b/content/browser/blob_storage/blob_storage_browsertest.cc
@@ -49,6 +49,8 @@
     content::ChromeBlobStorageContext* blob_context =
         ChromeBlobStorageContext::GetFor(
             shell()->web_contents()->GetBrowserContext());
+    if (!blob_context->context())
+      return nullptr;
     return blob_context->context()->mutable_memory_controller();
   }
 
@@ -56,6 +58,7 @@
     // The test page will perform tests on blob storage, then navigate to either
     // a #pass or #fail ref.
     Shell* the_browser = incognito ? CreateOffTheRecordBrowser() : shell();
+    ASSERT_TRUE(the_browser);
 
     VLOG(0) << "Navigating to URL and blocking. " << test_url.spec();
     NavigateToURLBlockUntilNavigationsComplete(the_browser, test_url, 2);
@@ -82,6 +85,7 @@
   SetBlobLimits();
   SimpleTest(GetTestUrl("blob_storage", "blob_creation_and_slicing.html"));
   storage::BlobMemoryController* memory_controller = GetMemoryController();
+  ASSERT_TRUE(memory_controller);
   // Our exact usages depend on IPC message ordering & garbage collection.
   // Since this is basically random, we just check bounds.
   EXPECT_LT(0u, memory_controller->memory_usage());
diff --git a/content/browser/media/session/media_session_impl.cc b/content/browser/media/session/media_session_impl.cc
index 778ce3a0..dd2e1db 100644
--- a/content/browser/media/session/media_session_impl.cc
+++ b/content/browser/media/session/media_session_impl.cc
@@ -110,6 +110,11 @@
   AbandonSystemAudioFocusIfNeeded();
 }
 
+void MediaSessionImpl::RenderFrameDeleted(RenderFrameHost* rfh) {
+  if (services_.count(rfh))
+    OnServiceDestroyed(services_[rfh]);
+}
+
 void MediaSessionImpl::AddObserver(MediaSessionObserver* observer) {
   observers_.AddObserver(observer);
 }
@@ -574,7 +579,11 @@
 // MediaSessionService-related methods
 
 void MediaSessionImpl::OnServiceCreated(MediaSessionServiceImpl* service) {
-  services_[service->GetRenderFrameHost()] = service;
+  RenderFrameHost* rfh = service->GetRenderFrameHost();
+  if (!rfh)
+    return;
+
+  services_[rfh] = service;
   UpdateRoutedService();
 }
 
diff --git a/content/browser/media/session/media_session_impl.h b/content/browser/media/session/media_session_impl.h
index 6ed0ce4..351486eb 100644
--- a/content/browser/media/session/media_session_impl.h
+++ b/content/browser/media/session/media_session_impl.h
@@ -166,6 +166,7 @@
 
   // WebContentsObserver implementation
   void WebContentsDestroyed() override;
+  void RenderFrameDeleted(RenderFrameHost* rfh) override;
 
   // MediaSessionService-related methods
 
diff --git a/content/browser/media/session/media_session_service_impl.cc b/content/browser/media/session/media_session_service_impl.cc
index 1d93af3..cf452e22 100644
--- a/content/browser/media/session/media_session_service_impl.cc
+++ b/content/browser/media/session/media_session_service_impl.cc
@@ -8,13 +8,15 @@
 #include "content/browser/media/session/media_session_impl.h"
 #include "content/browser/web_contents/web_contents_impl.h"
 #include "content/public/browser/browser_thread.h"
+#include "content/public/browser/render_frame_host.h"
 #include "content/public/browser/web_contents.h"
 
 namespace content {
 
 MediaSessionServiceImpl::MediaSessionServiceImpl(
     RenderFrameHost* render_frame_host)
-    : render_frame_host_(render_frame_host),
+    : render_frame_process_id_(render_frame_host->GetProcess()->GetID()),
+      render_frame_routing_id_(render_frame_host->GetRoutingID()),
       playback_state_(blink::mojom::MediaSessionPlaybackState::NONE) {
   MediaSessionImpl* session = GetMediaSession();
   if (session)
@@ -36,6 +38,11 @@
   impl->Bind(std::move(request));
 }
 
+RenderFrameHost* MediaSessionServiceImpl::GetRenderFrameHost() {
+  return RenderFrameHost::FromID(render_frame_process_id_,
+                                 render_frame_routing_id_);
+}
+
 void MediaSessionServiceImpl::SetClient(
     blink::mojom::MediaSessionClientPtr client) {
   client_ = std::move(client);
@@ -55,8 +62,11 @@
   // coming from a known and secure source. It must be processed accordingly.
   if (metadata.has_value() &&
       !MediaMetadataSanitizer::CheckSanity(metadata.value())) {
-    render_frame_host_->GetProcess()->ShutdownForBadMessage(
-        RenderProcessHost::CrashReportMode::GENERATE_CRASH_DUMP);
+    RenderFrameHost* rfh = GetRenderFrameHost();
+    if (rfh) {
+      rfh->GetProcess()->ShutdownForBadMessage(
+          RenderProcessHost::CrashReportMode::GENERATE_CRASH_DUMP);
+    }
     return;
   }
   metadata_ = metadata;
@@ -83,10 +93,15 @@
 }
 
 MediaSessionImpl* MediaSessionServiceImpl::GetMediaSession() {
-  WebContentsImpl* contents = static_cast<WebContentsImpl*>(
-      WebContentsImpl::FromRenderFrameHost(render_frame_host_));
+  RenderFrameHost* rfh = GetRenderFrameHost();
+  if (!rfh)
+    return nullptr;
+
+  WebContentsImpl* contents =
+      static_cast<WebContentsImpl*>(WebContentsImpl::FromRenderFrameHost(rfh));
   if (!contents)
     return nullptr;
+
   return MediaSessionImpl::Get(contents);
 }
 
diff --git a/content/browser/media/session/media_session_service_impl.h b/content/browser/media/session/media_session_service_impl.h
index 5f13486..03113849 100644
--- a/content/browser/media/session/media_session_service_impl.h
+++ b/content/browser/media/session/media_session_service_impl.h
@@ -26,7 +26,7 @@
   static void Create(RenderFrameHost* render_frame_host,
                      blink::mojom::MediaSessionServiceRequest request);
   const blink::mojom::MediaSessionClientPtr& GetClient() { return client_; }
-  RenderFrameHost* GetRenderFrameHost() { return render_frame_host_; }
+  RenderFrameHost* GetRenderFrameHost();
 
   blink::mojom::MediaSessionPlaybackState playback_state() const {
     return playback_state_;
@@ -53,7 +53,8 @@
 
   void Bind(blink::mojom::MediaSessionServiceRequest request);
 
-  RenderFrameHost* render_frame_host_;
+  const int render_frame_process_id_;
+  const int render_frame_routing_id_;
 
   // RAII binding of |this| to an MediaSessionService interface request.
   // The binding is removed when binding_ is cleared or goes out of scope.
diff --git a/content/browser/renderer_host/delegated_frame_host.cc b/content/browser/renderer_host/delegated_frame_host.cc
index 6e291141..450a427 100644
--- a/content/browser/renderer_host/delegated_frame_host.cc
+++ b/content/browser/renderer_host/delegated_frame_host.cc
@@ -443,7 +443,6 @@
     surface_returned_resources_.clear();
     last_compositor_frame_sink_id_ = compositor_frame_sink_id;
   }
-  bool skip_frame = false;
   pending_delegated_ack_count_++;
 
   background_color_ = frame.metadata.root_background_color;
@@ -462,25 +461,13 @@
       allocated_new_local_surface_id = true;
     }
 
-    gfx::Size desired_size = client_->DelegatedFrameHostDesiredSizeInDIP();
-    if (desired_size != frame_size_in_dip && !desired_size.IsEmpty()) {
-      skip_frame = true;
-      skipped_latency_info_list_.insert(skipped_latency_info_list_.end(),
-                                        frame.metadata.latency_info.begin(),
-                                        frame.metadata.latency_info.end());
-      frame.metadata.latency_info.clear();
-    } else {
-      frame.metadata.latency_info.insert(frame.metadata.latency_info.end(),
-                                         skipped_latency_info_list_.begin(),
-                                         skipped_latency_info_list_.end());
-      skipped_latency_info_list_.clear();
-    }
+    frame.metadata.latency_info.insert(frame.metadata.latency_info.end(),
+                                       skipped_latency_info_list_.begin(),
+                                       skipped_latency_info_list_.end());
+    skipped_latency_info_list_.clear();
 
-    cc::SurfaceFactory::DrawCallback ack_callback;
-    if (!skip_frame) {
-      ack_callback = base::Bind(&DelegatedFrameHost::SurfaceDrawn, AsWeakPtr(),
-                                compositor_frame_sink_id);
-    }
+    auto ack_callback = base::Bind(&DelegatedFrameHost::SurfaceDrawn,
+                                   AsWeakPtr(), compositor_frame_sink_id);
     surface_factory_->SubmitCompositorFrame(local_surface_id_, std::move(frame),
                                             ack_callback);
     if (allocated_new_local_surface_id) {
@@ -505,12 +492,7 @@
         damage_rect_in_dip);
   }
 
-  if (skip_frame) {
-    SendReclaimCompositorResources(compositor_frame_sink_id,
-                                   true /* is_swap_ack */);
-  }
-
-  if (compositor_ && !skip_frame)
+  if (compositor_)
     can_lock_compositor_ = NO_PENDING_COMMIT;
 
   if (local_surface_id_.is_valid()) {
diff --git a/content/renderer/BUILD.gn b/content/renderer/BUILD.gn
index 70b0c78..c483806 100644
--- a/content/renderer/BUILD.gn
+++ b/content/renderer/BUILD.gn
@@ -475,6 +475,7 @@
     "//third_party/icu",
     "//third_party/libjingle",
     "//third_party/libyuv",
+    "//third_party/webrtc/api/audio_codecs:builtin_audio_decoder_factory",
     "//third_party/widevine/cdm:headers",
     "//ui/accessibility",
     "//ui/base",
diff --git a/content/renderer/media/renderer_webaudiodevice_impl.cc b/content/renderer/media/renderer_webaudiodevice_impl.cc
index 6bb147a56..2c4f81b 100644
--- a/content/renderer/media/renderer_webaudiodevice_impl.cc
+++ b/content/renderer/media/renderer_webaudiodevice_impl.cc
@@ -76,18 +76,20 @@
 
 RendererWebAudioDeviceImpl* RendererWebAudioDeviceImpl::Create(
     media::ChannelLayout layout,
+    int channels,
     const blink::WebAudioLatencyHint& latency_hint,
     WebAudioDevice::RenderCallback* callback,
     int session_id,
     const url::Origin& security_origin) {
-  return new RendererWebAudioDeviceImpl(layout, latency_hint, callback,
-                                        session_id, security_origin,
+  return new RendererWebAudioDeviceImpl(layout, channels, latency_hint,
+                                        callback, session_id, security_origin,
                                         base::Bind(&GetOutputDeviceParameters),
                                         base::Bind(&FrameIdFromCurrentContext));
 }
 
 RendererWebAudioDeviceImpl::RendererWebAudioDeviceImpl(
     media::ChannelLayout layout,
+    int channels,
     const blink::WebAudioLatencyHint& latency_hint,
     WebAudioDevice::RenderCallback* callback,
     int session_id,
@@ -135,6 +137,9 @@
 
   sink_params_.Reset(media::AudioParameters::AUDIO_PCM_LOW_LATENCY, layout,
                      hardware_params.sample_rate(), 16, output_buffer_size);
+  // Always set channels, this should be a no-op in all but the discrete case;
+  // this call will fail if channels doesn't match the layout in other cases.
+  sink_params_.set_channels_for_discrete(channels);
 
   // Specify the latency info to be passed to the browser side.
   sink_params_.set_latency_tag(latency);
diff --git a/content/renderer/media/renderer_webaudiodevice_impl.h b/content/renderer/media/renderer_webaudiodevice_impl.h
index e411ae3..00dc5955 100644
--- a/content/renderer/media/renderer_webaudiodevice_impl.h
+++ b/content/renderer/media/renderer_webaudiodevice_impl.h
@@ -35,6 +35,7 @@
 
   static RendererWebAudioDeviceImpl* Create(
       media::ChannelLayout layout,
+      int channels,
       const blink::WebAudioLatencyHint& latency_hint,
       blink::WebAudioDevice::RenderCallback* callback,
       int session_id,
@@ -57,6 +58,10 @@
   void SetMediaTaskRunnerForTesting(
       const scoped_refptr<base::SingleThreadTaskRunner>& media_task_runner);
 
+  const media::AudioParameters& get_sink_params_for_testing() {
+    return sink_params_;
+  }
+
  protected:
   // Callback to get output device params (for tests).
   using OutputDeviceParamsCallback = base::Callback<media::AudioParameters(
@@ -69,6 +74,7 @@
   using RenderFrameIdCallback = base::Callback<int()>;
 
   RendererWebAudioDeviceImpl(media::ChannelLayout layout,
+                             int channels,
                              const blink::WebAudioLatencyHint& latency_hint,
                              blink::WebAudioDevice::RenderCallback* callback,
                              int session_id,
diff --git a/content/renderer/media/renderer_webaudiodevice_impl_unittest.cc b/content/renderer/media/renderer_webaudiodevice_impl_unittest.cc
index e389c35..1b61740a 100644
--- a/content/renderer/media/renderer_webaudiodevice_impl_unittest.cc
+++ b/content/renderer/media/renderer_webaudiodevice_impl_unittest.cc
@@ -6,9 +6,11 @@
 
 #include "base/bind.h"
 #include "base/message_loop/message_loop.h"
+#include "base/strings/stringprintf.h"
 #include "build/build_config.h"
 #include "content/renderer/media/audio_device_factory.h"
 #include "media/base/audio_capturer_source.h"
+#include "media/base/limits.h"
 #include "media/base/mock_audio_renderer_sink.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -41,11 +43,13 @@
  public:
   RendererWebAudioDeviceImplUnderTest(
       media::ChannelLayout layout,
+      int channels,
       const blink::WebAudioLatencyHint& latency_hint,
       blink::WebAudioDevice::RenderCallback* callback,
       int session_id,
       const url::Origin& security_origin)
       : RendererWebAudioDeviceImpl(layout,
+                                   channels,
                                    latency_hint,
                                    callback,
                                    session_id,
@@ -66,7 +70,16 @@
 
   void SetupDevice(blink::WebAudioLatencyHint latencyHint) {
     webaudio_device_.reset(new RendererWebAudioDeviceImplUnderTest(
-        media::CHANNEL_LAYOUT_MONO, latencyHint, this, 0, url::Origin()));
+        media::CHANNEL_LAYOUT_MONO, 1, latencyHint, this, 0, url::Origin()));
+    webaudio_device_->SetMediaTaskRunnerForTesting(message_loop_.task_runner());
+  }
+
+  void SetupDevice(media::ChannelLayout layout, int channels) {
+    webaudio_device_.reset(new RendererWebAudioDeviceImplUnderTest(
+        layout, channels,
+        blink::WebAudioLatencyHint(
+            blink::WebAudioLatencyHint::kCategoryInteractive),
+        this, 0, url::Origin()));
     webaudio_device_->SetMediaTaskRunnerForTesting(message_loop_.task_runner());
   }
 
@@ -110,6 +123,23 @@
   base::MessageLoop message_loop_;
 };
 
+TEST_F(RendererWebAudioDeviceImplTest, ChannelLayout) {
+  for (int ch = 1; ch < static_cast<int>(media::limits::kMaxChannels); ++ch) {
+    SCOPED_TRACE(base::StringPrintf("ch == %d", ch));
+
+    media::ChannelLayout layout = media::GuessChannelLayout(ch);
+    if (layout == media::CHANNEL_LAYOUT_UNSUPPORTED)
+      layout = media::CHANNEL_LAYOUT_DISCRETE;
+
+    SetupDevice(layout, ch);
+    media::AudioParameters sink_params =
+        webaudio_device_->get_sink_params_for_testing();
+    EXPECT_TRUE(sink_params.IsValid());
+    EXPECT_EQ(layout, sink_params.channel_layout());
+    EXPECT_EQ(ch, sink_params.channels());
+  }
+}
+
 TEST_F(RendererWebAudioDeviceImplTest, TestLatencyHintValues) {
   blink::WebAudioLatencyHint interactiveLatencyHint(
       blink::WebAudioLatencyHint::kCategoryInteractive);
diff --git a/content/renderer/media/webrtc/media_stream_track_metrics.h b/content/renderer/media/webrtc/media_stream_track_metrics.h
index 4a4a6d3..18287f3 100644
--- a/content/renderer/media/webrtc/media_stream_track_metrics.h
+++ b/content/renderer/media/webrtc/media_stream_track_metrics.h
@@ -97,6 +97,8 @@
   ObserverVector observers_;
 
   webrtc::PeerConnectionInterface::IceConnectionState ice_state_;
+
+  DISALLOW_COPY_AND_ASSIGN(MediaStreamTrackMetrics);
 };
 
 }  // namespace
diff --git a/content/renderer/media/webrtc/peer_connection_dependency_factory.cc b/content/renderer/media/webrtc/peer_connection_dependency_factory.cc
index 41e937be..6789d072 100644
--- a/content/renderer/media/webrtc/peer_connection_dependency_factory.cc
+++ b/content/renderer/media/webrtc/peer_connection_dependency_factory.cc
@@ -62,6 +62,7 @@
 #include "third_party/WebKit/public/platform/WebURL.h"
 #include "third_party/WebKit/public/web/WebDocument.h"
 #include "third_party/WebKit/public/web/WebFrame.h"
+#include "third_party/webrtc/api/audio_codecs/builtin_audio_decoder_factory.h"
 #include "third_party/webrtc/api/mediaconstraintsinterface.h"
 #include "third_party/webrtc/api/videosourceproxy.h"
 #include "third_party/webrtc/base/ssladapter.h"
@@ -251,7 +252,9 @@
 
   pc_factory_ = webrtc::CreatePeerConnectionFactory(
       worker_thread_, signaling_thread_, audio_device_.get(),
-      encoder_factory.release(), decoder_factory.release());
+      webrtc::CreateBuiltinAudioEncoderFactory(),
+      webrtc::CreateBuiltinAudioDecoderFactory(), encoder_factory.release(),
+      decoder_factory.release());
   CHECK(pc_factory_.get());
 
   webrtc::PeerConnectionFactoryInterface::Options factory_options;
diff --git a/content/renderer/renderer_blink_platform_impl.cc b/content/renderer/renderer_blink_platform_impl.cc
index 40c87d7..ed2baee5 100644
--- a/content/renderer/renderer_blink_platform_impl.cc
+++ b/content/renderer/renderer_blink_platform_impl.cc
@@ -675,49 +675,18 @@
   // The |channels| does not exactly identify the channel layout of the
   // device. The switch statement below assigns a best guess to the channel
   // layout based on number of channels.
-  media::ChannelLayout layout = media::CHANNEL_LAYOUT_UNSUPPORTED;
-  switch (channels) {
-    case 1:
-      layout = media::CHANNEL_LAYOUT_MONO;
-      break;
-    case 2:
-      layout = media::CHANNEL_LAYOUT_STEREO;
-      break;
-    case 3:
-      layout = media::CHANNEL_LAYOUT_2_1;
-      break;
-    case 4:
-      layout = media::CHANNEL_LAYOUT_4_0;
-      break;
-    case 5:
-      layout = media::CHANNEL_LAYOUT_5_0;
-      break;
-    case 6:
-      layout = media::CHANNEL_LAYOUT_5_1;
-      break;
-    case 7:
-      layout = media::CHANNEL_LAYOUT_7_0;
-      break;
-    case 8:
-      layout = media::CHANNEL_LAYOUT_7_1;
-      break;
-    default:
-      // TODO need to also pass 'channels' into RendererWebAudioDeviceImpl for
-      // CHANNEL_LAYOUT_DISCRETE
-      NOTREACHED();
-  }
+  media::ChannelLayout layout = media::GuessChannelLayout(channels);
+  if (layout == media::CHANNEL_LAYOUT_UNSUPPORTED)
+    layout = media::CHANNEL_LAYOUT_DISCRETE;
 
   int session_id = 0;
   if (input_device_id.isNull() ||
       !base::StringToInt(input_device_id.utf8(), &session_id)) {
-    if (input_channels > 0)
-      DLOG(WARNING) << "createAudioDevice(): request for audio input ignored";
-
-    input_channels = 0;
+    session_id = 0;
   }
 
   return RendererWebAudioDeviceImpl::Create(
-      layout, latency_hint, callback, session_id,
+      layout, channels, latency_hint, callback, session_id,
       static_cast<url::Origin>(security_origin));
 }
 
diff --git a/device/bluetooth/bluetooth_remote_gatt_characteristic_unittest.cc b/device/bluetooth/bluetooth_remote_gatt_characteristic_unittest.cc
index 52fabd3..d4c9412 100644
--- a/device/bluetooth/bluetooth_remote_gatt_characteristic_unittest.cc
+++ b/device/bluetooth/bluetooth_remote_gatt_characteristic_unittest.cc
@@ -64,7 +64,7 @@
   // setup stage to test.
   void StartNotifyBoilerplate(
       int properties,
-      uint16_t expected_config_descriptor_value,
+      NotifyValueState notify_value_state,
       StartNotifySetupError error = StartNotifySetupError::NONE) {
     if (error == StartNotifySetupError::CHARACTERISTIC_PROPERTIES) {
       properties = 0;
@@ -112,7 +112,7 @@
 
     EXPECT_EQ(0, callback_count_);
     SimulateGattNotifySessionStarted(characteristic1_);
-    EXPECT_EQ(1, gatt_notify_characteristic_attempts_);
+    ExpectedChangeNotifyValueAttempts(1);
     EXPECT_EQ(1, callback_count_);
     EXPECT_EQ(0, error_callback_count_);
     ASSERT_EQ(1u, notify_sessions_.size());
@@ -122,20 +122,7 @@
     EXPECT_TRUE(notify_sessions_[0]->IsActive());
 
     // Verify the Client Characteristic Configuration descriptor was written to.
-#if !defined(OS_MACOSX)
-    // macOS: Not applicable. CoreBluetooth exposes -[CBPeripheral
-    // setNotifyValue:forCharacteristic:] which handles all interactions with
-    // the CCC descriptor.
-    EXPECT_EQ(1, gatt_write_descriptor_attempts_);
-    EXPECT_EQ(2u, last_write_value_.size());
-    uint8_t expected_byte0 = expected_config_descriptor_value & 0xFF;
-    uint8_t expected_byte1 = (expected_config_descriptor_value >> 8) & 0xFF;
-    EXPECT_EQ(expected_byte0, last_write_value_[0]);
-    EXPECT_EQ(expected_byte1, last_write_value_[1]);
-#else
-    EXPECT_EQ(1, gatt_notify_characteristic_attempts_);
-    EXPECT_TRUE(last_notify_value);
-#endif  // !defined(OS_MACOSX)
+    ExpectedNotifyValue(notify_value_state);
   }
 
   BluetoothDevice* device_ = nullptr;
@@ -998,11 +985,10 @@
     return;
   }
   ASSERT_NO_FATAL_FAILURE(StartNotifyBoilerplate(
-      /* properties: NOTIFY */ 0x10,
-      /* expected_config_descriptor_value: NOTIFY */ 1,
+      /* properties: NOTIFY */ 0x10, NotifyValueState::NOTIFY,
       StartNotifySetupError::CHARACTERISTIC_PROPERTIES));
 
-  EXPECT_EQ(0, gatt_notify_characteristic_attempts_);
+  ExpectedChangeNotifyValueAttempts(0);
 
   // The expected error callback is asynchronous:
   EXPECT_EQ(0, error_callback_count_);
@@ -1023,11 +1009,10 @@
     return;
   }
   ASSERT_NO_FATAL_FAILURE(StartNotifyBoilerplate(
-      /* properties: NOTIFY */ 0x10,
-      /* expected_config_descriptor_value: NOTIFY */ 1,
+      /* properties: NOTIFY */ 0x10, NotifyValueState::NOTIFY,
       StartNotifySetupError::CONFIG_DESCRIPTOR_MISSING));
 
-  EXPECT_EQ(0, gatt_notify_characteristic_attempts_);
+  ExpectedChangeNotifyValueAttempts(0);
 
   // The expected error callback is asynchronous:
   EXPECT_EQ(0, error_callback_count_);
@@ -1048,11 +1033,10 @@
     return;
   }
   ASSERT_NO_FATAL_FAILURE(StartNotifyBoilerplate(
-      /* properties: NOTIFY */ 0x10,
-      /* expected_config_descriptor_value: NOTIFY */ 1,
+      /* properties: NOTIFY */ 0x10, NotifyValueState::NOTIFY,
       StartNotifySetupError::CONFIG_DESCRIPTOR_DUPLICATE));
 
-  EXPECT_EQ(0, gatt_notify_characteristic_attempts_);
+  ExpectedChangeNotifyValueAttempts(0);
 
   // The expected error callback is asynchronous:
   EXPECT_EQ(0, error_callback_count_);
@@ -1073,8 +1057,7 @@
 TEST_F(BluetoothRemoteGattCharacteristicTest,
        StartNotifySession_FailToSetCharacteristicNotification) {
   ASSERT_NO_FATAL_FAILURE(StartNotifyBoilerplate(
-      /* properties: NOTIFY */ 0x10,
-      /* expected_config_descriptor_value: NOTIFY */ 1,
+      /* properties: NOTIFY */ 0x10, NotifyValueState::NOTIFY,
       StartNotifySetupError::SET_NOTIFY));
 
   // The expected error callback is asynchronous:
@@ -1082,7 +1065,7 @@
   base::RunLoop().RunUntilIdle();
   EXPECT_EQ(1, error_callback_count_);
 
-  EXPECT_EQ(0, gatt_notify_characteristic_attempts_);
+  ExpectedChangeNotifyValueAttempts(0);
   ASSERT_EQ(0u, notify_sessions_.size());
 }
 #endif  // defined(OS_ANDROID)
@@ -1096,8 +1079,7 @@
 TEST_F(BluetoothRemoteGattCharacteristicTest,
        StartNotifySession_WriteDescriptorSynchronousError) {
   ASSERT_NO_FATAL_FAILURE(StartNotifyBoilerplate(
-      /* properties: NOTIFY */ 0x10,
-      /* expected_config_descriptor_value: NOTIFY */ 1,
+      /* properties: NOTIFY */ 0x10, NotifyValueState::NOTIFY,
       StartNotifySetupError::WRITE_DESCRIPTOR));
 
   // The expected error callback is asynchronous:
@@ -1106,6 +1088,8 @@
   EXPECT_EQ(1, error_callback_count_);
 
   EXPECT_EQ(1, gatt_notify_characteristic_attempts_);
+  EXPECT_EQ(0, gatt_write_descriptor_attempts_);
+  ASSERT_EQ(0u, last_write_value_.size());
   ASSERT_EQ(0u, notify_sessions_.size());
 }
 #endif  // defined(OS_ANDROID)
@@ -1118,8 +1102,7 @@
     return;
   }
   ASSERT_NO_FATAL_FAILURE(StartNotifyBoilerplate(
-      /* properties: NOTIFY */ 0x10,
-      /* expected_config_descriptor_value: NOTIFY */ 1));
+      /* properties: NOTIFY */ 0x10, NotifyValueState::NOTIFY));
 }
 #endif  // defined(OS_ANDROID) || defined(OS_MACOSX) || defined(OS_WIN)
 
@@ -1131,8 +1114,7 @@
     return;
   }
   ASSERT_NO_FATAL_FAILURE(StartNotifyBoilerplate(
-      /* properties: INDICATE */ 0x20,
-      /* expected_config_descriptor_value: INDICATE */ 2));
+      /* properties: INDICATE */ 0x20, NotifyValueState::INDICATE));
 }
 #endif  // defined(OS_ANDROID) || defined(OS_MACOSX) || defined(OS_WIN)
 
@@ -1147,7 +1129,7 @@
   }
   ASSERT_NO_FATAL_FAILURE(StartNotifyBoilerplate(
       /* properties: NOTIFY and INDICATE bits set */ 0x30,
-      /* expected_config_descriptor_value: NOTIFY */ 1));
+      NotifyValueState::NOTIFY));
 }
 #endif  // defined(OS_ANDROID) || defined(OS_MACOSX) || defined(OS_WIN)
 
@@ -1175,7 +1157,8 @@
   EXPECT_EQ(0, callback_count_);
   SimulateGattNotifySessionStarted(characteristic1_);
   base::RunLoop().RunUntilIdle();
-  EXPECT_EQ(1, gatt_notify_characteristic_attempts_);
+  ExpectedChangeNotifyValueAttempts(1);
+  ExpectedNotifyValue(NotifyValueState::NOTIFY);
   EXPECT_EQ(2, callback_count_);
   EXPECT_EQ(0, error_callback_count_);
   ASSERT_EQ(2u, notify_sessions_.size());
@@ -1210,7 +1193,8 @@
                                        GetGattErrorCallback(Call::EXPECTED));
   characteristic1_->StartNotifySession(GetNotifyCallback(Call::NOT_EXPECTED),
                                        GetGattErrorCallback(Call::EXPECTED));
-  EXPECT_EQ(1, gatt_notify_characteristic_attempts_);
+  ExpectedChangeNotifyValueAttempts(1);
+  ExpectedNotifyValue(NotifyValueState::NOTIFY);
   EXPECT_EQ(0, callback_count_);
   SimulateGattNotifySessionStartError(
       characteristic1_, BluetoothRemoteGattService::GATT_ERROR_FAILED);
@@ -1238,7 +1222,8 @@
 
   characteristic1_->StartNotifySession(GetNotifyCallback(Call::NOT_EXPECTED),
                                        GetGattErrorCallback(Call::EXPECTED));
-  EXPECT_EQ(1, gatt_notify_characteristic_attempts_);
+  ExpectedChangeNotifyValueAttempts(1);
+  ExpectedNotifyValue(NotifyValueState::NOTIFY);
   EXPECT_EQ(0, callback_count_);
 
   RememberCharacteristicForSubsequentAction(characteristic1_);
@@ -1273,7 +1258,8 @@
   characteristic1_->StartNotifySession(
       GetNotifyCallback(Call::EXPECTED),
       GetGattErrorCallback(Call::NOT_EXPECTED));
-  EXPECT_EQ(1, gatt_notify_characteristic_attempts_);
+  ExpectedChangeNotifyValueAttempts(1);
+  ExpectedNotifyValue(NotifyValueState::NOTIFY);
   EXPECT_EQ(0, callback_count_);
 
   SimulateGattNotifySessionStarted(characteristic1_);
@@ -1321,12 +1307,13 @@
           false /* error_in_reentrant */));
   EXPECT_EQ(0, callback_count_);
   SimulateGattNotifySessionStarted(characteristic1_);
-  EXPECT_EQ(1, gatt_notify_characteristic_attempts_);
+  ExpectedChangeNotifyValueAttempts(1);
+  ExpectedNotifyValue(NotifyValueState::NOTIFY);
 
   // Simulate reentrant StartNotifySession request from
   // BluetoothTestBase::ReentrantStartNotifySessionSuccessCallback.
   base::RunLoop().RunUntilIdle();
-  EXPECT_EQ(1, gatt_notify_characteristic_attempts_);
+  ExpectedChangeNotifyValueAttempts(1);
   EXPECT_EQ(2, callback_count_);
   EXPECT_EQ(0, error_callback_count_);
   ASSERT_EQ(2u, notify_sessions_.size());
@@ -1361,13 +1348,14 @@
           Call::EXPECTED, characteristic1_, false /* error_in_reentrant */));
   EXPECT_EQ(0, callback_count_);
   SimulateGattNotifySessionStarted(characteristic1_);
-  EXPECT_EQ(0, gatt_notify_characteristic_attempts_);
+  ExpectedChangeNotifyValueAttempts(0);
   EXPECT_EQ(1, error_callback_count_);
 
   // Simulate reentrant StartNotifySession request from
   // BluetoothTestBase::ReentrantStartNotifySessionErrorCallback.
   SimulateGattNotifySessionStarted(characteristic1_);
-  EXPECT_EQ(1, gatt_notify_characteristic_attempts_);
+  ExpectedChangeNotifyValueAttempts(1);
+  ExpectedNotifyValue(NotifyValueState::NOTIFY);
   EXPECT_EQ(1, callback_count_);
   EXPECT_EQ(1, error_callback_count_);
   ASSERT_EQ(1u, notify_sessions_.size());
@@ -1400,12 +1388,12 @@
           Call::EXPECTED, characteristic1_, true /* error_in_reentrant */));
   EXPECT_EQ(0, callback_count_);
   SimulateGattNotifySessionStarted(characteristic1_);
-  EXPECT_EQ(0, gatt_notify_characteristic_attempts_);
+  ExpectedChangeNotifyValueAttempts(0);
 
   // Simulate reentrant StartNotifySession request from
   // BluetoothTestBase::ReentrantStartNotifySessionErrorCallback.
   SimulateGattNotifySessionStarted(characteristic1_);
-  EXPECT_EQ(0, gatt_notify_characteristic_attempts_);
+  ExpectedChangeNotifyValueAttempts(0);
   EXPECT_EQ(0, callback_count_);
   EXPECT_EQ(2, error_callback_count_);
   ASSERT_EQ(0u, notify_sessions_.size());
@@ -1420,35 +1408,16 @@
     return;
   }
   ASSERT_NO_FATAL_FAILURE(StartNotifyBoilerplate(
-      /* properties: NOTIFY */ 0x10,
-      /* expected_config_descriptor_value: NOTIFY */ 1));
-// macOS: Not applicable. CoreBluetooth exposes -[CBPeripheral
-// setNotifyValue:forCharacteristic:] which handles all interactions with
-// the CCC descriptor.
-#if !defined(OS_MACOSX)
-  EXPECT_EQ(1, gatt_write_descriptor_attempts_);
-#else
-  EXPECT_EQ(1, gatt_notify_characteristic_attempts_);
-  EXPECT_TRUE(last_notify_value);
-#endif  // !defined(OS_MACOSX)
+      /* properties: NOTIFY */ 0x10, NotifyValueState::NOTIFY));
+  ExpectedChangeNotifyValueAttempts(1);
+  ExpectedNotifyValue(NotifyValueState::NOTIFY);
 
   notify_sessions_[0]->Stop(GetStopNotifyCallback(Call::EXPECTED));
   SimulateGattNotifySessionStopped(characteristic1_);
   base::RunLoop().RunUntilIdle();
 
-// macOS: Not applicable. CoreBluetooth exposes -[CBPeripheral
-// setNotifyValue:forCharacteristic:] which handles all interactions with
-// the CCC descriptor.
-#if !defined(OS_MACOSX)
-  // Check that the right values were written to the descriptor.
-  EXPECT_EQ(2, gatt_write_descriptor_attempts_);
-  ASSERT_EQ(2u, last_write_value_.size());
-  EXPECT_EQ(0, last_write_value_[0]);
-  EXPECT_EQ(0, last_write_value_[1]);
-#else
-  EXPECT_EQ(2, gatt_notify_characteristic_attempts_);
-  EXPECT_FALSE(last_notify_value);
-#endif  // !defined(OS_MACOSX)
+  ExpectedChangeNotifyValueAttempts(2);
+  ExpectedNotifyValue(NotifyValueState::NONE);
 
   // Check that the notify session is inactive.
   EXPECT_FALSE(notify_sessions_[0]->IsActive());
@@ -1465,35 +1434,16 @@
     return;
   }
   ASSERT_NO_FATAL_FAILURE(StartNotifyBoilerplate(
-      /* properties: NOTIFY */ 0x10,
-      /* expected_config_descriptor_value: NOTIFY */ 1));
-// macOS: Not applicable. CoreBluetooth exposes -[CBPeripheral
-// setNotifyValue:forCharacteristic:] which handles all interactions with
-// the CCC descriptor.
-#if !defined(OS_MACOSX)
-  EXPECT_EQ(1, gatt_write_descriptor_attempts_);
-#else
-  EXPECT_EQ(1, gatt_notify_characteristic_attempts_);
-  EXPECT_TRUE(last_notify_value);
-#endif  // !defined(OS_MACOSX)
+      /* properties: NOTIFY */ 0x10, NotifyValueState::NOTIFY));
+  ExpectedChangeNotifyValueAttempts(1);
+  ExpectedNotifyValue(NotifyValueState::NOTIFY);
 
   notify_sessions_.clear();
   SimulateGattNotifySessionStopped(characteristic1_);
   base::RunLoop().RunUntilIdle();
 
-// macOS: Not applicable. CoreBluetooth exposes -[CBPeripheral
-// setNotifyValue:forCharacteristic:] which handles all interactions with
-// the CCC descriptor.
-#if !defined(OS_MACOSX)
-  // Check that the right values were written to the descriptor.
-  EXPECT_EQ(2, gatt_write_descriptor_attempts_);
-  ASSERT_EQ(2u, last_write_value_.size());
-  EXPECT_EQ(0, last_write_value_[0]);
-  EXPECT_EQ(0, last_write_value_[1]);
-#else
-  EXPECT_EQ(2, gatt_notify_characteristic_attempts_);
-  EXPECT_FALSE(last_notify_value);
-#endif  // !defined(OS_MACOSX)
+  ExpectedChangeNotifyValueAttempts(2);
+  ExpectedNotifyValue(NotifyValueState::NONE);
 
   // Check that the notify session is inactive.
   EXPECT_FALSE(characteristic1_->IsNotifying());
@@ -1528,7 +1478,8 @@
   EXPECT_EQ(0, callback_count_);
   SimulateGattNotifySessionStarted(characteristic1_);
   base::RunLoop().RunUntilIdle();
-  EXPECT_EQ(1, gatt_notify_characteristic_attempts_);
+  ExpectedChangeNotifyValueAttempts(1);
+  ExpectedNotifyValue(NotifyValueState::NOTIFY);
   EXPECT_EQ(2, callback_count_);
   EXPECT_EQ(0, error_callback_count_);
   ASSERT_EQ(2u, notify_sessions_.size());
@@ -1568,8 +1519,7 @@
     return;
   }
   ASSERT_NO_FATAL_FAILURE(StartNotifyBoilerplate(
-      /* properties: NOTIFY */ 0x10,
-      /* expected_config_descriptor_value: NOTIFY */ 1));
+      /* properties: NOTIFY */ 0x10, NotifyValueState::NOTIFY));
 
   // Check that the session is correctly setup.
   std::string characteristic_identifier = characteristic1_->GetIdentifier();
@@ -1594,8 +1544,7 @@
     return;
   }
   ASSERT_NO_FATAL_FAILURE(StartNotifyBoilerplate(
-      /* properties: NOTIFY */ 0x10,
-      /* expected_config_descriptor_value: NOTIFY */ 1));
+      /* properties: NOTIFY */ 0x10, NotifyValueState::NOTIFY));
 
   // Check that the session is correctly setup
   std::string characteristic_identifier = characteristic1_->GetIdentifier();
@@ -1632,35 +1581,16 @@
     return;
   }
   ASSERT_NO_FATAL_FAILURE(StartNotifyBoilerplate(
-      /* properties: INDICATE */ 0x20,
-      /* expected_config_descriptor_value: INDICATE */ 2));
-// macOS: Not applicable. CoreBluetooth exposes -[CBPeripheral
-// setNotifyValue:forCharacteristic:] which handles all interactions with
-// the CCC descriptor.
-#if !defined(OS_MACOSX)
-  EXPECT_EQ(1, gatt_write_descriptor_attempts_);
-#else
-  EXPECT_EQ(1, gatt_notify_characteristic_attempts_);
-  EXPECT_TRUE(last_notify_value);
-#endif  // !defined(OS_MACOSX)
+      /* properties: INDICATE */ 0x20, NotifyValueState::INDICATE));
+  ExpectedChangeNotifyValueAttempts(1);
+  ExpectedNotifyValue(NotifyValueState::INDICATE);
 
   notify_sessions_[0]->Stop(GetStopNotifyCallback(Call::EXPECTED));
   SimulateGattNotifySessionStopped(characteristic1_);
   base::RunLoop().RunUntilIdle();
 
-// macOS: Not applicable. CoreBluetooth exposes -[CBPeripheral
-// setNotifyValue:forCharacteristic:] which handles all interactions with
-// the CCC descriptor.
-#if !defined(OS_MACOSX)
-  // Check that the right values were written to the descriptor.
-  EXPECT_EQ(2, gatt_write_descriptor_attempts_);
-  ASSERT_EQ(2u, last_write_value_.size());
-  EXPECT_EQ(0, last_write_value_[0]);
-  EXPECT_EQ(0, last_write_value_[1]);
-#else
-  EXPECT_EQ(2, gatt_notify_characteristic_attempts_);
-  EXPECT_FALSE(last_notify_value);
-#endif  // !defined(OS_MACOSX)
+  ExpectedChangeNotifyValueAttempts(2);
+  ExpectedNotifyValue(NotifyValueState::NONE);
 
   // Check that the notify session is inactive.
   EXPECT_FALSE(notify_sessions_[0]->IsActive());
@@ -1679,34 +1609,16 @@
   }
   ASSERT_NO_FATAL_FAILURE(StartNotifyBoilerplate(
       /* properties: NOTIFY and INDICATE bits set */ 0x30,
-      /* expected_config_descriptor_value: INDICATE */ 1));
-// macOS: Not applicable. CoreBluetooth exposes -[CBPeripheral
-// setNotifyValue:forCharacteristic:] which handles all interactions with
-// the CCC descriptor.
-#if !defined(OS_MACOSX)
-  EXPECT_EQ(1, gatt_write_descriptor_attempts_);
-#else
-  EXPECT_EQ(1, gatt_notify_characteristic_attempts_);
-  EXPECT_TRUE(last_notify_value);
-#endif  // !defined(OS_MACOSX)
+      NotifyValueState::NOTIFY));
+  ExpectedChangeNotifyValueAttempts(1);
+  ExpectedNotifyValue(NotifyValueState::NOTIFY);
 
   notify_sessions_[0]->Stop(GetStopNotifyCallback(Call::EXPECTED));
   SimulateGattNotifySessionStopped(characteristic1_);
   base::RunLoop().RunUntilIdle();
 
-// macOS: Not applicable. CoreBluetooth exposes -[CBPeripheral
-// setNotifyValue:forCharacteristic:] which handles all interactions with
-// the CCC descriptor.
-#if !defined(OS_MACOSX)
-  // Check that the right values were written to the descriptor.
-  EXPECT_EQ(2, gatt_write_descriptor_attempts_);
-  ASSERT_EQ(2u, last_write_value_.size());
-  EXPECT_EQ(0, last_write_value_[0]);
-  EXPECT_EQ(0, last_write_value_[1]);
-#else
-  EXPECT_EQ(2, gatt_notify_characteristic_attempts_);
-  EXPECT_FALSE(last_notify_value);
-#endif  // !defined(OS_MACOSX)
+  ExpectedChangeNotifyValueAttempts(2);
+  ExpectedNotifyValue(NotifyValueState::NONE);
 
   // Check that the notify session is inactive.
   EXPECT_FALSE(notify_sessions_[0]->IsActive());
@@ -1722,8 +1634,7 @@
     return;
   }
   ASSERT_NO_FATAL_FAILURE(StartNotifyBoilerplate(
-      /* properties: NOTIFY */ 0x10,
-      /* expected_config_descriptor_value: NOTIFY */ 1));
+      /* properties: NOTIFY */ 0x10, NotifyValueState::NOTIFY));
 
   // Check that the notify session is active.
   EXPECT_TRUE(notify_sessions_[0]->IsActive());
@@ -1766,7 +1677,8 @@
   EXPECT_EQ(0, callback_count_);
   SimulateGattNotifySessionStarted(characteristic1_);
   base::RunLoop().RunUntilIdle();
-  EXPECT_EQ(1, gatt_notify_characteristic_attempts_);
+  ExpectedChangeNotifyValueAttempts(1);
+  ExpectedNotifyValue(NotifyValueState::NOTIFY);
   EXPECT_EQ(1, callback_count_);
   EXPECT_EQ(0, error_callback_count_);
   ASSERT_EQ(1u, notify_sessions_.size());
@@ -1814,7 +1726,8 @@
   EXPECT_EQ(0, callback_count_);
   SimulateGattNotifySessionStarted(characteristic1_);
   base::RunLoop().RunUntilIdle();
-  EXPECT_EQ(1, gatt_notify_characteristic_attempts_);
+  ExpectedChangeNotifyValueAttempts(1);
+  ExpectedNotifyValue(NotifyValueState::NOTIFY);
   EXPECT_EQ(2, callback_count_);
   EXPECT_EQ(0, error_callback_count_);
   ASSERT_EQ(2u, notify_sessions_.size());
@@ -1902,8 +1815,7 @@
     return;
   }
   ASSERT_NO_FATAL_FAILURE(StartNotifyBoilerplate(
-      /* properties: NOTIFY */ 0x10,
-      /* expected_config_descriptor_value: NOTIFY */ 1));
+      /* properties: NOTIFY */ 0x10, NotifyValueState::NOTIFY));
 
   // Check that the initial notify session is active.
   EXPECT_TRUE(notify_sessions_[0]->IsActive());
@@ -1976,48 +1888,23 @@
   ASSERT_EQ(1u, notify_sessions_.size());
   ASSERT_TRUE(notify_sessions_[0]);
   EXPECT_TRUE(notify_sessions_[0]->IsActive());
-// macOS: Not applicable. CoreBluetooth exposes -[CBPeripheral
-// setNotifyValue:forCharacteristic:] which handles all interactions with
-// the CCC descriptor.
-#if !defined(OS_MACOSX)
-  EXPECT_EQ(1, gatt_write_descriptor_attempts_);
-#else
-  EXPECT_EQ(1, gatt_notify_characteristic_attempts_);
-  EXPECT_TRUE(last_notify_value);
-#endif  // !defined(OS_MACOSX)
+  ExpectedChangeNotifyValueAttempts(1);
+  ExpectedNotifyValue(NotifyValueState::NOTIFY);
 
   // Stop the notify session twice
   notify_sessions_[0]->Stop(GetStopNotifyCheckForPrecedingCalls(1));
   notify_sessions_[0]->Stop(GetStopNotifyCheckForPrecedingCalls(2));
 
-// macOS: Not applicable. CoreBluetooth exposes -[CBPeripheral
-// setNotifyValue:forCharacteristic:] which handles all interactions with
-// the CCC descriptor.
-#if !defined(OS_MACOSX)
-  // Check that the right values were written to the descriptor.
-  EXPECT_EQ(2, gatt_write_descriptor_attempts_);
-  ASSERT_EQ(2u, last_write_value_.size());
-  EXPECT_EQ(0, last_write_value_[0]);
-  EXPECT_EQ(0, last_write_value_[1]);
-#else
-  EXPECT_EQ(2, gatt_notify_characteristic_attempts_);
-  EXPECT_FALSE(last_notify_value);
-#endif  // !defined(OS_MACOSX)
+  ExpectedChangeNotifyValueAttempts(2);
+  ExpectedNotifyValue(NotifyValueState::NONE);
 
   // Start another notify session
   characteristic1_->StartNotifySession(
       GetNotifyCheckForPrecedingCalls(3),
       GetGattErrorCallback(Call::NOT_EXPECTED));
 
-// macOS: Not applicable. CoreBluetooth exposes -[CBPeripheral
-// setNotifyValue:forCharacteristic:] which handles all interactions with
-// the CCC descriptor.
-#if !defined(OS_MACOSX)
-  // Check that nothing was written by the StartNotifySession call above
-  EXPECT_EQ(2, gatt_write_descriptor_attempts_);
-#else
-  EXPECT_EQ(2, gatt_notify_characteristic_attempts_);
-#endif  // !defined(OS_MACOSX)
+  ExpectedChangeNotifyValueAttempts(2);
+  ExpectedNotifyValue(NotifyValueState::NONE);
 
   SimulateGattNotifySessionStopped(characteristic1_);
   base::RunLoop().RunUntilIdle();
@@ -2026,19 +1913,8 @@
   SimulateGattNotifySessionStarted(characteristic1_);
   base::RunLoop().RunUntilIdle();
 
-// macOS: Not applicable. CoreBluetooth exposes -[CBPeripheral
-// setNotifyValue:forCharacteristic:] which handles all interactions with
-// the CCC descriptor.
-#if !defined(OS_MACOSX)
-  // Check that the right values were written to the descriptor.
-  EXPECT_EQ(3, gatt_write_descriptor_attempts_);
-  ASSERT_EQ(2u, last_write_value_.size());
-  EXPECT_EQ(1, last_write_value_[0]);
-  EXPECT_EQ(0, last_write_value_[1]);
-#else
-  EXPECT_EQ(3, gatt_notify_characteristic_attempts_);
-  EXPECT_TRUE(last_notify_value);
-#endif  // !defined(OS_MACOSX)
+  ExpectedChangeNotifyValueAttempts(3);
+  ExpectedNotifyValue(NotifyValueState::NOTIFY);
 
   // Check the notify state
   ASSERT_EQ(2u, notify_sessions_.size());
@@ -2102,8 +1978,7 @@
     return;
   }
   ASSERT_NO_FATAL_FAILURE(StartNotifyBoilerplate(
-      /* properties: NOTIFY */ 0x10,
-      /* expected_config_descriptor_value: NOTIFY */ 1));
+      /* properties: NOTIFY */ 0x10, NotifyValueState::NOTIFY));
 
   // Check that the notify session is active.
   EXPECT_TRUE(notify_sessions_[0]->IsActive());
@@ -2148,8 +2023,7 @@
     return;
   }
   ASSERT_NO_FATAL_FAILURE(StartNotifyBoilerplate(
-      /* properties: NOTIFY */ 0x10,
-      /* expected_config_descriptor_value: NOTIFY */ 1));
+      /* properties: NOTIFY */ 0x10, NotifyValueState::NOTIFY));
 
   // Check that the notify session is active.
   EXPECT_TRUE(notify_sessions_[0]->IsActive());
@@ -2192,8 +2066,7 @@
     return;
   }
   ASSERT_NO_FATAL_FAILURE(StartNotifyBoilerplate(
-      /* properties: NOTIFY */ 0x10,
-      /* expected_config_descriptor_value: NOTIFY */ 1));
+      /* properties: NOTIFY */ 0x10, NotifyValueState::NOTIFY));
 
   TestBluetoothAdapterObserver observer(adapter_);
 
@@ -2219,8 +2092,7 @@
 TEST_F(BluetoothRemoteGattCharacteristicTest,
        GattCharacteristicValueChanged_AfterDeleted) {
   ASSERT_NO_FATAL_FAILURE(StartNotifyBoilerplate(
-      /* properties: NOTIFY */ 0x10,
-      /* expected_config_descriptor_value: NOTIFY */ 1));
+      /* properties: NOTIFY */ 0x10, NotifyValueState::NOTIFY));
   TestBluetoothAdapterObserver observer(adapter_);
 
   RememberCharacteristicForSubsequentAction(characteristic1_);
diff --git a/device/bluetooth/test/bluetooth_test.cc b/device/bluetooth/test/bluetooth_test.cc
index 394227812e..234b0486 100644
--- a/device/bluetooth/test/bluetooth_test.cc
+++ b/device/bluetooth/test/bluetooth_test.cc
@@ -92,6 +92,30 @@
   return std::vector<uint8_t>();
 }
 
+void BluetoothTestBase::ExpectedChangeNotifyValueAttempts(int attempts) {
+  EXPECT_EQ(attempts, gatt_write_descriptor_attempts_);
+  EXPECT_EQ(attempts, gatt_notify_characteristic_attempts_);
+}
+
+void BluetoothTestBase::ExpectedNotifyValue(
+    NotifyValueState expected_value_state) {
+  ASSERT_EQ(2u, last_write_value_.size());
+  switch (expected_value_state) {
+    case NotifyValueState::NONE:
+      EXPECT_EQ(0, last_write_value_[0]);
+      EXPECT_EQ(0, last_write_value_[1]);
+      break;
+    case NotifyValueState::NOTIFY:
+      EXPECT_EQ(1, last_write_value_[0]);
+      EXPECT_EQ(0, last_write_value_[1]);
+      break;
+    case NotifyValueState::INDICATE:
+      EXPECT_EQ(2, last_write_value_[0]);
+      EXPECT_EQ(0, last_write_value_[1]);
+      break;
+  }
+}
+
 std::vector<BluetoothLocalGattService*>
 BluetoothTestBase::RegisteredGattServices() {
   NOTIMPLEMENTED();
diff --git a/device/bluetooth/test/bluetooth_test.h b/device/bluetooth/test/bluetooth_test.h
index 52b6224..96fca52 100644
--- a/device/bluetooth/test/bluetooth_test.h
+++ b/device/bluetooth/test/bluetooth_test.h
@@ -56,6 +56,12 @@
     HEART_RATE_DEVICE,
   };
 
+  enum class NotifyValueState {
+    NONE,
+    NOTIFY,
+    INDICATE,
+  };
+
   static const std::string kTestAdapterName;
   static const std::string kTestAdapterAddress;
 
@@ -401,6 +407,15 @@
   virtual void SimulateGattDescriptorWriteWillFailSynchronouslyOnce(
       BluetoothRemoteGattDescriptor* descriptor) {}
 
+  // Tests that functions to change the notify value have been called |attempts|
+  // times.
+  virtual void ExpectedChangeNotifyValueAttempts(int attempts);
+
+  // Tests that the notify value is |expected_value_state|. The default
+  // implementation checks that the correct value has been written to the CCC
+  // Descriptor.
+  virtual void ExpectedNotifyValue(NotifyValueState expected_value_state);
+
   // Returns a list of local GATT services registered with the adapter.
   virtual std::vector<BluetoothLocalGattService*> RegisteredGattServices();
 
@@ -465,7 +480,7 @@
       bool error_in_reentrant);
 
   // Reset all event count members to 0.
-  void ResetEventCounts();
+  virtual void ResetEventCounts();
 
   void RemoveTimedOutDevices();
 
diff --git a/device/bluetooth/test/bluetooth_test_mac.h b/device/bluetooth/test/bluetooth_test_mac.h
index 3370856..daa0d46 100644
--- a/device/bluetooth/test/bluetooth_test_mac.h
+++ b/device/bluetooth/test/bluetooth_test_mac.h
@@ -39,6 +39,7 @@
   void InitWithDefaultAdapter() override;
   void InitWithoutDefaultAdapter() override;
   void InitWithFakeAdapter() override;
+  void ResetEventCounts() override;
   BluetoothDevice* SimulateLowEnergyDevice(int device_ordinal) override;
   void SimulateConnectedLowEnergyDevice(
       ConnectedDeviceType device_ordinal) override;
@@ -84,6 +85,8 @@
   void SimulateGattCharacteristicRemoved(
       BluetoothRemoteGattService* service,
       BluetoothRemoteGattCharacteristic* characteristic) override;
+  void ExpectedChangeNotifyValueAttempts(int attempts) override;
+  void ExpectedNotifyValue(NotifyValueState expected_value_state) override;
 
   // Callback for the bluetooth central manager mock.
   void OnFakeBluetoothDeviceConnectGattCalled();
@@ -120,7 +123,7 @@
   std::unique_ptr<ScopedMockCentralManager> mock_central_manager_;
 
   // Value set by -[CBPeripheral setNotifyValue:forCharacteristic:] call.
-  bool last_notify_value = false;
+  bool last_notify_value_ = false;
 };
 
 // Defines common test fixture name. Use TEST_F(BluetoothTest, YourTestName).
diff --git a/device/bluetooth/test/bluetooth_test_mac.mm b/device/bluetooth/test/bluetooth_test_mac.mm
index eb4bf905e..41b5748 100644
--- a/device/bluetooth/test/bluetooth_test_mac.mm
+++ b/device/bluetooth/test/bluetooth_test_mac.mm
@@ -133,6 +133,11 @@
   }
 }
 
+void BluetoothTestMac::ResetEventCounts() {
+  BluetoothTestBase::ResetEventCounts();
+  last_notify_value_ = false;
+}
+
 BluetoothDevice* BluetoothTestMac::SimulateLowEnergyDevice(int device_ordinal) {
   TestBluetoothAdapterObserver observer(adapter_);
   CBCentralManager* central_manager = adapter_mac_->low_energy_central_manager_;
@@ -465,6 +470,23 @@
   [peripheral_mock mockDidDiscoverEvents];
 }
 
+void BluetoothTestMac::ExpectedChangeNotifyValueAttempts(int attempts) {
+  EXPECT_EQ(attempts, gatt_notify_characteristic_attempts_);
+}
+
+void BluetoothTestMac::ExpectedNotifyValue(
+    NotifyValueState expected_value_state) {
+  switch (expected_value_state) {
+    case NotifyValueState::NONE:
+      EXPECT_FALSE(last_notify_value_);
+      break;
+    case NotifyValueState::NOTIFY:
+    case NotifyValueState::INDICATE:
+      EXPECT_TRUE(last_notify_value_);
+      break;
+  }
+}
+
 void BluetoothTestMac::OnFakeBluetoothDeviceConnectGattCalled() {
   gatt_connection_attempts_++;
 }
@@ -489,7 +511,7 @@
 
 void BluetoothTest::OnFakeBluetoothGattSetCharacteristicNotification(
     bool notify_value) {
-  last_notify_value = notify_value;
+  last_notify_value_ = notify_value;
   gatt_notify_characteristic_attempts_++;
 }
 
diff --git a/extensions/BUILD.gn b/extensions/BUILD.gn
index 0b11984..ffe15e8e6 100644
--- a/extensions/BUILD.gn
+++ b/extensions/BUILD.gn
@@ -18,6 +18,7 @@
 
 grit("extensions_resources_grd") {
   source = "extensions_resources.grd"
+  use_qualified_include = true
   outputs = [
     "grit/extensions_resources.h",
     "extensions_resources.pak",
@@ -26,6 +27,10 @@
 
 grit("extensions_browser_resources") {
   source = "browser/resources/extensions_browser_resources.grd"
+
+  # TODO(thakis): Enable, https://crbug.com/401588
+  #use_qualified_include = true
+
   outputs = [
     "grit/extensions_browser_resources.h",
     "grit/extensions_browser_resources_map.cc",
@@ -41,6 +46,7 @@
 
 grit("extensions_renderer_resources") {
   source = "renderer/resources/extensions_renderer_resources.grd"
+  use_qualified_include = true
   outputs = [
     "grit/extensions_renderer_resources.h",
     "extensions_renderer_resources.pak",
diff --git a/extensions/DEPS b/extensions/DEPS
index 0765359a..f79d2406 100644
--- a/extensions/DEPS
+++ b/extensions/DEPS
@@ -12,9 +12,9 @@
   "+content/public/test",
   "+crypto",
   "-extensions/components",
+  "+extensions/grit/extensions_renderer_resources.h",
+  "+extensions/grit/extensions_resources.h",
   "+extensions/test",
-  "+grit/extensions_renderer_resources.h",
-  "+grit/extensions_resources.h",
   "+mojo/public",
   "+testing",
   "+third_party/skia/include",
diff --git a/extensions/renderer/app_window_custom_bindings.cc b/extensions/renderer/app_window_custom_bindings.cc
index 7493219..1d7c7378 100644
--- a/extensions/renderer/app_window_custom_bindings.cc
+++ b/extensions/renderer/app_window_custom_bindings.cc
@@ -11,8 +11,8 @@
 #include "content/public/renderer/render_view.h"
 #include "extensions/common/extension_messages.h"
 #include "extensions/common/switches.h"
+#include "extensions/grit/extensions_renderer_resources.h"
 #include "extensions/renderer/script_context.h"
-#include "grit/extensions_renderer_resources.h"
 #include "third_party/WebKit/public/web/WebLocalFrame.h"
 #include "third_party/WebKit/public/web/WebView.h"
 #include "ui/base/resource/resource_bundle.h"
diff --git a/extensions/renderer/dispatcher.cc b/extensions/renderer/dispatcher.cc
index 5510df1..b78e7977 100644
--- a/extensions/renderer/dispatcher.cc
+++ b/extensions/renderer/dispatcher.cc
@@ -54,6 +54,7 @@
 #include "extensions/common/permissions/permissions_data.h"
 #include "extensions/common/switches.h"
 #include "extensions/common/view_type.h"
+#include "extensions/grit/extensions_renderer_resources.h"
 #include "extensions/renderer/api_activity_logger.h"
 #include "extensions/renderer/api_definitions_natives.h"
 #include "extensions/renderer/app_window_custom_bindings.h"
@@ -100,7 +101,6 @@
 #include "extensions/renderer/worker_script_context_set.h"
 #include "extensions/renderer/worker_thread_dispatcher.h"
 #include "gin/converter.h"
-#include "grit/extensions_renderer_resources.h"
 #include "mojo/public/js/constants.h"
 #include "third_party/WebKit/public/platform/WebString.h"
 #include "third_party/WebKit/public/platform/WebURLRequest.h"
diff --git a/extensions/renderer/event_unittest.cc b/extensions/renderer/event_unittest.cc
index 5969462..7601b30 100644
--- a/extensions/renderer/event_unittest.cc
+++ b/extensions/renderer/event_unittest.cc
@@ -3,8 +3,8 @@
 // found in the LICENSE file.
 
 #include "extensions/common/extension_urls.h"
+#include "extensions/grit/extensions_renderer_resources.h"
 #include "extensions/renderer/module_system_test.h"
-#include "grit/extensions_renderer_resources.h"
 
 namespace extensions {
 namespace {
diff --git a/extensions/renderer/json_schema_unittest.cc b/extensions/renderer/json_schema_unittest.cc
index 122f0397..789ec96 100644
--- a/extensions/renderer/json_schema_unittest.cc
+++ b/extensions/renderer/json_schema_unittest.cc
@@ -2,10 +2,10 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include "extensions/grit/extensions_renderer_resources.h"
 #include "extensions/renderer/module_system_test.h"
 #include "extensions/renderer/v8_schema_registry.h"
 #include "gin/dictionary.h"
-#include "grit/extensions_renderer_resources.h"
 
 namespace extensions {
 
diff --git a/extensions/renderer/messaging_utils_unittest.cc b/extensions/renderer/messaging_utils_unittest.cc
index fbb2dec..bd2c66d 100644
--- a/extensions/renderer/messaging_utils_unittest.cc
+++ b/extensions/renderer/messaging_utils_unittest.cc
@@ -3,8 +3,8 @@
 // found in the LICENSE file.
 
 #include "base/strings/stringprintf.h"
+#include "extensions/grit/extensions_renderer_resources.h"
 #include "extensions/renderer/module_system_test.h"
-#include "grit/extensions_renderer_resources.h"
 
 namespace extensions {
 namespace {
diff --git a/extensions/renderer/mojo/keep_alive_client_unittest.cc b/extensions/renderer/mojo/keep_alive_client_unittest.cc
index 2fc90b7..37bc9b4 100644
--- a/extensions/renderer/mojo/keep_alive_client_unittest.cc
+++ b/extensions/renderer/mojo/keep_alive_client_unittest.cc
@@ -7,8 +7,8 @@
 #include "base/macros.h"
 #include "base/memory/ptr_util.h"
 #include "extensions/common/mojo/keep_alive.mojom.h"
+#include "extensions/grit/extensions_renderer_resources.h"
 #include "extensions/renderer/api_test_base.h"
-#include "grit/extensions_renderer_resources.h"
 #include "mojo/public/cpp/bindings/strong_binding.h"
 
 // A test launcher for tests for the stash client defined in
diff --git a/extensions/renderer/user_script_injector.cc b/extensions/renderer/user_script_injector.cc
index 7309686..e43f461a 100644
--- a/extensions/renderer/user_script_injector.cc
+++ b/extensions/renderer/user_script_injector.cc
@@ -9,16 +9,16 @@
 
 #include "base/lazy_instance.h"
 #include "content/public/common/url_constants.h"
-#include "content/public/renderer/render_thread.h"
 #include "content/public/renderer/render_frame.h"
+#include "content/public/renderer/render_thread.h"
 #include "content/public/renderer/render_view.h"
 #include "extensions/common/extension.h"
 #include "extensions/common/guest_view/extensions_guest_view_messages.h"
 #include "extensions/common/permissions/permissions_data.h"
+#include "extensions/grit/extensions_renderer_resources.h"
 #include "extensions/renderer/injection_host.h"
 #include "extensions/renderer/script_context.h"
 #include "extensions/renderer/scripts_run_info.h"
-#include "grit/extensions_renderer_resources.h"
 #include "third_party/WebKit/public/web/WebDocument.h"
 #include "third_party/WebKit/public/web/WebLocalFrame.h"
 #include "third_party/WebKit/public/web/WebScriptSource.h"
diff --git a/extensions/renderer/utils_unittest.cc b/extensions/renderer/utils_unittest.cc
index b45c359..31f856b 100644
--- a/extensions/renderer/utils_unittest.cc
+++ b/extensions/renderer/utils_unittest.cc
@@ -3,9 +3,9 @@
 // found in the LICENSE file.
 
 #include "base/strings/stringprintf.h"
+#include "extensions/grit/extensions_renderer_resources.h"
 #include "extensions/renderer/module_system_test.h"
 #include "gin/dictionary.h"
-#include "grit/extensions_renderer_resources.h"
 
 namespace extensions {
 namespace {
diff --git a/extensions/shell/common/shell_extensions_client.cc b/extensions/shell/common/shell_extensions_client.cc
index 89246e5..6ecb7d78 100644
--- a/extensions/shell/common/shell_extensions_client.cc
+++ b/extensions/shell/common/shell_extensions_client.cc
@@ -21,13 +21,13 @@
 #include "extensions/common/permissions/permissions_info.h"
 #include "extensions/common/permissions/permissions_provider.h"
 #include "extensions/common/url_pattern_set.h"
+#include "extensions/grit/extensions_resources.h"
 #include "extensions/shell/common/api/generated_schemas.h"
 #include "extensions/shell/common/api/shell_api_features.h"
 #include "extensions/shell/common/api/shell_behavior_features.h"
 #include "extensions/shell/common/api/shell_manifest_features.h"
 #include "extensions/shell/common/api/shell_permission_features.h"
 #include "grit/app_shell_resources.h"
-#include "grit/extensions_resources.h"
 
 namespace extensions {
 
diff --git a/extensions/test/test_extensions_client.cc b/extensions/test/test_extensions_client.cc
index e71d89ed9..2a6f6c07 100644
--- a/extensions/test/test_extensions_client.cc
+++ b/extensions/test/test_extensions_client.cc
@@ -20,12 +20,12 @@
 #include "extensions/common/permissions/extensions_api_permissions.h"
 #include "extensions/common/permissions/permissions_info.h"
 #include "extensions/common/url_pattern_set.h"
+#include "extensions/grit/extensions_resources.h"
 #include "extensions/test/test_api_features.h"
 #include "extensions/test/test_behavior_features.h"
 #include "extensions/test/test_manifest_features.h"
 #include "extensions/test/test_permission_features.h"
 #include "extensions/test/test_permission_message_provider.h"
-#include "grit/extensions_resources.h"
 
 namespace extensions {
 
diff --git a/ios/chrome/browser/ui/tools_menu/BUILD.gn b/ios/chrome/browser/ui/tools_menu/BUILD.gn
index 9dcda12..91972e30 100644
--- a/ios/chrome/browser/ui/tools_menu/BUILD.gn
+++ b/ios/chrome/browser/ui/tools_menu/BUILD.gn
@@ -8,8 +8,6 @@
     "reading_list_menu_view_item.mm",
     "tools_menu_context.h",
     "tools_menu_context.mm",
-    "tools_menu_model.h",
-    "tools_menu_model.mm",
     "tools_menu_view_controller.h",
     "tools_menu_view_controller.mm",
     "tools_menu_view_item.h",
diff --git a/ios/chrome/browser/ui/tools_menu/tools_menu_model.h b/ios/chrome/browser/ui/tools_menu/tools_menu_model.h
deleted file mode 100644
index 2965b491..0000000
--- a/ios/chrome/browser/ui/tools_menu/tools_menu_model.h
+++ /dev/null
@@ -1,83 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef IOS_CHROME_BROWSER_UI_TOOLS_MENU_TOOLS_MENU_MODEL_H_
-#define IOS_CHROME_BROWSER_UI_TOOLS_MENU_TOOLS_MENU_MODEL_H_
-
-#import <Foundation/Foundation.h>
-
-// New Tab item accessibility Identifier.
-extern NSString* const kToolsMenuNewTabId;
-// New incognito Tab item accessibility Identifier.
-extern NSString* const kToolsMenuNewIncognitoTabId;
-// Close all Tabs item accessibility Identifier.
-extern NSString* const kToolsMenuCloseAllTabsId;
-// Close all incognito Tabs item accessibility Identifier.
-extern NSString* const kToolsMenuCloseAllIncognitoTabsId;
-// Bookmarks item accessibility Identifier.
-extern NSString* const kToolsMenuBookmarksId;
-// Reading List item accessibility Identifier.
-extern NSString* const kToolsMenuReadingListId;
-// Other Devices item accessibility Identifier.
-extern NSString* const kToolsMenuOtherDevicesId;
-// History item accessibility Identifier.
-extern NSString* const kToolsMenuHistoryId;
-// Report an issue item accessibility Identifier.
-extern NSString* const kToolsMenuReportAnIssueId;
-// Find in Page item accessibility Identifier.
-extern NSString* const kToolsMenuFindInPageId;
-// Reader Mode item accessibility Identifier.
-extern NSString* const kToolsMenuReaderMode;
-// Request desktop item accessibility Identifier.
-extern NSString* const kToolsMenuRequestDesktopId;
-// Settings item accessibility Identifier.
-extern NSString* const kToolsMenuSettingsId;
-// Help item accessibility Identifier.
-extern NSString* const kToolsMenuHelpId;
-// Suggestions item accessibility Identifier.
-extern NSString* const kToolsMenuSuggestionsId;
-
-// Total number of possible menu items.
-const int kToolsMenuNumberOfItems = 15;
-
-// Initialization table for all possible commands to initialize the
-// tools menu at run time. Data initialized into this structure is not mutable.
-struct MenuItemInfo {
-  int title_id;
-  NSString* accessibility_id;
-  int command_id;
-  int toolbar_types;
-  // |visibility| is applied if a menu item is included for a given
-  // |toolbar_types|. A value of 0 means the menu item is always visible for
-  // the given |toolbar_types|.
-  int visibility;
-  // Custom class, if any, for the menu item, or |nil|.
-  Class item_class;
-};
-
-// Flags for different toolbar types
-typedef NS_OPTIONS(NSUInteger, ToolbarType) {
-  // clang-format off
-  ToolbarTypeNone            = 0,
-  ToolbarTypeWebiPhone       = 1 << 0,
-  ToolbarTypeWebiPad         = 1 << 1,
-  ToolbarTypeNoTabsiPad      = 1 << 2,
-  ToolbarTypeSwitcheriPhone  = 1 << 3,
-  ToolbarTypeWebAll          = ToolbarTypeWebiPhone | ToolbarTypeWebiPad,
-  ToolbarTypeAll             = ToolbarTypeWebAll |
-                               ToolbarTypeSwitcheriPhone |
-                               ToolbarTypeNoTabsiPad,
-  // clang-format on
-};
-
-// All possible items.
-extern const MenuItemInfo itemInfoList[kToolsMenuNumberOfItems];
-
-// Returns true if a given item should be visible based on the Toolbar type
-// and if incognito mode or not.
-bool ToolsMenuItemShouldBeVisible(const MenuItemInfo& item,
-                                  bool incognito,
-                                  ToolbarType toolbarType);
-
-#endif  // IOS_CHROME_BROWSER_UI_TOOLS_MENU_TOOLS_MENU_MODEL_H_
diff --git a/ios/chrome/browser/ui/tools_menu/tools_menu_model.mm b/ios/chrome/browser/ui/tools_menu/tools_menu_model.mm
deleted file mode 100644
index 6556f3ad..0000000
--- a/ios/chrome/browser/ui/tools_menu/tools_menu_model.mm
+++ /dev/null
@@ -1,119 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "ios/chrome/browser/ui/tools_menu/tools_menu_model.h"
-
-#include "components/reading_list/core/reading_list_switches.h"
-#include "components/strings/grit/components_strings.h"
-#include "ios/chrome/browser/experimental_flags.h"
-#include "ios/chrome/browser/ui/commands/ios_command_ids.h"
-#import "ios/chrome/browser/ui/tools_menu/reading_list_menu_view_item.h"
-#include "ios/chrome/browser/ui/ui_util.h"
-#include "ios/chrome/grit/ios_strings.h"
-#include "ios/public/provider/chrome/browser/chrome_browser_provider.h"
-#import "ios/public/provider/chrome/browser/user_feedback/user_feedback_provider.h"
-
-NSString* const kToolsMenuNewTabId = @"kToolsMenuNewTabId";
-NSString* const kToolsMenuNewIncognitoTabId = @"kToolsMenuNewIncognitoTabId";
-NSString* const kToolsMenuCloseAllTabsId = @"kToolsMenuCloseAllTabsId";
-NSString* const kToolsMenuCloseAllIncognitoTabsId =
-    @"kToolsMenuCloseAllIncognitoTabsId";
-NSString* const kToolsMenuBookmarksId = @"kToolsMenuBookmarksId";
-NSString* const kToolsMenuReadingListId = @"kToolsMenuReadingListId";
-NSString* const kToolsMenuOtherDevicesId = @"kToolsMenuOtherDevicesId";
-NSString* const kToolsMenuHistoryId = @"kToolsMenuHistoryId";
-NSString* const kToolsMenuReportAnIssueId = @"kToolsMenuReportAnIssueId";
-NSString* const kToolsMenuFindInPageId = @"kToolsMenuFindInPageId";
-NSString* const kToolsMenuReaderMode = @"kToolsMenuReaderMode";
-NSString* const kToolsMenuRequestDesktopId = @"kToolsMenuRequestDesktopId";
-NSString* const kToolsMenuSettingsId = @"kToolsMenuSettingsId";
-NSString* const kToolsMenuHelpId = @"kToolsMenuHelpId";
-NSString* const kToolsMenuSuggestionsId = @"kToolsMenuSuggestionsId";
-
-// Menu items can be marked as visible or not when Incognito is enabled.
-// The following bits are used for |visibility| field in |MenuItemInfo|.
-const NSInteger kVisibleIncognitoOnly = 1 << 0;
-const NSInteger kVisibleNotIncognitoOnly = 1 << 1;
-
-// Declare all the possible items.
-const MenuItemInfo itemInfoList[] = {
-    // clang-format off
-  { IDS_IOS_TOOLS_MENU_NEW_TAB,           kToolsMenuNewTabId,
-    IDC_NEW_TAB,                          ToolbarTypeAll,
-    0,                                    nil },
-  { IDS_IOS_TOOLS_MENU_NEW_INCOGNITO_TAB, kToolsMenuNewIncognitoTabId,
-    IDC_NEW_INCOGNITO_TAB,                ToolbarTypeAll,
-    0,                                    nil },
-  { IDS_IOS_TOOLS_MENU_CLOSE_ALL_TABS,    kToolsMenuCloseAllTabsId,
-    IDC_CLOSE_ALL_TABS,                   ToolbarTypeSwitcheriPhone,
-    kVisibleNotIncognitoOnly,             nil },
-  { IDS_IOS_TOOLS_MENU_CLOSE_ALL_INCOGNITO_TABS,
-    kToolsMenuCloseAllIncognitoTabsId,
-    IDC_CLOSE_ALL_INCOGNITO_TABS,         ToolbarTypeSwitcheriPhone,
-    kVisibleIncognitoOnly,                nil },
-  { IDS_IOS_TOOLS_MENU_BOOKMARKS,         kToolsMenuBookmarksId,
-    IDC_SHOW_BOOKMARK_MANAGER,            ToolbarTypeWebAll,
-    0,                                    nil },
-  { IDS_IOS_TOOLS_MENU_READING_LIST,      kToolsMenuReadingListId,
-    IDC_SHOW_READING_LIST,                ToolbarTypeWebAll,
-    0,                                    [ReadingListMenuViewItem class] },
-  { IDS_IOS_TOOLS_MENU_SUGGESTIONS,       kToolsMenuSuggestionsId,
-    IDC_SHOW_SUGGESTIONS,                 ToolbarTypeWebAll,
-    0,                                    nil },
-  { IDS_IOS_TOOLS_MENU_RECENT_TABS,       kToolsMenuOtherDevicesId,
-    IDC_SHOW_OTHER_DEVICES,               ToolbarTypeWebAll,
-    kVisibleNotIncognitoOnly,             nil },
-  { IDS_HISTORY_SHOW_HISTORY,             kToolsMenuHistoryId,
-    IDC_SHOW_HISTORY,                     ToolbarTypeWebAll,
-    0,                                    nil },
-  { IDS_IOS_OPTIONS_REPORT_AN_ISSUE,      kToolsMenuReportAnIssueId,
-    IDC_REPORT_AN_ISSUE,                  ToolbarTypeAll,
-    0,                                    nil },
-  { IDS_IOS_TOOLS_MENU_FIND_IN_PAGE,      kToolsMenuFindInPageId,
-    IDC_FIND,                             ToolbarTypeWebAll,
-    0,                                    nil },
-  { IDS_IOS_TOOLS_MENU_REQUEST_DESKTOP_SITE, kToolsMenuRequestDesktopId,
-    IDC_REQUEST_DESKTOP_SITE,             ToolbarTypeWebAll,
-    0,                                    nil },
-  { IDS_IOS_TOOLS_MENU_READER_MODE,       kToolsMenuReaderMode,
-    IDC_READER_MODE,                      ToolbarTypeWebAll,
-    0,                                    nil },
-  { IDS_IOS_TOOLS_MENU_SETTINGS,          kToolsMenuSettingsId,
-    IDC_OPTIONS,                          ToolbarTypeAll,
-    0,                                    nil },
-  { IDS_IOS_TOOLS_MENU_HELP_MOBILE,           kToolsMenuHelpId,
-    IDC_HELP_PAGE_VIA_MENU,               ToolbarTypeWebAll,
-    0,                                    nil },
-    // clang-format on
-};
-
-bool ToolsMenuItemShouldBeVisible(const MenuItemInfo& item,
-                                  bool incognito,
-                                  ToolbarType toolbarType) {
-  if (!(item.toolbar_types & toolbarType))
-    return false;
-
-  if (incognito && (item.visibility & kVisibleNotIncognitoOnly))
-    return false;
-
-  if (!incognito && (item.visibility & kVisibleIncognitoOnly))
-    return false;
-
-  switch (item.title_id) {
-    case IDS_IOS_TOOLBAR_SHOW_TABS:
-      return IsIPadIdiom();
-    case IDS_IOS_TOOLS_MENU_READER_MODE:
-      return experimental_flags::IsReaderModeEnabled();
-    case IDS_IOS_TOOLS_MENU_READING_LIST:
-      return reading_list::switches::IsReadingListEnabled();
-    case IDS_IOS_TOOLS_MENU_SUGGESTIONS:
-      return experimental_flags::IsSuggestionsUIEnabled();
-    case IDS_IOS_OPTIONS_REPORT_AN_ISSUE:
-      return !ios::GetChromeBrowserProvider()
-                  ->GetUserFeedbackProvider()
-                  ->IsUserFeedbackEnabled();
-    default:
-      return true;
-  }
-}
diff --git a/ios/chrome/browser/ui/tools_menu/tools_menu_view_controller.mm b/ios/chrome/browser/ui/tools_menu/tools_menu_view_controller.mm
index 34d8df6..e6157e4 100644
--- a/ios/chrome/browser/ui/tools_menu/tools_menu_view_controller.mm
+++ b/ios/chrome/browser/ui/tools_menu/tools_menu_view_controller.mm
@@ -12,6 +12,9 @@
 #include "base/mac/objc_property_releaser.h"
 #include "base/mac/scoped_nsobject.h"
 #include "base/metrics/field_trial.h"
+#include "components/reading_list/core/reading_list_switches.h"
+#include "components/strings/grit/components_strings.h"
+#include "ios/chrome/browser/experimental_flags.h"
 #import "ios/chrome/browser/ui/animation_util.h"
 #import "ios/chrome/browser/ui/commands/UIKit+ChromeExecuteCommand.h"
 #include "ios/chrome/browser/ui/commands/ios_command_ids.h"
@@ -19,19 +22,38 @@
 #import "ios/chrome/browser/ui/reading_list/reading_list_menu_notifier.h"
 #import "ios/chrome/browser/ui/tools_menu/reading_list_menu_view_item.h"
 #import "ios/chrome/browser/ui/tools_menu/tools_menu_context.h"
-#include "ios/chrome/browser/ui/tools_menu/tools_menu_model.h"
 #import "ios/chrome/browser/ui/tools_menu/tools_menu_view_item.h"
 #import "ios/chrome/browser/ui/tools_menu/tools_menu_view_tools_cell.h"
 #import "ios/chrome/browser/ui/tools_menu/tools_popup_controller.h"
+#include "ios/chrome/browser/ui/ui_util.h"
 #import "ios/chrome/browser/ui/uikit_ui_util.h"
 #import "ios/chrome/common/material_timing.h"
 #include "ios/chrome/grit/ios_strings.h"
+#include "ios/public/provider/chrome/browser/chrome_browser_provider.h"
+#import "ios/public/provider/chrome/browser/user_feedback/user_feedback_provider.h"
 #import "ios/third_party/material_components_ios/src/components/Ink/src/MaterialInk.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/base/l10n/l10n_util_mac.h"
 
 using ios::material::TimingFunction;
 
+NSString* const kToolsMenuNewTabId = @"kToolsMenuNewTabId";
+NSString* const kToolsMenuNewIncognitoTabId = @"kToolsMenuNewIncognitoTabId";
+NSString* const kToolsMenuCloseAllTabsId = @"kToolsMenuCloseAllTabsId";
+NSString* const kToolsMenuCloseAllIncognitoTabsId =
+    @"kToolsMenuCloseAllIncognitoTabsId";
+NSString* const kToolsMenuBookmarksId = @"kToolsMenuBookmarksId";
+NSString* const kToolsMenuReadingListId = @"kToolsMenuReadingListId";
+NSString* const kToolsMenuOtherDevicesId = @"kToolsMenuOtherDevicesId";
+NSString* const kToolsMenuHistoryId = @"kToolsMenuHistoryId";
+NSString* const kToolsMenuReportAnIssueId = @"kToolsMenuReportAnIssueId";
+NSString* const kToolsMenuFindInPageId = @"kToolsMenuFindInPageId";
+NSString* const kToolsMenuReaderMode = @"kToolsMenuReaderMode";
+NSString* const kToolsMenuRequestDesktopId = @"kToolsMenuRequestDesktopId";
+NSString* const kToolsMenuSettingsId = @"kToolsMenuSettingsId";
+NSString* const kToolsMenuHelpId = @"kToolsMenuHelpId";
+NSString* const kToolsMenuSuggestionsId = @"kToolsMenuSuggestionsId";
+
 namespace {
 
 // Time for ink to fade into view.
@@ -41,6 +63,140 @@
 
 static NSString* const kToolsItemCellID = @"ToolsItemCellID";
 
+// Menu items can be marked as visible or not when Incognito is enabled.
+// The following bits are used for |visibility| field in |MenuItemInfo|.
+const NSInteger kVisibleIncognitoOnly = 1 << 0;
+const NSInteger kVisibleNotIncognitoOnly = 1 << 1;
+
+// Initialization table for all possible commands to initialize the
+// tools menu at run time. Data initialized into this structure is not mutable.
+struct MenuItemInfo {
+  int title_id;
+  NSString* accessibility_id;
+  int command_id;
+  int toolbar_types;
+  // |visibility| is applied if a menu item is included for a given
+  // |toolbar_types|. A value of 0 means the menu item is always visible for
+  // the given |toolbar_types|.
+  int visibility;
+  // Custom class, if any, for the menu item, or |nil|.
+  Class item_class;
+};
+
+// Flags for different toolbar types
+typedef NS_OPTIONS(NSUInteger, kToolbarType) {
+  // clang-format off
+    kToolbarTypeNone            = 0,
+    kToolbarTypeWebiPhone       = 1 << 0,
+    kToolbarTypeWebiPad         = 1 << 1,
+    kToolbarTypeNoTabsiPad      = 1 << 2,
+    kToolbarTypeSwitcheriPhone  = 1 << 3,
+    kToolbarTypeWebAll          = kToolbarTypeWebiPhone | kToolbarTypeWebiPad,
+    kToolbarTypeAll             = kToolbarTypeWebAll |
+                                  kToolbarTypeSwitcheriPhone |
+                                  kToolbarTypeNoTabsiPad,
+  // clang-format on
+};
+
+// Declare all the possible items.
+static MenuItemInfo itemInfoList[] = {
+    // clang-format off
+  { IDS_IOS_TOOLS_MENU_NEW_TAB,           kToolsMenuNewTabId,
+    IDC_NEW_TAB,                          kToolbarTypeAll,
+    0,                                    nil },
+  { IDS_IOS_TOOLS_MENU_NEW_INCOGNITO_TAB, kToolsMenuNewIncognitoTabId,
+    IDC_NEW_INCOGNITO_TAB,                kToolbarTypeAll,
+    0,                                    nil },
+  { IDS_IOS_TOOLS_MENU_CLOSE_ALL_TABS,    kToolsMenuCloseAllTabsId,
+    IDC_CLOSE_ALL_TABS,                   kToolbarTypeSwitcheriPhone,
+    kVisibleNotIncognitoOnly,             nil },
+  { IDS_IOS_TOOLS_MENU_CLOSE_ALL_INCOGNITO_TABS,
+    kToolsMenuCloseAllIncognitoTabsId,
+    IDC_CLOSE_ALL_INCOGNITO_TABS,         kToolbarTypeSwitcheriPhone,
+    kVisibleIncognitoOnly,                nil },
+  { IDS_IOS_TOOLS_MENU_BOOKMARKS,         kToolsMenuBookmarksId,
+    IDC_SHOW_BOOKMARK_MANAGER,            kToolbarTypeWebAll,
+    0,                                    nil },
+  { IDS_IOS_TOOLS_MENU_READING_LIST,      kToolsMenuReadingListId,
+    IDC_SHOW_READING_LIST,                kToolbarTypeWebAll,
+    0,                                    [ReadingListMenuViewItem class] },
+  { IDS_IOS_TOOLS_MENU_SUGGESTIONS,       kToolsMenuSuggestionsId,
+    IDC_SHOW_SUGGESTIONS,                 kToolbarTypeWebAll,
+    0,                                    nil },
+  { IDS_IOS_TOOLS_MENU_RECENT_TABS,       kToolsMenuOtherDevicesId,
+    IDC_SHOW_OTHER_DEVICES,               kToolbarTypeWebAll,
+    kVisibleNotIncognitoOnly,             nil },
+  { IDS_HISTORY_SHOW_HISTORY,             kToolsMenuHistoryId,
+    IDC_SHOW_HISTORY,                     kToolbarTypeWebAll,
+    0,                                    nil },
+  { IDS_IOS_OPTIONS_REPORT_AN_ISSUE,      kToolsMenuReportAnIssueId,
+    IDC_REPORT_AN_ISSUE,                  kToolbarTypeAll,
+    0,                                    nil },
+  { IDS_IOS_TOOLS_MENU_FIND_IN_PAGE,      kToolsMenuFindInPageId,
+    IDC_FIND,                             kToolbarTypeWebAll,
+    0,                                    nil },
+  { IDS_IOS_TOOLS_MENU_REQUEST_DESKTOP_SITE, kToolsMenuRequestDesktopId,
+    IDC_REQUEST_DESKTOP_SITE,             kToolbarTypeWebAll,
+    0,                                    nil },
+  { IDS_IOS_TOOLS_MENU_READER_MODE,       kToolsMenuReaderMode,
+    IDC_READER_MODE,                      kToolbarTypeWebAll,
+    0,                                    nil },
+  { IDS_IOS_TOOLS_MENU_SETTINGS,          kToolsMenuSettingsId,
+    IDC_OPTIONS,                          kToolbarTypeAll,
+    0,                                    nil },
+  { IDS_IOS_TOOLS_MENU_HELP_MOBILE,           kToolsMenuHelpId,
+    IDC_HELP_PAGE_VIA_MENU,               kToolbarTypeWebAll,
+    0,                                    nil },
+    // clang-format on
+};
+
+NS_INLINE BOOL ItemShouldBeVisible(const MenuItemInfo& item,
+                                   BOOL incognito,
+                                   kToolbarType toolbarType) {
+  if (!(item.toolbar_types & toolbarType))
+    return NO;
+
+  if (incognito && (item.visibility & kVisibleNotIncognitoOnly))
+    return NO;
+
+  if (!incognito && (item.visibility & kVisibleIncognitoOnly))
+    return NO;
+
+  if (item.title_id == IDS_IOS_TOOLBAR_SHOW_TABS) {
+    if (!IsIPadIdiom()) {
+      return NO;
+    }
+  }
+
+  if (item.title_id == IDS_IOS_TOOLS_MENU_READER_MODE) {
+    if (!experimental_flags::IsReaderModeEnabled()) {
+      return NO;
+    }
+  }
+
+  if (item.title_id == IDS_IOS_TOOLS_MENU_READING_LIST) {
+    if (!reading_list::switches::IsReadingListEnabled()) {
+      return NO;
+    }
+  }
+
+  if (item.title_id == IDS_IOS_TOOLS_MENU_SUGGESTIONS) {
+    if (!experimental_flags::IsSuggestionsUIEnabled()) {
+      return NO;
+    }
+  }
+
+  if (item.title_id == IDS_IOS_OPTIONS_REPORT_AN_ISSUE) {
+    if (!ios::GetChromeBrowserProvider()
+             ->GetUserFeedbackProvider()
+             ->IsUserFeedbackEnabled()) {
+      return NO;
+    }
+  }
+
+  return YES;
+}
+
 NS_INLINE void AnimateInViews(NSArray* views,
                               CGFloat initialX,
                               CGFloat initialY) {
@@ -106,7 +262,7 @@
 @property(nonatomic, retain) ToolsMenuCollectionView* menuView;
 @property(nonatomic, retain) MDCInkView* touchFeedbackView;
 @property(nonatomic, retain) NSMutableArray* menuItems;
-@property(nonatomic, assign) ToolbarType toolbarType;
+@property(nonatomic, assign) kToolbarType toolbarType;
 
 // Get the reading list cell.
 - (ReadingListMenuViewCell*)readingListCell;
@@ -126,7 +282,7 @@
 
 - (CGFloat)optimalHeight:(CGFloat)suggestedHeight {
   NSInteger numberOfItems = [self.menuItems count];
-  if (_toolbarType == ToolbarTypeWebiPhone) {
+  if (_toolbarType == kToolbarTypeWebiPhone) {
     // Account for the height of the first row, not included in |menuItems|.
     numberOfItems++;
   }
@@ -212,16 +368,16 @@
   }
 
   if (IsIPadIdiom()) {
-    _toolbarType =
-        context.hasNoOpenedTabs
-            ? ToolbarTypeNoTabsiPad
-            : (!IsCompactTablet() ? ToolbarTypeWebiPad : ToolbarTypeWebiPhone);
+    _toolbarType = context.hasNoOpenedTabs
+                       ? kToolbarTypeNoTabsiPad
+                       : (!IsCompactTablet() ? kToolbarTypeWebiPad
+                                             : kToolbarTypeWebiPhone);
   } else {
     // kOptionInTabSwitcher option must be enabled on iPhone with
     // no opened tabs.
     DCHECK(!context.hasNoOpenedTabs || context.isInTabSwitcher);
-    _toolbarType = context.isInTabSwitcher ? ToolbarTypeSwitcheriPhone
-                                           : ToolbarTypeWebiPhone;
+    _toolbarType = context.isInTabSwitcher ? kToolbarTypeSwitcheriPhone
+                                           : kToolbarTypeWebiPhone;
   }
 
   // Build the menu, adding all relevant items.
@@ -229,8 +385,7 @@
 
   for (size_t i = 0; i < arraysize(itemInfoList); ++i) {
     const MenuItemInfo& item = itemInfoList[i];
-    if (!ToolsMenuItemShouldBeVisible(item, context.isInIncognito,
-                                      _toolbarType))
+    if (!ItemShouldBeVisible(item, context.isInIncognito, _toolbarType))
       continue;
 
     NSString* title = l10n_util::GetNSStringWithFixup(item.title_id);
@@ -247,7 +402,7 @@
 
 #if !defined(NDEBUG)
   NSUserDefaults* standardDefaults = [NSUserDefaults standardUserDefaults];
-  if ((_toolbarType & ToolbarTypeWebAll) &&
+  if ((_toolbarType & kToolbarTypeWebAll) &&
       [standardDefaults boolForKey:@"DevViewSource"]) {
     // Debug menu, not localized, only visible if turned on by a default.
     [menu addObject:[self createViewSourceItem]];
@@ -293,7 +448,7 @@
 - (NSInteger)dataIndexForIndexPath:(NSIndexPath*)path {
   NSInteger item = [path item];
 
-  if (_toolbarType == ToolbarTypeWebiPhone)
+  if (_toolbarType == kToolbarTypeWebiPhone)
     --item;
 
   return item;
@@ -403,7 +558,7 @@
       }];
 
   ToolsMenuViewToolsCell* toolsCell = nil;
-  if (_toolbarType == ToolbarTypeWebiPhone) {
+  if (_toolbarType == kToolbarTypeWebiPhone) {
     toolsCell = [visibleCells firstObject];
     if ([toolsCell isKindOfClass:[ToolsMenuViewToolsCell class]]) {
       visibleCells = [visibleCells
@@ -543,7 +698,7 @@
 - (NSInteger)collectionView:(UICollectionView*)view
      numberOfItemsInSection:(NSInteger)section {
   NSInteger numberOfItems = [_menuItems count];
-  if (_toolbarType == ToolbarTypeWebiPhone)
+  if (_toolbarType == kToolbarTypeWebiPhone)
     ++numberOfItems;
 
   return numberOfItems;
diff --git a/media/capture/video/fake_video_capture_device.cc b/media/capture/video/fake_video_capture_device.cc
index ba59bb4..7e46f68 100644
--- a/media/capture/video/fake_video_capture_device.cc
+++ b/media/capture/video/fake_video_capture_device.cc
@@ -23,6 +23,7 @@
 #include "third_party/skia/include/core/SkCanvas.h"
 #include "third_party/skia/include/core/SkMatrix.h"
 #include "third_party/skia/include/core/SkPaint.h"
+#include "ui/gfx/codec/jpeg_codec.h"
 #include "ui/gfx/codec/png_codec.h"
 
 namespace media {
@@ -46,9 +47,6 @@
 static const int kSupportedSizesCount =
     arraysize(kSupportedSizesOrderedByIncreasingWidth);
 
-static const VideoPixelFormat kSupportedPixelFormats[] = {
-    PIXEL_FORMAT_I420, PIXEL_FORMAT_Y16, PIXEL_FORMAT_ARGB};
-
 static gfx::Size SnapToSupportedSize(const gfx::Size& requested_size) {
   for (const gfx::Size& supported_size :
        kSupportedSizesOrderedByIncreasingWidth) {
@@ -74,11 +72,8 @@
 // as a frame count and timer.
 class PacmanFramePainter {
  public:
-  // Currently, only the following values are supported for |pixel_format|:
-  //   PIXEL_FORMAT_I420
-  //   PIXEL_FORMAT_Y16
-  //   PIXEL_FORMAT_ARGB
-  PacmanFramePainter(VideoPixelFormat pixel_format,
+  enum class Format { I420, SK_N32, Y16 };
+  PacmanFramePainter(Format pixel_format,
                      const FakeDeviceState* fake_device_state);
 
   void PaintFrame(base::TimeDelta elapsed_time, uint8_t* target_buffer);
@@ -89,7 +84,7 @@
 
   void DrawPacman(base::TimeDelta elapsed_time, uint8_t* target_buffer);
 
-  const VideoPixelFormat pixel_format_;
+  const Format pixel_format_;
   const FakeDeviceState* fake_device_state_ = nullptr;
 };
 
@@ -101,8 +96,14 @@
   virtual ~FrameDeliverer() {}
   virtual void Initialize(VideoPixelFormat pixel_format,
                           std::unique_ptr<VideoCaptureDevice::Client> client,
-                          const FakeDeviceState* device_state) = 0;
-  virtual void Uninitialize() = 0;
+                          const FakeDeviceState* device_state) {
+    client_ = std::move(client);
+    device_state_ = device_state;
+  }
+  virtual void Uninitialize() {
+    client_.reset();
+    device_state_ = nullptr;
+  }
   virtual void PaintAndDeliverNextFrame(base::TimeDelta timestamp_to_paint) = 0;
 
  protected:
@@ -112,11 +113,14 @@
     return now - first_ref_time_;
   }
 
+  PacmanFramePainter* frame_painter() { return frame_painter_.get(); }
+  const FakeDeviceState* device_state() { return device_state_; }
+  VideoCaptureDevice::Client* client() { return client_.get(); }
+
+ private:
   const std::unique_ptr<PacmanFramePainter> frame_painter_;
   const FakeDeviceState* device_state_ = nullptr;
   std::unique_ptr<VideoCaptureDevice::Client> client_;
-
- private:
   base::TimeTicks first_ref_time_;
 };
 
@@ -145,17 +149,27 @@
   ~ClientBufferFrameDeliverer() override;
 
   // Implementation of FrameDeliverer
-  void Initialize(VideoPixelFormat pixel_format,
-                  std::unique_ptr<VideoCaptureDevice::Client> client,
-                  const FakeDeviceState* device_state) override;
+  void PaintAndDeliverNextFrame(base::TimeDelta timestamp_to_paint) override;
+};
+
+class JpegEncodingFrameDeliverer : public FrameDeliverer {
+ public:
+  JpegEncodingFrameDeliverer(std::unique_ptr<PacmanFramePainter> frame_painter);
+  ~JpegEncodingFrameDeliverer() override;
+
+  // Implementation of FrameDeliveryStrategy
   void Uninitialize() override;
   void PaintAndDeliverNextFrame(base::TimeDelta timestamp_to_paint) override;
+
+ private:
+  std::vector<uint8_t> sk_n32_buffer_;
+  std::vector<unsigned char> jpeg_buffer_;
 };
 
 // Implements the photo functionality of a VideoCaptureDevice
 class FakePhotoDevice {
  public:
-  FakePhotoDevice(std::unique_ptr<PacmanFramePainter> argb_painter,
+  FakePhotoDevice(std::unique_ptr<PacmanFramePainter> painter,
                   const FakeDeviceState* fake_device_state);
   ~FakePhotoDevice();
 
@@ -165,7 +179,7 @@
                  base::TimeDelta elapsed_time);
 
  private:
-  const std::unique_ptr<PacmanFramePainter> argb_painter_;
+  const std::unique_ptr<PacmanFramePainter> painter_;
   const FakeDeviceState* const fake_device_state_;
 };
 
@@ -224,40 +238,51 @@
 
 // static
 std::unique_ptr<VideoCaptureDevice> FakeVideoCaptureDeviceMaker::MakeInstance(
-    VideoPixelFormat pixel_format,
+    PixelFormat pixel_format,
     DeliveryMode delivery_mode,
     float frame_rate) {
-  bool pixel_format_supported = false;
-  for (const auto& supported_pixel_format : kSupportedPixelFormats) {
-    if (pixel_format == supported_pixel_format) {
-      pixel_format_supported = true;
+  auto device_state = base::MakeUnique<FakeDeviceState>(
+      kInitialZoom, frame_rate,
+      static_cast<media::VideoPixelFormat>(pixel_format));
+  PacmanFramePainter::Format painter_format;
+  switch (pixel_format) {
+    case PixelFormat::I420:
+      painter_format = PacmanFramePainter::Format::I420;
       break;
-    }
+    case PixelFormat::Y16:
+      painter_format = PacmanFramePainter::Format::Y16;
+      break;
+    case PixelFormat::MJPEG:
+      painter_format = PacmanFramePainter::Format::SK_N32;
+      break;
   }
-  if (!pixel_format_supported) {
-    DLOG(ERROR) << "Requested an unsupported pixel format "
-                << VideoPixelFormatToString(pixel_format);
-    return nullptr;
-  }
-
-  auto device_state =
-      base::MakeUnique<FakeDeviceState>(kInitialZoom, frame_rate, pixel_format);
   auto video_frame_painter =
-      base::MakeUnique<PacmanFramePainter>(pixel_format, device_state.get());
+      base::MakeUnique<PacmanFramePainter>(painter_format, device_state.get());
+
   std::unique_ptr<FrameDeliverer> frame_delivery_strategy;
   switch (delivery_mode) {
     case DeliveryMode::USE_DEVICE_INTERNAL_BUFFERS:
-      frame_delivery_strategy = base::MakeUnique<OwnBufferFrameDeliverer>(
-          std::move(video_frame_painter));
+      if (pixel_format == PixelFormat::MJPEG) {
+        frame_delivery_strategy = base::MakeUnique<JpegEncodingFrameDeliverer>(
+            std::move(video_frame_painter));
+      } else {
+        frame_delivery_strategy = base::MakeUnique<OwnBufferFrameDeliverer>(
+            std::move(video_frame_painter));
+      }
       break;
     case DeliveryMode::USE_CLIENT_PROVIDED_BUFFERS:
+      if (pixel_format == PixelFormat::MJPEG) {
+        DLOG(ERROR) << "PixelFormat::MJPEG cannot be used in combination with "
+                    << "USE_CLIENT_PROVIDED_BUFFERS.";
+        return nullptr;
+      }
       frame_delivery_strategy = base::MakeUnique<ClientBufferFrameDeliverer>(
           std::move(video_frame_painter));
       break;
   }
 
   auto photo_frame_painter = base::MakeUnique<PacmanFramePainter>(
-      PIXEL_FORMAT_ARGB, device_state.get());
+      PacmanFramePainter::Format::SK_N32, device_state.get());
   auto photo_device = base::MakeUnique<FakePhotoDevice>(
       std::move(photo_frame_painter), device_state.get());
 
@@ -266,12 +291,9 @@
       std::move(device_state));
 }
 
-PacmanFramePainter::PacmanFramePainter(VideoPixelFormat pixel_format,
+PacmanFramePainter::PacmanFramePainter(Format pixel_format,
                                        const FakeDeviceState* fake_device_state)
-    : pixel_format_(pixel_format), fake_device_state_(fake_device_state) {
-  DCHECK(pixel_format == PIXEL_FORMAT_I420 ||
-         pixel_format == PIXEL_FORMAT_Y16 || pixel_format == PIXEL_FORMAT_ARGB);
-}
+    : pixel_format_(pixel_format), fake_device_state_(fake_device_state) {}
 
 void PacmanFramePainter::PaintFrame(base::TimeDelta elapsed_time,
                                     uint8_t* target_buffer) {
@@ -305,16 +327,16 @@
             static_cast<unsigned int>(start + (x + y) * color_step) & 0xFFFF;
         size_t offset = (y * width) + x;
         switch (pixel_format_) {
-          case PIXEL_FORMAT_Y16:
+          case Format::Y16:
             target_buffer[offset * sizeof(uint16_t)] = value & 0xFF;
             target_buffer[offset * sizeof(uint16_t) + 1] = value >> 8;
             break;
-          case PIXEL_FORMAT_ARGB:
+          case Format::SK_N32:
             target_buffer[offset * sizeof(uint32_t) + 1] = value >> 8;
             target_buffer[offset * sizeof(uint32_t) + 2] = value >> 8;
             target_buffer[offset * sizeof(uint32_t) + 3] = value >> 8;
             break;
-          default:
+          case Format::I420:
             target_buffer[offset] = value >> 8;
             break;
         }
@@ -328,12 +350,30 @@
   const int width = fake_device_state_->format.frame_size.width();
   const int height = fake_device_state_->format.frame_size.height();
 
-  // |kN32_SkColorType| stands for the appropriate RGBA/BGRA format.
-  const SkColorType colorspace = (pixel_format_ == PIXEL_FORMAT_ARGB)
-                                     ? kN32_SkColorType
-                                     : kAlpha_8_SkColorType;
-  // Skia doesn't support 16 bit alpha rendering, so we 8 bit alpha and then use
-  // this as high byte values in 16 bit pixels.
+  SkColorType colorspace = kAlpha_8_SkColorType;
+  switch (pixel_format_) {
+    case Format::I420:
+      // Skia doesn't support painting in I420. Instead, paint an 8bpp
+      // monochrome image to the beginning of |target_buffer|. This section of
+      // |target_buffer| corresponds to the Y-plane of the YUV image. Do not
+      // touch the U or V planes of |target_buffer|. Assuming they have been
+      // initialized to 0, which corresponds to a green color tone, the result
+      // will be an green-ish monochrome frame.
+      colorspace = kAlpha_8_SkColorType;
+      break;
+    case Format::SK_N32:
+      // SkColorType is RGBA on some platforms and BGRA on others.
+      colorspace = kN32_SkColorType;
+      break;
+    case Format::Y16:
+      // Skia doesn't support painting in Y16. Instead, paint an 8bpp monochrome
+      // image to the beginning of |target_buffer|. Later, move the 8bit pixel
+      // values to a position corresponding to the high byte values of 16bit
+      // pixel values (assuming the byte order is little-endian).
+      colorspace = kAlpha_8_SkColorType;
+      break;
+  }
+
   const SkImageInfo info =
       SkImageInfo::Make(width, height, colorspace, kOpaque_SkAlphaType);
   SkBitmap bitmap;
@@ -348,13 +388,14 @@
   matrix.setScale(unscaled_zoom, unscaled_zoom, width / 2, height / 2);
   canvas.setMatrix(matrix);
 
-  // Equalize Alpha_8 that has light green background while RGBA has white.
-  if (pixel_format_ == PIXEL_FORMAT_ARGB) {
+  // For the SK_N32 case, match the green color tone produced by the
+  // I420 case.
+  if (pixel_format_ == Format::SK_N32) {
     const SkRect full_frame = SkRect::MakeWH(width, height);
     paint.setARGB(255, 0, 127, 0);
     canvas.drawRect(full_frame, paint);
+    paint.setColor(SK_ColorGREEN);
   }
-  paint.setColor(SK_ColorGREEN);
 
   // Draw a sweeping circle to show an animation.
   const float end_angle =
@@ -378,7 +419,7 @@
   canvas.scale(3, 3);
   canvas.drawText(time_string.data(), time_string.length(), 30, 20, paint);
 
-  if (pixel_format_ == PIXEL_FORMAT_Y16) {
+  if (pixel_format_ == Format::Y16) {
     // Use 8 bit bitmap rendered to first half of the buffer as high byte values
     // for the whole buffer. Low byte values are not important.
     for (int i = (width * height) - 1; i >= 0; --i)
@@ -386,27 +427,31 @@
   }
 }
 
-FakePhotoDevice::FakePhotoDevice(
-    std::unique_ptr<PacmanFramePainter> argb_painter,
-    const FakeDeviceState* fake_device_state)
-    : argb_painter_(std::move(argb_painter)),
-      fake_device_state_(fake_device_state) {}
+FakePhotoDevice::FakePhotoDevice(std::unique_ptr<PacmanFramePainter> painter,
+                                 const FakeDeviceState* fake_device_state)
+    : painter_(std::move(painter)), fake_device_state_(fake_device_state) {}
 
 FakePhotoDevice::~FakePhotoDevice() = default;
 
 void FakePhotoDevice::TakePhoto(VideoCaptureDevice::TakePhotoCallback callback,
                                 base::TimeDelta elapsed_time) {
   // Create a PNG-encoded frame and send it back to |callback|.
-  std::unique_ptr<uint8_t[]> buffer(new uint8_t[VideoFrame::AllocationSize(
-      PIXEL_FORMAT_ARGB, fake_device_state_->format.frame_size)]);
-  argb_painter_->PaintFrame(elapsed_time, buffer.get());
+  auto required_sk_n32_buffer_size = VideoFrame::AllocationSize(
+      PIXEL_FORMAT_ARGB, fake_device_state_->format.frame_size);
+  std::unique_ptr<uint8_t[]> buffer(new uint8_t[required_sk_n32_buffer_size]);
+  memset(buffer.get(), 0, required_sk_n32_buffer_size);
+  painter_->PaintFrame(elapsed_time, buffer.get());
   mojom::BlobPtr blob = mojom::Blob::New();
-  const bool result =
-      gfx::PNGCodec::Encode(buffer.get(), gfx::PNGCodec::FORMAT_RGBA,
-                            fake_device_state_->format.frame_size,
-                            fake_device_state_->format.frame_size.width() * 4,
-                            true /* discard_transparency */,
-                            std::vector<gfx::PNGCodec::Comment>(), &blob->data);
+  const gfx::PNGCodec::ColorFormat encoding_source_format =
+      (kN32_SkColorType == kRGBA_8888_SkColorType) ? gfx::PNGCodec::FORMAT_RGBA
+                                                   : gfx::PNGCodec::FORMAT_BGRA;
+  const bool result = gfx::PNGCodec::Encode(
+      buffer.get(), encoding_source_format,
+      fake_device_state_->format.frame_size,
+      VideoFrame::RowBytes(0 /* plane */, PIXEL_FORMAT_ARGB,
+                           fake_device_state_->format.frame_size.width()),
+      true /* discard_transparency */, std::vector<gfx::PNGCodec::Comment>(),
+      &blob->data);
   DCHECK(result);
 
   blob->mime_type = "image/png";
@@ -524,29 +569,27 @@
     VideoPixelFormat pixel_format,
     std::unique_ptr<VideoCaptureDevice::Client> client,
     const FakeDeviceState* device_state) {
-  client_ = std::move(client);
-  device_state_ = device_state;
+  FrameDeliverer::Initialize(pixel_format, std::move(client), device_state);
   buffer_.reset(new uint8_t[VideoFrame::AllocationSize(
-      pixel_format, device_state_->format.frame_size)]);
+      pixel_format, device_state->format.frame_size)]);
 }
 
 void OwnBufferFrameDeliverer::Uninitialize() {
-  client_.reset();
-  device_state_ = nullptr;
+  FrameDeliverer::Uninitialize();
   buffer_.reset();
 }
 
 void OwnBufferFrameDeliverer::PaintAndDeliverNextFrame(
     base::TimeDelta timestamp_to_paint) {
-  if (!client_)
+  if (!client())
     return;
-  const size_t frame_size = device_state_->format.ImageAllocationSize();
+  const size_t frame_size = device_state()->format.ImageAllocationSize();
   memset(buffer_.get(), 0, frame_size);
-  frame_painter_->PaintFrame(timestamp_to_paint, buffer_.get());
+  frame_painter()->PaintFrame(timestamp_to_paint, buffer_.get());
   base::TimeTicks now = base::TimeTicks::Now();
-  client_->OnIncomingCapturedData(buffer_.get(), frame_size,
-                                  device_state_->format, 0 /* rotation */, now,
-                                  CalculateTimeSinceFirstInvocation(now));
+  client()->OnIncomingCapturedData(buffer_.get(), frame_size,
+                                   device_state()->format, 0 /* rotation */,
+                                   now, CalculateTimeSinceFirstInvocation(now));
 }
 
 ClientBufferFrameDeliverer::ClientBufferFrameDeliverer(
@@ -555,44 +598,79 @@
 
 ClientBufferFrameDeliverer::~ClientBufferFrameDeliverer() = default;
 
-void ClientBufferFrameDeliverer::Initialize(
-    VideoPixelFormat,
-    std::unique_ptr<VideoCaptureDevice::Client> client,
-    const FakeDeviceState* device_state) {
-  client_ = std::move(client);
-  device_state_ = device_state;
-}
-
-void ClientBufferFrameDeliverer::Uninitialize() {
-  client_.reset();
-  device_state_ = nullptr;
-}
-
 void ClientBufferFrameDeliverer::PaintAndDeliverNextFrame(
     base::TimeDelta timestamp_to_paint) {
-  if (client_ == nullptr)
+  if (!client())
     return;
 
   const int arbitrary_frame_feedback_id = 0;
-  auto capture_buffer = client_->ReserveOutputBuffer(
-      device_state_->format.frame_size, device_state_->format.pixel_format,
-      device_state_->format.pixel_storage, arbitrary_frame_feedback_id);
+  auto capture_buffer = client()->ReserveOutputBuffer(
+      device_state()->format.frame_size, device_state()->format.pixel_format,
+      device_state()->format.pixel_storage, arbitrary_frame_feedback_id);
   DLOG_IF(ERROR, !capture_buffer.is_valid())
       << "Couldn't allocate Capture Buffer";
   auto buffer_access =
       capture_buffer.handle_provider->GetHandleForInProcessAccess();
   DCHECK(buffer_access->data()) << "Buffer has NO backing memory";
 
-  DCHECK_EQ(PIXEL_STORAGE_CPU, device_state_->format.pixel_storage);
+  DCHECK_EQ(PIXEL_STORAGE_CPU, device_state()->format.pixel_storage);
 
   uint8_t* data_ptr = buffer_access->data();
   memset(data_ptr, 0, buffer_access->mapped_size());
-  frame_painter_->PaintFrame(timestamp_to_paint, data_ptr);
+  frame_painter()->PaintFrame(timestamp_to_paint, data_ptr);
 
   base::TimeTicks now = base::TimeTicks::Now();
-  client_->OnIncomingCapturedBuffer(std::move(capture_buffer),
-                                    device_state_->format, now,
-                                    CalculateTimeSinceFirstInvocation(now));
+  client()->OnIncomingCapturedBuffer(std::move(capture_buffer),
+                                     device_state()->format, now,
+                                     CalculateTimeSinceFirstInvocation(now));
+}
+
+JpegEncodingFrameDeliverer::JpegEncodingFrameDeliverer(
+    std::unique_ptr<PacmanFramePainter> frame_painter)
+    : FrameDeliverer(std::move(frame_painter)) {}
+
+JpegEncodingFrameDeliverer::~JpegEncodingFrameDeliverer() = default;
+
+void JpegEncodingFrameDeliverer::Uninitialize() {
+  FrameDeliverer::Uninitialize();
+  sk_n32_buffer_.clear();
+  jpeg_buffer_.clear();
+}
+
+void JpegEncodingFrameDeliverer::PaintAndDeliverNextFrame(
+    base::TimeDelta timestamp_to_paint) {
+  if (!client())
+    return;
+
+  auto required_sk_n32_buffer_size = VideoFrame::AllocationSize(
+      PIXEL_FORMAT_ARGB, device_state()->format.frame_size);
+  sk_n32_buffer_.resize(required_sk_n32_buffer_size);
+  memset(&sk_n32_buffer_[0], 0, required_sk_n32_buffer_size);
+
+  frame_painter()->PaintFrame(timestamp_to_paint, &sk_n32_buffer_[0]);
+
+  static const int kQuality = 75;
+  const gfx::JPEGCodec::ColorFormat encoding_source_format =
+      (kN32_SkColorType == kRGBA_8888_SkColorType)
+          ? gfx::JPEGCodec::FORMAT_RGBA
+          : gfx::JPEGCodec::FORMAT_BGRA;
+  bool success = gfx::JPEGCodec::Encode(
+      &sk_n32_buffer_[0], encoding_source_format,
+      device_state()->format.frame_size.width(),
+      device_state()->format.frame_size.height(),
+      VideoFrame::RowBytes(0 /* plane */, PIXEL_FORMAT_ARGB,
+                           device_state()->format.frame_size.width()),
+      kQuality, &jpeg_buffer_);
+  if (!success) {
+    DLOG(ERROR) << "Jpeg encoding failed";
+    return;
+  }
+
+  const size_t frame_size = jpeg_buffer_.size();
+  base::TimeTicks now = base::TimeTicks::Now();
+  client()->OnIncomingCapturedData(&jpeg_buffer_[0], frame_size,
+                                   device_state()->format, 0 /* rotation */,
+                                   now, CalculateTimeSinceFirstInvocation(now));
 }
 
 void FakeVideoCaptureDevice::BeepAndScheduleNextCapture(
@@ -619,9 +697,10 @@
       std::max(current_time, expected_execution_time + frame_interval);
   const base::TimeDelta delay = next_execution_time - current_time;
   base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
-      FROM_HERE, base::Bind(&FakeVideoCaptureDevice::OnNextFrameDue,
-                            weak_factory_.GetWeakPtr(), next_execution_time,
-                            current_session_id_),
+      FROM_HERE,
+      base::Bind(&FakeVideoCaptureDevice::OnNextFrameDue,
+                 weak_factory_.GetWeakPtr(), next_execution_time,
+                 current_session_id_),
       delay);
 }
 
diff --git a/media/capture/video/fake_video_capture_device.h b/media/capture/video/fake_video_capture_device.h
index c853b4d..7919e9f6 100644
--- a/media/capture/video/fake_video_capture_device.h
+++ b/media/capture/video/fake_video_capture_device.h
@@ -18,6 +18,11 @@
 // on a given target OutputMode and frame rate.
 class CAPTURE_EXPORT FakeVideoCaptureDeviceMaker {
  public:
+  enum class PixelFormat {
+    I420 = media::PIXEL_FORMAT_I420,
+    Y16 = media::PIXEL_FORMAT_Y16,
+    MJPEG = media::PIXEL_FORMAT_MJPEG
+  };
   enum class DeliveryMode {
     USE_DEVICE_INTERNAL_BUFFERS,
     USE_CLIENT_PROVIDED_BUFFERS
@@ -26,7 +31,7 @@
   static void GetSupportedSizes(std::vector<gfx::Size>* supported_sizes);
 
   static std::unique_ptr<VideoCaptureDevice> MakeInstance(
-      VideoPixelFormat pixel_format,
+      PixelFormat pixel_format,
       DeliveryMode delivery_mode,
       float frame_rate);
 };
diff --git a/media/capture/video/fake_video_capture_device_factory.cc b/media/capture/video/fake_video_capture_device_factory.cc
index 49df8e6..5b850e64 100644
--- a/media/capture/video/fake_video_capture_device_factory.cc
+++ b/media/capture/video/fake_video_capture_device_factory.cc
@@ -13,19 +13,10 @@
 #include "build/build_config.h"
 #include "media/base/media_switches.h"
 
+namespace media {
 namespace {
 
 static const size_t kDepthDeviceIndex = 1;
-static const char kDepthDeviceId[] = "/dev/video1";
-
-media::VideoPixelFormat GetPixelFormatFromDeviceId(
-    const std::string& device_id) {
-  return (device_id == kDepthDeviceId) ? media::PIXEL_FORMAT_Y16
-                                       : media::PIXEL_FORMAT_I420;
-}
-}
-
-namespace media {
 
 // Cap the frame rate command line input to reasonable values.
 static const float kFakeCaptureMinFrameRate = 5.0f;
@@ -36,6 +27,17 @@
 static const int kFakeCaptureMinDeviceCount = 1;
 static const int kFakeCaptureMaxDeviceCount = 10;
 
+FakeVideoCaptureDeviceMaker::PixelFormat GetPixelFormatFromDeviceId(
+    const std::string& device_id) {
+  if (device_id == "/dev/video1")
+    return FakeVideoCaptureDeviceMaker::PixelFormat::Y16;
+  if (device_id == "/dev/video2")
+    return FakeVideoCaptureDeviceMaker::PixelFormat::MJPEG;
+  return FakeVideoCaptureDeviceMaker::PixelFormat::I420;
+}
+
+}  // anonymous namespace
+
 FakeVideoCaptureDeviceFactory::FakeVideoCaptureDeviceFactory()
     : number_of_devices_(1),
       delivery_mode_(FakeVideoCaptureDeviceMaker::DeliveryMode::
@@ -51,8 +53,19 @@
   for (int n = 0; n < number_of_devices_; ++n) {
     std::string possible_id = base::StringPrintf("/dev/video%d", n);
     if (device_descriptor.device_id.compare(possible_id) == 0) {
+      FakeVideoCaptureDeviceMaker::PixelFormat pixel_format =
+          GetPixelFormatFromDeviceId(possible_id);
+      FakeVideoCaptureDeviceMaker::DeliveryMode delivery_mode = delivery_mode_;
+      if (delivery_mode ==
+              FakeVideoCaptureDeviceMaker::DeliveryMode::
+                  USE_CLIENT_PROVIDED_BUFFERS &&
+          pixel_format == FakeVideoCaptureDeviceMaker::PixelFormat::MJPEG) {
+        // Incompatible options. Fall back to using internal buffers.
+        delivery_mode = FakeVideoCaptureDeviceMaker::DeliveryMode::
+            USE_DEVICE_INTERNAL_BUFFERS;
+      }
       return FakeVideoCaptureDeviceMaker::MakeInstance(
-          GetPixelFormatFromDeviceId(possible_id), delivery_mode_, frame_rate_);
+          pixel_format, delivery_mode, frame_rate_);
     }
   }
   return std::unique_ptr<VideoCaptureDevice>();
@@ -100,8 +113,8 @@
 
   ParseCommandLine();
 
-  const VideoPixelFormat pixel_format =
-      GetPixelFormatFromDeviceId(device_descriptor.device_id);
+  const VideoPixelFormat pixel_format = static_cast<VideoPixelFormat>(
+      GetPixelFormatFromDeviceId(device_descriptor.device_id));
   const VideoPixelStorage pixel_storage = PIXEL_STORAGE_CPU;
   std::vector<gfx::Size> supported_sizes;
   FakeVideoCaptureDeviceMaker::GetSupportedSizes(&supported_sizes);
diff --git a/media/capture/video/fake_video_capture_device_unittest.cc b/media/capture/video/fake_video_capture_device_unittest.cc
index 67e67e7..c6f46af 100644
--- a/media/capture/video/fake_video_capture_device_unittest.cc
+++ b/media/capture/video/fake_video_capture_device_unittest.cc
@@ -266,13 +266,22 @@
 class FakeVideoCaptureDeviceTest
     : public FakeVideoCaptureDeviceBase,
       public ::testing::WithParamInterface<
-          ::testing::tuple<VideoPixelFormat,
+          ::testing::tuple<FakeVideoCaptureDeviceMaker::PixelFormat,
                            FakeVideoCaptureDeviceMaker::DeliveryMode,
                            float>> {};
 
 // Tests that a frame is delivered with the expected settings.
 // Sweeps through a fixed set of requested/expected resolutions.
 TEST_P(FakeVideoCaptureDeviceTest, CaptureUsing) {
+  if (testing::get<1>(GetParam()) ==
+          FakeVideoCaptureDeviceMaker::DeliveryMode::
+              USE_CLIENT_PROVIDED_BUFFERS &&
+      testing::get<0>(GetParam()) ==
+          FakeVideoCaptureDeviceMaker::PixelFormat::MJPEG) {
+    // Unsupported case
+    return;
+  }
+
   const std::unique_ptr<VideoCaptureDeviceDescriptors> descriptors(
       EnumerateDevices());
   ASSERT_FALSE(descriptors->empty());
@@ -303,7 +312,8 @@
     WaitForCapturedFrame();
     EXPECT_EQ(resolution.second.width(), last_format().frame_size.width());
     EXPECT_EQ(resolution.second.height(), last_format().frame_size.height());
-    EXPECT_EQ(last_format().pixel_format, testing::get<0>(GetParam()));
+    EXPECT_EQ(last_format().pixel_format,
+              static_cast<VideoPixelFormat>(testing::get<0>(GetParam())));
     EXPECT_EQ(last_format().frame_rate, testing::get<2>(GetParam()));
     device->StopAndDeAllocate();
   }
@@ -312,7 +322,9 @@
 INSTANTIATE_TEST_CASE_P(
     ,
     FakeVideoCaptureDeviceTest,
-    Combine(Values(PIXEL_FORMAT_I420, PIXEL_FORMAT_Y16, PIXEL_FORMAT_ARGB),
+    Combine(Values(FakeVideoCaptureDeviceMaker::PixelFormat::I420,
+                   FakeVideoCaptureDeviceMaker::PixelFormat::Y16,
+                   FakeVideoCaptureDeviceMaker::PixelFormat::MJPEG),
             Values(FakeVideoCaptureDeviceMaker::DeliveryMode::
                        USE_DEVICE_INTERNAL_BUFFERS,
                    FakeVideoCaptureDeviceMaker::DeliveryMode::
@@ -321,19 +333,22 @@
 
 TEST_F(FakeVideoCaptureDeviceTest, GetDeviceSupportedFormats) {
   base::CommandLine::ForCurrentProcess()->AppendSwitchASCII(
-      switches::kUseFakeDeviceForMediaStream, "device-count=3");
+      switches::kUseFakeDeviceForMediaStream, "device-count=4");
   std::unique_ptr<VideoCaptureDeviceDescriptors> descriptors(
       EnumerateDevices());
-  ASSERT_EQ(3u, descriptors->size());
+  ASSERT_EQ(4u, descriptors->size());
+  const VideoPixelFormat expected_format_by_device_index[] = {
+      PIXEL_FORMAT_I420, PIXEL_FORMAT_Y16, PIXEL_FORMAT_MJPEG,
+      PIXEL_FORMAT_I420};
 
+  int device_index = 0;
   for (const auto& descriptors_iterator : *descriptors) {
     VideoCaptureFormats supported_formats;
     video_capture_device_factory_->GetSupportedFormats(descriptors_iterator,
                                                        &supported_formats);
     ASSERT_EQ(5u, supported_formats.size());
-    const std::string device_id = descriptors_iterator.device_id;
     VideoPixelFormat expected_format =
-        (device_id == "/dev/video1") ? PIXEL_FORMAT_Y16 : PIXEL_FORMAT_I420;
+        expected_format_by_device_index[device_index];
     EXPECT_EQ(96, supported_formats[0].frame_size.width());
     EXPECT_EQ(96, supported_formats[0].frame_size.height());
     EXPECT_EQ(expected_format, supported_formats[0].pixel_format);
@@ -354,6 +369,7 @@
     EXPECT_EQ(1080, supported_formats[4].frame_size.height());
     EXPECT_EQ(expected_format, supported_formats[4].pixel_format);
     EXPECT_GE(supported_formats[4].frame_rate, 20.0);
+    device_index++;
   }
 }
 
@@ -375,7 +391,7 @@
 
 TEST_F(FakeVideoCaptureDeviceTest, GetAndSetCapabilities) {
   auto device = FakeVideoCaptureDeviceMaker::MakeInstance(
-      PIXEL_FORMAT_I420,
+      FakeVideoCaptureDeviceMaker::PixelFormat::I420,
       FakeVideoCaptureDeviceMaker::DeliveryMode::USE_DEVICE_INTERNAL_BUFFERS,
       30.0);
   ASSERT_TRUE(device);
@@ -486,7 +502,7 @@
 
 TEST_F(FakeVideoCaptureDeviceTest, TakePhoto) {
   auto device = FakeVideoCaptureDeviceMaker::MakeInstance(
-      PIXEL_FORMAT_I420,
+      FakeVideoCaptureDeviceMaker::PixelFormat::I420,
       FakeVideoCaptureDeviceMaker::DeliveryMode::USE_DEVICE_INTERNAL_BUFFERS,
       30.0);
   ASSERT_TRUE(device);
@@ -578,15 +594,17 @@
                                60,
                                1u,
                                {PIXEL_FORMAT_I420}},
-           CommandLineTestData{
-               "device-count=3",
-               20,
-               3u,
-               {PIXEL_FORMAT_I420, PIXEL_FORMAT_Y16, PIXEL_FORMAT_I420}},
-           CommandLineTestData{
-               "device-count=3,ownership=client",
-               20,
-               3u,
-               {PIXEL_FORMAT_I420, PIXEL_FORMAT_Y16, PIXEL_FORMAT_I420}},
+           CommandLineTestData{"device-count=4",
+                               20,
+                               4u,
+
+                               {PIXEL_FORMAT_I420, PIXEL_FORMAT_Y16,
+                                PIXEL_FORMAT_MJPEG, PIXEL_FORMAT_I420}},
+           CommandLineTestData{"device-count=4,ownership=client",
+                               20,
+                               4u,
+
+                               {PIXEL_FORMAT_I420, PIXEL_FORMAT_Y16,
+                                PIXEL_FORMAT_MJPEG, PIXEL_FORMAT_I420}},
            CommandLineTestData{"device-count=0", 20, 1u, {PIXEL_FORMAT_I420}}));
 };  // namespace media
diff --git a/net/base/linked_hash_map.h b/net/base/linked_hash_map.h
index e12fd12a..921764f 100644
--- a/net/base/linked_hash_map.h
+++ b/net/base/linked_hash_map.h
@@ -47,8 +47,11 @@
   typedef typename ListType::value_type value_type;
   typedef typename ListType::size_type size_type;
 
-  linked_hash_map() : map_(), list_() {
-  }
+  linked_hash_map() = default;
+  explicit linked_hash_map(size_type bucket_count) : map_(bucket_count) {}
+
+  linked_hash_map(linked_hash_map&& other) = default;
+  linked_hash_map& operator=(linked_hash_map&& other) = default;
 
   // Returns an iterator to the first (insertion-ordered) element.  Like a map,
   // this can be dereferenced to a pair<Key, Value>.
diff --git a/net/http/http_server_properties_manager.cc b/net/http/http_server_properties_manager.cc
index 4130a57a..e467f56 100644
--- a/net/http/http_server_properties_manager.cc
+++ b/net/http/http_server_properties_manager.cc
@@ -775,6 +775,9 @@
 
   http_server_properties_impl_->SetServerNetworkStats(server_network_stats_map);
 
+  UMA_HISTOGRAM_COUNTS_1000("Net.CountOfQuicServerInfos",
+                            quic_server_info_map->size());
+
   http_server_properties_impl_->SetQuicServerInfoMap(quic_server_info_map);
 
   // Update the prefs with what we have read (delete all corrupted prefs).
diff --git a/net/spdy/spdy_header_block.cc b/net/spdy/spdy_header_block.cc
index 32481174..2683a1ca 100644
--- a/net/spdy/spdy_header_block.cc
+++ b/net/spdy/spdy_header_block.cc
@@ -28,6 +28,14 @@
 namespace net {
 namespace {
 
+// By default, linked_hash_map's internal map allocates space for 100 map
+// buckets on construction, which is larger than necessary.  Standard library
+// unordered map implementations use a list of prime numbers to set the bucket
+// count for a particular capacity.  |kInitialMapBuckets| is chosen to reduce
+// memory usage for small header blocks, at the cost of having to rehash for
+// large header blocks.
+const size_t kInitialMapBuckets = 11;
+
 // SpdyHeaderBlock::Storage allocates blocks of this size by default.
 const size_t kDefaultStorageBlockSize = 2048;
 
@@ -97,6 +105,8 @@
 
  private:
   UnsafeArena arena_;
+
+  DISALLOW_COPY_AND_ASSIGN(Storage);
 };
 
 SpdyHeaderBlock::HeaderValue::HeaderValue(Storage* storage,
@@ -212,12 +222,9 @@
   }
 }
 
-SpdyHeaderBlock::SpdyHeaderBlock() {}
+SpdyHeaderBlock::SpdyHeaderBlock() : block_(kInitialMapBuckets) {}
 
-SpdyHeaderBlock::SpdyHeaderBlock(SpdyHeaderBlock&& other) {
-  block_.swap(other.block_);
-  storage_.swap(other.storage_);
-}
+SpdyHeaderBlock::SpdyHeaderBlock(SpdyHeaderBlock&& other) = default;
 
 SpdyHeaderBlock::~SpdyHeaderBlock() {}
 
@@ -247,6 +254,7 @@
   if (empty()) {
     return "{}";
   }
+
   string output = "\n{\n";
   for (auto it = begin(); it != end(); ++it) {
     output +=
diff --git a/remoting/protocol/webrtc_transport.cc b/remoting/protocol/webrtc_transport.cc
index dcbbb8c..468bf0f4 100644
--- a/remoting/protocol/webrtc_transport.cc
+++ b/remoting/protocol/webrtc_transport.cc
@@ -29,6 +29,7 @@
 #include "remoting/protocol/webrtc_audio_module.h"
 #include "remoting/protocol/webrtc_dummy_video_encoder.h"
 #include "third_party/libjingle_xmpp/xmllite/xmlelement.h"
+#include "third_party/webrtc/api/audio_codecs/builtin_audio_decoder_factory.h"
 #include "third_party/webrtc/api/test/fakeconstraints.h"
 
 using buzz::QName;
@@ -175,7 +176,9 @@
 
     peer_connection_factory_ = webrtc::CreatePeerConnectionFactory(
         worker_thread, rtc::Thread::Current(), audio_module_.get(),
-        encoder_factory.release(), nullptr);
+        webrtc::CreateBuiltinAudioEncoderFactory(),
+        webrtc::CreateBuiltinAudioDecoderFactory(), encoder_factory.release(),
+        nullptr);
 
     webrtc::FakeConstraints constraints;
     constraints.AddMandatory(webrtc::MediaConstraintsInterface::kEnableDtlsSrtp,
diff --git a/testing/buildbot/chromium.fyi.json b/testing/buildbot/chromium.fyi.json
index 9ba543d..bd0fed8 100644
--- a/testing/buildbot/chromium.fyi.json
+++ b/testing/buildbot/chromium.fyi.json
@@ -10253,6 +10253,11 @@
       }
     ]
   },
+  "Linux Clang Analyzer": {
+    "additional_compile_targets": [
+      "all"
+    ]
+  },
   "Linux Trusty": {
     "gtest_tests": [
       {
diff --git a/third_party/WebKit/LayoutTests/FlagExpectations/enable-slimming-paint-v2 b/third_party/WebKit/LayoutTests/FlagExpectations/enable-slimming-paint-v2
index 3a217be4..9632f9d7 100644
--- a/third_party/WebKit/LayoutTests/FlagExpectations/enable-slimming-paint-v2
+++ b/third_party/WebKit/LayoutTests/FlagExpectations/enable-slimming-paint-v2
@@ -304,6 +304,8 @@
 Bug(none) compositing/layer-creation/translatez-overlap.html [ Failure ]
 Bug(none) compositing/layers-inside-overflow-scroll.html [ Failure ]
 Bug(none) compositing/layout-width-change.html [ Failure ]
+Bug(none) compositing/masks/mask-with-added-filters.html [ Failure ]
+Bug(none) compositing/masks/mask-with-removed-filters.html [ Failure ]
 Bug(none) compositing/overflow/accelerated-overflow-scroll-should-not-affect-perspective.html [ Failure ]
 Bug(none) compositing/overflow/accelerated-scrolling-with-clip-path-text.html [ Failure ]
 Bug(none) compositing/overflow/accelerated-scrolling-with-clip-path.html [ Failure ]
@@ -318,6 +320,8 @@
 Bug(none) compositing/overflow/content-gains-scrollbars.html [ Failure ]
 Bug(none) compositing/overflow/content-loses-scrollbars.html [ Failure ]
 Bug(none) compositing/overflow/descendant-with-clip-path.html [ Failure ]
+Bug(none) compositing/overflow/mask-with-filter.html [ Failure ]
+Bug(none) compositing/overflow/mask-with-small-content-rect.html [ Failure ]
 Bug(none) compositing/overflow/nested-border-radius-clipping.html [ Failure ]
 Bug(none) compositing/overflow/nested-render-surfaces-with-intervening-clip.html [ Failure ]
 Bug(none) compositing/overflow/nested-render-surfaces-with-rotation.html [ Failure ]
@@ -336,6 +340,7 @@
 Bug(none) compositing/overflow/overflow-scrollbar-layers.html [ Failure ]
 Bug(none) compositing/overflow/reparented-scrollbars-non-sc-anc.html [ Failure ]
 Bug(none) compositing/overflow/resize-painting.html [ Failure ]
+Bug(none) compositing/overflow/scaled-mask.html [ Failure ]
 Bug(none) compositing/overflow/scaled-overflow.html [ Failure ]
 Bug(none) compositing/overflow/scroll-ancestor-update.html [ Failure ]
 Bug(none) compositing/overflow/scroll-parent-absolute-with-backdrop-filter.html [ Failure ]
@@ -348,6 +353,7 @@
 crbug.com/667946 compositing/overflow/scrolls-with-respect-to-transform.html [ Failure ]
 crbug.com/667946 compositing/overflow/scrolls-with-respect-to.html [ Failure ]
 Bug(none) compositing/overflow/textarea-scroll-touch.html [ Failure ]
+Bug(none) compositing/overflow/tiled-mask.html [ Failure ]
 Bug(none) compositing/overflow/universal-accelerated-overflow-scroll.html [ Failure ]
 Bug(none) compositing/overlap-blending/children-opacity-huge.html [ Failure ]
 Bug(none) compositing/overlap-blending/reflection-opacity-huge.html [ Failure ]
diff --git a/third_party/WebKit/LayoutTests/TestExpectations b/third_party/WebKit/LayoutTests/TestExpectations
index 4830aa8..3cd1567 100644
--- a/third_party/WebKit/LayoutTests/TestExpectations
+++ b/third_party/WebKit/LayoutTests/TestExpectations
@@ -1669,7 +1669,7 @@
 crbug.com/618082 [ Win10 ] printing/thead-repeats-at-top-of-each-page.html [ Failure ]
 crbug.com/618082 [ Win10 ] virtual/threaded/printing/thead-repeats-at-top-of-each-page.html [ Failure ]
 
-crbug.com/695203 inspector/sources/debugger-breakpoints/debugger-reload-breakpoints-with-source-maps.html [ NeedsManualRebaseline ]
+crbug.com/695203 inspector/sources/debugger-breakpoints/debugger-reload-breakpoints-with-source-maps.html [ NeedsManualRebaseline Timeout ]
 crbug.com/695203 inspector/sources/debugger-breakpoints/possible-breakpoints.html [ NeedsManualRebaseline ]
 crbug.com/695203 inspector/sources/debugger/source-frame-inline-breakpoint-decorations.html [ NeedsManualRebaseline ]
 
diff --git a/third_party/WebKit/LayoutTests/inspector/coverage/coverage-view-expected.txt b/third_party/WebKit/LayoutTests/inspector/coverage/coverage-view-expected.txt
new file mode 100644
index 0000000..ca8a2c88
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/inspector/coverage/coverage-view-expected.txt
@@ -0,0 +1,4 @@
+Tests the coverage list view after finishing recording in the Coverage view.
+
+.../inspector/coverage/resources/highlight-in-source.css used: 65 unused: 128 total: 209
+
diff --git a/third_party/WebKit/LayoutTests/inspector/coverage/coverage-view.html b/third_party/WebKit/LayoutTests/inspector/coverage/coverage-view.html
new file mode 100644
index 0000000..6451d2c
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/inspector/coverage/coverage-view.html
@@ -0,0 +1,44 @@
+<html>
+<head>
+<script src="../../http/tests/inspector/inspector-test.js"></script>
+<script src="../../http/tests/inspector/elements-test.js"></script>
+
+<link rel="stylesheet" type="text/css" href="resources/highlight-in-source.css">
+
+<script>
+
+var initialize_CoverageTest = function() {
+    InspectorTest.preloadModule("coverage");
+}
+
+function test()
+{
+    var coverageView = self.runtime.sharedInstance(Coverage.CoverageView);
+    InspectorTest.addSniffer(coverageView._listView, "update", displayResults);
+
+    coverageView._toggleRecording(true);
+    UI.viewManager.showView("coverage");
+    coverageView._toggleRecording(false);
+
+    function displayResults()
+    {
+        var dataGrid = coverageView._listView._dataGrid;
+        for (var child of dataGrid.rootNode().children) {
+            var data = child._coverageInfo;
+            var url = InspectorTest.formatters.formatAsURL(data.url);
+            InspectorTest.addResult(`${url} used: ${data.usedSize} unused: ${data.unusedSize} total: ${data.size}`);
+        }
+        InspectorTest.completeTest();
+    }
+}
+
+</script>
+</head>
+
+<body onload="runTest()">
+<p class="class">
+Tests the coverage list view after finishing recording in the Coverage view.
+</p>
+
+</body>
+</html>
\ No newline at end of file
diff --git a/third_party/WebKit/LayoutTests/inspector/css_tracker/decorations-after-inplace-formatter-expected.txt b/third_party/WebKit/LayoutTests/inspector/coverage/decorations-after-inplace-formatter-expected.txt
similarity index 100%
rename from third_party/WebKit/LayoutTests/inspector/css_tracker/decorations-after-inplace-formatter-expected.txt
rename to third_party/WebKit/LayoutTests/inspector/coverage/decorations-after-inplace-formatter-expected.txt
diff --git a/third_party/WebKit/LayoutTests/inspector/css_tracker/decorations-after-inplace-formatter.html b/third_party/WebKit/LayoutTests/inspector/coverage/decorations-after-inplace-formatter.html
similarity index 73%
rename from third_party/WebKit/LayoutTests/inspector/css_tracker/decorations-after-inplace-formatter.html
rename to third_party/WebKit/LayoutTests/inspector/coverage/decorations-after-inplace-formatter.html
index 86adbdb..135fde3 100644
--- a/third_party/WebKit/LayoutTests/inspector/css_tracker/decorations-after-inplace-formatter.html
+++ b/third_party/WebKit/LayoutTests/inspector/coverage/decorations-after-inplace-formatter.html
@@ -1,26 +1,25 @@
 <html>
 <head>
 <script src="../../http/tests/inspector/inspector-test.js"></script>
-<script src="../../http/tests/inspector/timeline-test.js"></script>
 <script src="../../http/tests/inspector/debugger-test.js"></script>
 <script src="../../http/tests/inspector/sources-test.js"></script>
 
 <link rel="stylesheet" type="text/css" href="resources/decorations-after-inplace-formatter.css">
 <script>
 
-var initialize_CSSTracker = function() {
-    InspectorTest.preloadModule("css_tracker");
+var initialize_CoverageTest = function() {
+    InspectorTest.preloadModule("coverage");
 }
 
 function test()
 {
     var scriptFormatter;
 
-    var tracker = self.runtime.sharedInstance(CSSTracker.CSSTrackerView);
-    InspectorTest.addSniffer(CSSTracker.CSSTrackerView.prototype, "_updateGutter", onTracingFinished);
+    var tracker = self.runtime.sharedInstance(Coverage.CoverageView);
+    InspectorTest.addSniffer(Coverage.CoverageView.prototype, "_updateGutter", onTracingFinished);
 
     tracker._toggleRecording(true);
-    UI.viewManager.showView("css_tracker");
+    UI.viewManager.showView("coverage");
     tracker._toggleRecording(false);
 
     function onTracingFinished()
@@ -36,19 +35,19 @@
     }
     function showSource()
     {
-        InspectorTest.addSniffer(CSSTracker.CSSTrackerView.LineDecorator.prototype, "decorate", formatSource);
+        InspectorTest.addSniffer(Coverage.CoverageView.LineDecorator.prototype, "decorate", formatSource);
         InspectorTest.showScriptSource("decorations-after-inplace-formatter.css");
     }
 
     function formatSource(frame) 
     {
-        InspectorTest.addSniffer(CSSTracker.CSSTrackerView.LineDecorator.prototype, "decorate", uiSourceCodeScriptFormatted);
+        InspectorTest.addSniffer(Coverage.CoverageView.LineDecorator.prototype, "decorate", uiSourceCodeScriptFormatted);
         scriptFormatter._formatSourceInPlace();
     }
 
     function uiSourceCodeScriptFormatted()
     {
-        var lines = Array.prototype.map.call(document.querySelectorAll(".text-editor-css-rule-unused-marker"), 
+        var lines = Array.prototype.map.call(document.querySelectorAll(".text-editor-coverage-unused-marker"),
                                                 e => e.parentElement.previousSibling.textContent);
 
         InspectorTest.addResult("Formatted line numbers of rules that were not used:");
diff --git a/third_party/WebKit/LayoutTests/inspector/coverage/highlight-in-source-expected.txt b/third_party/WebKit/LayoutTests/inspector/coverage/highlight-in-source-expected.txt
new file mode 100644
index 0000000..e4cc307
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/inspector/coverage/highlight-in-source-expected.txt
@@ -0,0 +1,8 @@
+PASS
+
+Tests the coverage highlight in sources after the recording finishes.
+
+
+Running: testMarking
+5,6,7,9,10,11,12,18,19,20,22,23,24
+
diff --git a/third_party/WebKit/LayoutTests/inspector/css_tracker/highlight-in-source.html b/third_party/WebKit/LayoutTests/inspector/coverage/highlight-in-source.html
similarity index 61%
rename from third_party/WebKit/LayoutTests/inspector/css_tracker/highlight-in-source.html
rename to third_party/WebKit/LayoutTests/inspector/coverage/highlight-in-source.html
index a134463..fefd780 100644
--- a/third_party/WebKit/LayoutTests/inspector/css_tracker/highlight-in-source.html
+++ b/third_party/WebKit/LayoutTests/inspector/coverage/highlight-in-source.html
@@ -1,15 +1,14 @@
 <html>
 <head>
 <script src="../../http/tests/inspector/inspector-test.js"></script>
-<script src="../../http/tests/inspector/timeline-test.js"></script>
 <script src="../../http/tests/inspector/debugger-test.js"></script>
 <script src="../../http/tests/inspector/sources-test.js"></script>
 
 <link rel="stylesheet" type="text/css" href="resources/highlight-in-source.css">
 <script>
 
-var initialize_CSSTracker = function() {
-    InspectorTest.preloadModule("css_tracker");
+var initialize_Coverage = function() {
+    InspectorTest.preloadModule("coverage");
 }
 
 function test()
@@ -17,12 +16,12 @@
     InspectorTest.runTestSuite([
         function testMarking(next)
         {
-            var tracker = self.runtime.sharedInstance(CSSTracker.CSSTrackerView);
-            InspectorTest.addSniffer(CSSTracker.CSSTrackerView.prototype, "_updateGutter", printResults);
+            var coverageView = self.runtime.sharedInstance(Coverage.CoverageView);
+            InspectorTest.addSniffer(Coverage.CoverageView.prototype, "_updateGutter", printResults);
 
-            tracker._toggleRecording(true);
-            UI.viewManager.showView("css_tracker");
-            tracker._toggleRecording(false);
+            coverageView._toggleRecording(true);
+            UI.viewManager.showView("coverage");
+            coverageView._toggleRecording(false);
 
             function printResults()
             {
@@ -36,12 +35,12 @@
 
             function waitForDecorations()
             {
-                InspectorTest.addSniffer(CSSTracker.CSSTrackerView.LineDecorator.prototype, "decorate", didShowDecorations);
+                InspectorTest.addSniffer(Coverage.CoverageView.LineDecorator.prototype, "decorate", didShowDecorations);
             }
 
             function didShowDecorations(sourceFrame)
             {
-                var lines = Array.prototype.map.call(document.querySelectorAll(".text-editor-css-rule-unused-marker"), 
+                var lines = Array.prototype.map.call(document.querySelectorAll(".text-editor-coverage-unused-marker"),
                                                         e => e.parentElement.previousSibling.textContent);
 
                 InspectorTest.addResult(lines);
@@ -54,6 +53,6 @@
 </head>
 <p id="id">PASS</p>
 <body onload="runTest()">
-<p>Tests the CSS highlight in sources after the timeline recording finishes.</p>
+<p>Tests the coverage highlight in sources after the recording finishes.</p>
 </body>
 </html>
\ No newline at end of file
diff --git a/third_party/WebKit/LayoutTests/inspector/css_tracker/resources/decorations-after-inplace-formatter.css b/third_party/WebKit/LayoutTests/inspector/coverage/resources/decorations-after-inplace-formatter.css
similarity index 100%
rename from third_party/WebKit/LayoutTests/inspector/css_tracker/resources/decorations-after-inplace-formatter.css
rename to third_party/WebKit/LayoutTests/inspector/coverage/resources/decorations-after-inplace-formatter.css
diff --git a/third_party/WebKit/LayoutTests/inspector/css_tracker/resources/highlight-in-source.css b/third_party/WebKit/LayoutTests/inspector/coverage/resources/highlight-in-source.css
similarity index 100%
rename from third_party/WebKit/LayoutTests/inspector/css_tracker/resources/highlight-in-source.css
rename to third_party/WebKit/LayoutTests/inspector/coverage/resources/highlight-in-source.css
diff --git a/third_party/WebKit/LayoutTests/inspector/css_tracker/css-tracker-view-expected.txt b/third_party/WebKit/LayoutTests/inspector/css_tracker/css-tracker-view-expected.txt
deleted file mode 100644
index 847bada..0000000
--- a/third_party/WebKit/LayoutTests/inspector/css_tracker/css-tracker-view-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-Tests the unused CSS list after finishing recording in the CSSTracker view.
-
-.../inspector/css_tracker/resources/highlight-in-source.css used: 65 unused: 128 total: 209
-
diff --git a/third_party/WebKit/LayoutTests/inspector/css_tracker/css-tracker-view.html b/third_party/WebKit/LayoutTests/inspector/css_tracker/css-tracker-view.html
deleted file mode 100644
index 5a9ecb8..0000000
--- a/third_party/WebKit/LayoutTests/inspector/css_tracker/css-tracker-view.html
+++ /dev/null
@@ -1,44 +0,0 @@
-<html>
-<head>
-<script src="../../http/tests/inspector/inspector-test.js"></script>
-<script src="../../http/tests/inspector/elements-test.js"></script>
-
-<link rel="stylesheet" type="text/css" href="resources/highlight-in-source.css">
-
-<script>
-
-var initialize_CSSTracker = function() {
-    InspectorTest.preloadModule("css_tracker");
-}
-
-function test()
-{
-    var tracker = self.runtime.sharedInstance(CSSTracker.CSSTrackerView);
-    InspectorTest.addSniffer(tracker._listView, "update", displayResults);
-
-    tracker._toggleRecording(true);
-    UI.viewManager.showView("css_tracker");
-    tracker._toggleRecording(false);
-
-    function displayResults()
-    {
-        var dataGrid = tracker._listView._dataGrid;
-        for (var child of dataGrid.rootNode().children) {
-            var data = child._coverageInfo;
-            var url = InspectorTest.formatters.formatAsURL(data.url);
-            InspectorTest.addResult(`${url} used: ${data.usedSize} unused: ${data.unusedSize} total: ${data.size}`);
-        }
-        InspectorTest.completeTest();
-    }
-}
-
-</script>
-</head>
-
-<body onload="runTest()">
-<p class="class">
-Tests the unused CSS list after finishing recording in the CSSTracker view.
-</p>
-
-</body>
-</html>
\ No newline at end of file
diff --git a/third_party/WebKit/LayoutTests/inspector/css_tracker/highlight-in-source-expected.txt b/third_party/WebKit/LayoutTests/inspector/css_tracker/highlight-in-source-expected.txt
deleted file mode 100644
index 3da4dc6..0000000
--- a/third_party/WebKit/LayoutTests/inspector/css_tracker/highlight-in-source-expected.txt
+++ /dev/null
@@ -1,8 +0,0 @@
-PASS
-
-Tests the CSS highlight in sources after the timeline recording finishes.
-
-
-Running: testMarking
-5,6,7,9,10,11,12,18,19,20,22,23,24
-
diff --git a/third_party/WebKit/LayoutTests/inspector/sources/debugger-breakpoints/debugger-reload-breakpoints-with-source-maps-expected.txt b/third_party/WebKit/LayoutTests/inspector/sources/debugger-breakpoints/debugger-reload-breakpoints-with-source-maps-expected.txt
new file mode 100644
index 0000000..64a8eed
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/inspector/sources/debugger-breakpoints/debugger-reload-breakpoints-with-source-maps-expected.txt
@@ -0,0 +1,9 @@
+Tests "reload" from within inspector window while on pause.
+
+
+Breakpoint sidebar pane before reload:
+source1.js:17}
+Page reloaded.
+Breakpoint sidebar pane after reload:
+source1.js:17}
+
diff --git a/third_party/WebKit/LayoutTests/inspector/sources/debugger-breakpoints/debugger-reload-breakpoints-with-source-maps.html b/third_party/WebKit/LayoutTests/inspector/sources/debugger-breakpoints/debugger-reload-breakpoints-with-source-maps.html
index 0e19c180..535de4f 100644
--- a/third_party/WebKit/LayoutTests/inspector/sources/debugger-breakpoints/debugger-reload-breakpoints-with-source-maps.html
+++ b/third_party/WebKit/LayoutTests/inspector/sources/debugger-breakpoints/debugger-reload-breakpoints-with-source-maps.html
@@ -20,7 +20,6 @@
             .then(waitUntilReady)
             .then(onBreakpointsReady);
         InspectorTest.setBreakpoint(sourceFrame, 14, "", true);
-        InspectorTest.setBreakpoint(sourceFrame, 15, "", true);
 
         function onBreakpointsReady()
         {
@@ -36,7 +35,7 @@
     }
 
     function waitUntilReady() {
-        var expectedBreakpointLocations = [[14, 0], [16, 4]];
+        var expectedBreakpointLocations = [[16, 4]];
         var paneElement = self.runtime.sharedInstance(Sources.JavaScriptBreakpointsSidebarPane).contentElement;
         var entries = Array.from(paneElement.querySelectorAll('.breakpoint-entry'));
         for (var entry of entries) {
diff --git a/third_party/WebKit/LayoutTests/inspector/sources/debugger-frameworks/frameworks-skip-break-program.html b/third_party/WebKit/LayoutTests/inspector/sources/debugger-frameworks/frameworks-skip-break-program.html
index 5f1267f2..cca9f1e 100644
--- a/third_party/WebKit/LayoutTests/inspector/sources/debugger-frameworks/frameworks-skip-break-program.html
+++ b/third_party/WebKit/LayoutTests/inspector/sources/debugger-frameworks/frameworks-skip-break-program.html
@@ -39,7 +39,7 @@
     function step2()
     {
         InspectorTest.DebuggerAgent.setPauseOnExceptions(SDK.DebuggerModel.PauseOnExceptionsState.PauseOnAllExceptions);
-        pane._setBreakpoint("instrumentation:setTimer");
+        pane._setBreakpoint("instrumentation:setTimeout");
         InspectorTest.resumeExecution(InspectorTest.waitUntilPaused.bind(InspectorTest, didPause));
     }
 
@@ -52,7 +52,7 @@
     function completeTest()
     {
         InspectorTest.DebuggerAgent.setPauseOnExceptions(SDK.DebuggerModel.PauseOnExceptionsState.DontPauseOnExceptions);
-        pane._removeBreakpoint("instrumentation:setTimer");
+        pane._removeBreakpoint("instrumentation:setTimeout");
         InspectorTest.completeDebuggerTest();
     }
 }
diff --git a/third_party/WebKit/LayoutTests/typedcssom/inlinestyle/properties/bottom.html b/third_party/WebKit/LayoutTests/typedcssom/inlinestyle/properties/bottom.html
index 4669161c..830b649 100644
--- a/third_party/WebKit/LayoutTests/typedcssom/inlinestyle/properties/bottom.html
+++ b/third_party/WebKit/LayoutTests/typedcssom/inlinestyle/properties/bottom.html
@@ -3,8 +3,6 @@
 <script src='../../../resources/testharnessreport.js'></script>
 <script src='property-suite.js'></script>
 
-<div id="testElement"></div>
-
 <script>
 
 runInlineStylePropertyMapTests({
diff --git a/third_party/WebKit/LayoutTests/typedcssom/inlinestyle/properties/property-suite.js b/third_party/WebKit/LayoutTests/typedcssom/inlinestyle/properties/property-suite.js
index e3837b5..5dbcaaf8 100644
--- a/third_party/WebKit/LayoutTests/typedcssom/inlinestyle/properties/property-suite.js
+++ b/third_party/WebKit/LayoutTests/typedcssom/inlinestyle/properties/property-suite.js
@@ -28,6 +28,7 @@
 
 function runInlineStylePropertyMapTests(config) {
   let element = document.createElement('div');
+  document.documentElement.appendChild(element);
   let validKeywords = config.validKeywords.concat([
     // CSS-wide keywords
     'initial',
@@ -78,14 +79,19 @@
       element.style = '';
       element.styleMap.set(propertyName, new CSSKeywordValue(keyword));
       assert_equals(element.style[propertyName], keyword);
+      // Force a style recalc to check for crashes in style recalculation.
+      getComputedStyle(element)[propertyName];
+      assert_equals(element.style[propertyName], keyword);
     }, 'Setting ' + propertyName + ' to ' + keyword);
   }
   for (let validObject of validObjects) {
     test(function() {
       element.style = '';
-
       element.styleMap.set(propertyName, validObject);
       assert_equals(element.style[propertyName], validObject.cssText);
+      // Force a style recalc to check for crashes in style recalculation.
+      getComputedStyle(element)[propertyName];
+      assert_equals(element.style[propertyName], validObject.cssText);
     }, 'Setting ' + propertyName + ' to ' + validObject.constructor.name +
         ' with value ' +  validObject.cssText);
   }
@@ -143,11 +149,15 @@
     propertyName, validObject, invalidObject, element) {
   test(function() {
     element.style = '';
-
     element.styleMap.set(propertyName, [validObject, validObject]);
     assert_equals(
         element.style[propertyName], validObject.cssText + ', ' +
         validObject.cssText);
+    // Force a style recalc to check for crashes in style recalculation.
+    getComputedStyle(element)[propertyName];
+    assert_equals(
+        element.style[propertyName], validObject.cssText + ', ' +
+        validObject.cssText);
   }, 'Set ' + propertyName + ' to a sequence');
 
   test(function() {
@@ -170,6 +180,11 @@
     assert_equals(
         element.style[propertyName], validObject.cssText + ', ' +
         validObject.cssText);
+    // Force a style recalc to check for crashes in style recalculation.
+    getComputedStyle(element)[propertyName];
+    assert_equals(
+        element.style[propertyName], validObject.cssText + ', ' +
+        validObject.cssText);
   }, 'Appending a ' + validObject.constructor.name + ' to ' + propertyName);
 
   test(function() {
@@ -179,6 +194,11 @@
     assert_equals(
         element.style[propertyName], validObject.cssText + ', ' +
         validObject.cssText);
+    // Force a style recalc to check for crashes in style recalculation.
+    getComputedStyle(element)[propertyName];
+    assert_equals(
+        element.style[propertyName], validObject.cssText + ', ' +
+        validObject.cssText);
   }, 'Append a sequence to ' + propertyName);
 
   // Negative tests
@@ -237,6 +257,10 @@
     element.styleMap.delete(propertyName);
     assert_equals(element.style[propertyName], '');
     assert_equals(element.styleMap.get(propertyName), null);
+    // Force a style recalc to check for crashes in style recalculation.
+    getComputedStyle(element)[propertyName];
+    assert_equals(element.style[propertyName], '');
+    assert_equals(element.styleMap.get(propertyName), null);
   }, 'Delete ' + propertyName + ' removes the value from the styleMap');
 }
 
@@ -244,9 +268,10 @@
   test(function() {
     element.style = '';
     assert_array_equals(element.styleMap.getProperties(), []);
-
     element.styleMap.set(propertyName, validObject);
-
+    assert_array_equals(element.styleMap.getProperties(), [propertyName]);
+    // Force a style recalc to check for crashes in style recalculation.
+    getComputedStyle(element)[propertyName];
     assert_array_equals(element.styleMap.getProperties(), [propertyName]);
   }, propertyName + ' shows up in getProperties');
 }
diff --git a/third_party/WebKit/Source/bindings/core/v8/ScriptController.cpp b/third_party/WebKit/Source/bindings/core/v8/ScriptController.cpp
index eb6c5d4f..e6a135ae 100644
--- a/third_party/WebKit/Source/bindings/core/v8/ScriptController.cpp
+++ b/third_party/WebKit/Source/bindings/core/v8/ScriptController.cpp
@@ -127,7 +127,7 @@
                InspectorEvaluateScriptEvent::data(
                    frame(), source.url().getString(), source.startPosition()));
   InspectorInstrumentation::NativeBreakpoint nativeBreakpoint(
-      frame()->document(), "scriptFirstStatement", false);
+      frame()->document(), "scriptFirstStatement");
 
   v8::Local<v8::Value> result;
   {
diff --git a/third_party/WebKit/Source/core/css/cssom/CSSKeywordValue.cpp b/third_party/WebKit/Source/core/css/cssom/CSSKeywordValue.cpp
index cde43e13..b1a593d 100644
--- a/third_party/WebKit/Source/core/css/cssom/CSSKeywordValue.cpp
+++ b/third_party/WebKit/Source/core/css/cssom/CSSKeywordValue.cpp
@@ -7,6 +7,9 @@
 #include "bindings/core/v8/ExceptionState.h"
 #include "core/css/CSSCustomIdentValue.h"
 #include "core/css/CSSIdentifierValue.h"
+#include "core/css/CSSInheritedValue.h"
+#include "core/css/CSSInitialValue.h"
+#include "core/css/CSSUnsetValue.h"
 #include "core/css/parser/CSSPropertyParser.h"
 
 namespace blink {
@@ -60,10 +63,18 @@
 
 CSSValue* CSSKeywordValue::toCSSValue() const {
   CSSValueID keywordID = keywordValueID();
-  if (keywordID == CSSValueID::CSSValueInvalid) {
-    return CSSCustomIdentValue::create(m_keywordValue);
+  switch (keywordID) {
+    case (CSSValueInherit):
+      return CSSInheritedValue::create();
+    case (CSSValueInitial):
+      return CSSInitialValue::create();
+    case (CSSValueUnset):
+      return CSSUnsetValue::create();
+    case (CSSValueInvalid):
+      return CSSCustomIdentValue::create(m_keywordValue);
+    default:
+      return CSSIdentifierValue::create(keywordID);
   }
-  return CSSIdentifierValue::create(keywordID);
 }
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/core/css/cssom/InlineStylePropertyMap.cpp b/third_party/WebKit/Source/core/css/cssom/InlineStylePropertyMap.cpp
index 1e8c28b3..edcc24f27 100644
--- a/third_party/WebKit/Source/core/css/cssom/InlineStylePropertyMap.cpp
+++ b/third_party/WebKit/Source/core/css/cssom/InlineStylePropertyMap.cpp
@@ -30,13 +30,14 @@
 
 const CSSValue* singleStyleValueAsCSSValue(CSSPropertyID propertyID,
                                            const CSSStyleValue& styleValue) {
-  if (!CSSPropertyMetadata::propertyIsRepeated(propertyID))
-    return styleValueToCSSValue(propertyID, styleValue);
-
   const CSSValue* cssValue = styleValueToCSSValue(propertyID, styleValue);
   if (!cssValue)
     return nullptr;
 
+  if (!CSSPropertyMetadata::propertyIsRepeated(propertyID) ||
+      cssValue->isCSSWideKeyword())
+    return cssValue;
+
   // TODO(meade): Determine the correct separator for each property.
   CSSValueList* valueList = CSSValueList::createSpaceSeparated();
   valueList->append(*cssValue);
diff --git a/third_party/WebKit/Source/core/dom/Document.cpp b/third_party/WebKit/Source/core/dom/Document.cpp
index 43a8d63..e70bfb8 100644
--- a/third_party/WebKit/Source/core/dom/Document.cpp
+++ b/third_party/WebKit/Source/core/dom/Document.cpp
@@ -3231,8 +3231,7 @@
   PerformanceMonitor::reportGenericViolation(
       this, PerformanceMonitor::kDiscouragedAPIUse,
       "Avoid using document.write().", 0, nullptr);
-  InspectorInstrumentation::NativeBreakpoint nativeBreakpoint(
-      this, "Document.write", true);
+  InspectorInstrumentation::breakIfNeeded(this, "Document.write");
   m_parser->insert(text);
 }
 
diff --git a/third_party/WebKit/Source/core/dom/Element.cpp b/third_party/WebKit/Source/core/dom/Element.cpp
index 5145d37..d6dbd65 100644
--- a/third_party/WebKit/Source/core/dom/Element.cpp
+++ b/third_party/WebKit/Source/core/dom/Element.cpp
@@ -1673,7 +1673,7 @@
     if (hasPendingResources()) {
       treeScope()
           .ensureSVGTreeScopedResources()
-          .removeElementFromPendingResources(this);
+          .removeElementFromPendingResources(*this);
     }
 
     if (getCustomElementState() == CustomElementState::Custom)
@@ -2899,8 +2899,7 @@
 }
 
 void Element::setInnerHTML(const String& html, ExceptionState& exceptionState) {
-  InspectorInstrumentation::NativeBreakpoint nativeBreakpoint(
-      &document(), "Element.setInnerHTML", true);
+  InspectorInstrumentation::breakIfNeeded(&document(), "Element.setInnerHTML");
   if (DocumentFragment* fragment = createFragmentForInnerOuterHTML(
           html, this, AllowScriptingContent, "innerHTML", exceptionState)) {
     ContainerNode* container = this;
diff --git a/third_party/WebKit/Source/core/dom/FrameRequestCallbackCollection.cpp b/third_party/WebKit/Source/core/dom/FrameRequestCallbackCollection.cpp
index 167ddabab..550fbb1 100644
--- a/third_party/WebKit/Source/core/dom/FrameRequestCallbackCollection.cpp
+++ b/third_party/WebKit/Source/core/dom/FrameRequestCallbackCollection.cpp
@@ -26,19 +26,16 @@
   TRACE_EVENT_INSTANT1("devtools.timeline", "RequestAnimationFrame",
                        TRACE_EVENT_SCOPE_THREAD, "data",
                        InspectorAnimationFrameEvent::data(m_context, id));
-  InspectorInstrumentation::asyncTaskScheduled(
+  InspectorInstrumentation::asyncTaskScheduledBreakable(
       m_context, "requestAnimationFrame", callback);
-  InspectorInstrumentation::NativeBreakpoint nativeBreakpoint(
-      m_context, "DOMWindow.requestAnimationFrame", true);
   return id;
 }
 
 void FrameRequestCallbackCollection::cancelCallback(CallbackId id) {
   for (size_t i = 0; i < m_callbacks.size(); ++i) {
     if (m_callbacks[i]->m_id == id) {
-      InspectorInstrumentation::asyncTaskCanceled(m_context, m_callbacks[i]);
-      InspectorInstrumentation::NativeBreakpoint nativeBreakpoint(
-          m_context, "DOMWindow.cancelAnimationFrame", true);
+      InspectorInstrumentation::asyncTaskCanceledBreakable(
+          m_context, "cancelAnimationFrame", m_callbacks[i]);
       m_callbacks.remove(i);
       TRACE_EVENT_INSTANT1("devtools.timeline", "CancelAnimationFrame",
                            TRACE_EVENT_SCOPE_THREAD, "data",
@@ -48,9 +45,8 @@
   }
   for (const auto& callback : m_callbacksToInvoke) {
     if (callback->m_id == id) {
-      InspectorInstrumentation::asyncTaskCanceled(m_context, callback);
-      InspectorInstrumentation::NativeBreakpoint nativeBreakpoint(
-          m_context, "DOMWindow.cancelAnimationFrame", true);
+      InspectorInstrumentation::asyncTaskCanceledBreakable(
+          m_context, "cancelAnimationFrame", callback);
       TRACE_EVENT_INSTANT1("devtools.timeline", "CancelAnimationFrame",
                            TRACE_EVENT_SCOPE_THREAD, "data",
                            InspectorAnimationFrameEvent::data(m_context, id));
@@ -74,9 +70,8 @@
       TRACE_EVENT1(
           "devtools.timeline", "FireAnimationFrame", "data",
           InspectorAnimationFrameEvent::data(m_context, callback->m_id));
-      InspectorInstrumentation::NativeBreakpoint nativeBreakpoint(
-          m_context, "DOMWindow.requestAnimationFrame.callback", false);
-      InspectorInstrumentation::AsyncTask asyncTask(m_context, callback);
+      InspectorInstrumentation::AsyncTask asyncTask(
+          m_context, callback, "requestAnimationFrame.callback");
       PerformanceMonitor::HandlerCall handlerCall(
           m_context, "requestAnimationFrame", true);
       if (callback->m_useLegacyTimeBase)
diff --git a/third_party/WebKit/Source/core/editing/FrameSelection.cpp b/third_party/WebKit/Source/core/editing/FrameSelection.cpp
index e64e78d..7d88e25 100644
--- a/third_party/WebKit/Source/core/editing/FrameSelection.cpp
+++ b/third_party/WebKit/Source/core/editing/FrameSelection.cpp
@@ -474,8 +474,9 @@
 bool FrameSelection::shouldPaintCaret(const LayoutBlock& block) const {
   DCHECK_GE(document().lifecycle().state(), DocumentLifecycle::LayoutClean);
   bool result = m_frameCaret->shouldPaintCaret(block);
-  DCHECK(!result || (computeVisibleSelectionInDOMTreeDeprecated().isCaret() &&
-                     hasEditableStyle()));
+  DCHECK(!result ||
+         (computeVisibleSelectionInDOMTreeDeprecated().isCaret() &&
+          computeVisibleSelectionInDOMTree().hasEditableStyle()));
   return result;
 }
 
diff --git a/third_party/WebKit/Source/core/editing/FrameSelection.h b/third_party/WebKit/Source/core/editing/FrameSelection.h
index f672dce6..68e5550 100644
--- a/third_party/WebKit/Source/core/editing/FrameSelection.h
+++ b/third_party/WebKit/Source/core/editing/FrameSelection.h
@@ -103,10 +103,6 @@
   Element* rootEditableElementOrDocumentElement() const;
   ContainerNode* rootEditableElementOrTreeScopeRootNode() const;
 
-  bool hasEditableStyle() const {
-    return computeVisibleSelectionInDOMTreeDeprecated().hasEditableStyle();
-  }
-
   // An implementation of |WebFrame::moveCaretSelection()|
   void moveCaretSelection(const IntPoint&);
 
diff --git a/third_party/WebKit/Source/core/editing/SelectionController.cpp b/third_party/WebKit/Source/core/editing/SelectionController.cpp
index 3db73c6b..dc170495 100644
--- a/third_party/WebKit/Source/core/editing/SelectionController.cpp
+++ b/third_party/WebKit/Source/core/editing/SelectionController.cpp
@@ -795,7 +795,10 @@
   // Avoid double-tap touch gesture confusion by restricting multi-click side
   // effects, e.g., word selection, to editable regions.
   m_mouseDownAllowsMultiClick =
-      !event.event().fromTouch() || selection().hasEditableStyle();
+      !event.event().fromTouch() ||
+      selection()
+          .computeVisibleSelectionInDOMTreeDeprecated()
+          .hasEditableStyle();
 }
 
 void SelectionController::handleMouseDraggedEvent(
diff --git a/third_party/WebKit/Source/core/frame/DOMTimer.cpp b/third_party/WebKit/Source/core/frame/DOMTimer.cpp
index d74c1e8..e3be451 100644
--- a/third_party/WebKit/Source/core/frame/DOMTimer.cpp
+++ b/third_party/WebKit/Source/core/frame/DOMTimer.cpp
@@ -64,8 +64,6 @@
                        TRACE_EVENT_SCOPE_THREAD, "data",
                        InspectorTimerInstallEvent::data(context, timeoutID,
                                                         timeout, singleShot));
-  InspectorInstrumentation::NativeBreakpoint nativeBreakpoint(context,
-                                                              "setTimer", true);
   return timeoutID;
 }
 
@@ -74,8 +72,6 @@
   TRACE_EVENT_INSTANT1("devtools.timeline", "TimerRemove",
                        TRACE_EVENT_SCOPE_THREAD, "data",
                        InspectorTimerRemoveEvent::data(context, timeoutID));
-  InspectorInstrumentation::NativeBreakpoint nativeBreakpoint(
-      context, "clearTimer", true);
   // Eagerly unregister as ExecutionContext observer.
   if (timer)
     timer->clearContext();
@@ -97,9 +93,6 @@
     m_userGestureToken = UserGestureIndicator::currentToken();
   }
 
-  InspectorInstrumentation::asyncTaskScheduled(
-      context, singleShot ? "setTimeout" : "setInterval", this, !singleShot);
-
   double intervalMilliseconds =
       std::max(oneMillisecond, interval * oneMillisecond);
   if (intervalMilliseconds < minimumInterval &&
@@ -109,6 +102,10 @@
     startOneShot(intervalMilliseconds, BLINK_FROM_HERE);
   else
     startRepeating(intervalMilliseconds, BLINK_FROM_HERE);
+
+  suspendIfNeeded();
+  InspectorInstrumentation::asyncTaskScheduledBreakable(
+      context, singleShot ? "setTimeout" : "setInterval", this, !singleShot);
 }
 
 DOMTimer::~DOMTimer() {
@@ -117,7 +114,10 @@
 }
 
 void DOMTimer::stop() {
-  InspectorInstrumentation::asyncTaskCanceled(getExecutionContext(), this);
+  InspectorInstrumentation::asyncTaskCanceledBreakable(
+      getExecutionContext(),
+      repeatInterval() ? "clearInterval" : "clearTimeout", this);
+
   m_userGestureToken = nullptr;
   // Need to release JS objects potentially protected by ScheduledAction
   // because they can form circular references back to the ExecutionContext
@@ -145,9 +145,7 @@
                InspectorTimerFireEvent::data(context, m_timeoutID));
   PerformanceMonitor::HandlerCall handlerCall(
       context, repeatInterval() ? "setInterval" : "setTimeout", true);
-  InspectorInstrumentation::NativeBreakpoint nativeBreakpoint(
-      context, "timerFired", false);
-  InspectorInstrumentation::AsyncTask asyncTask(context, this);
+  InspectorInstrumentation::AsyncTask asyncTask(context, this, "timerFired");
 
   // Simple case for non-one-shot timers.
   if (isActive()) {
diff --git a/third_party/WebKit/Source/core/frame/DOMTimerCoordinator.cpp b/third_party/WebKit/Source/core/frame/DOMTimerCoordinator.cpp
index dc80519..8aec65a 100644
--- a/third_party/WebKit/Source/core/frame/DOMTimerCoordinator.cpp
+++ b/third_party/WebKit/Source/core/frame/DOMTimerCoordinator.cpp
@@ -23,14 +23,8 @@
   // FIXME: DOMTimers depends heavily on ExecutionContext. Decouple them.
   ASSERT(context->timers() == this);
   int timeoutID = nextID();
-  TimeoutMap::AddResult result = m_timers.insert(
-      timeoutID,
-      DOMTimer::create(context, action, timeout, singleShot, timeoutID));
-  ASSERT(result.isNewEntry);
-  DOMTimer* timer = result.storedValue->value.get();
-
-  timer->suspendIfNeeded();
-
+  m_timers.insert(timeoutID, DOMTimer::create(context, action, timeout,
+                                              singleShot, timeoutID));
   return timeoutID;
 }
 
diff --git a/third_party/WebKit/Source/core/frame/DOMWindow.cpp b/third_party/WebKit/Source/core/frame/DOMWindow.cpp
index 6b33d24..e6d1258 100644
--- a/third_party/WebKit/Source/core/frame/DOMWindow.cpp
+++ b/third_party/WebKit/Source/core/frame/DOMWindow.cpp
@@ -378,8 +378,7 @@
   if (!frame()->shouldClose())
     return;
 
-  InspectorInstrumentation::NativeBreakpoint nativeBreakpoint(
-      context, "DOMWindow.close", true);
+  InspectorInstrumentation::breakIfNeeded(context, "DOMWindow.close");
 
   page->closeSoon();
 
diff --git a/third_party/WebKit/Source/core/inspector/BUILD.gn b/third_party/WebKit/Source/core/inspector/BUILD.gn
index ad14bc7..42eec860 100644
--- a/third_party/WebKit/Source/core/inspector/BUILD.gn
+++ b/third_party/WebKit/Source/core/inspector/BUILD.gn
@@ -44,7 +44,6 @@
     "InspectorInputAgent.h",
     "InspectorInstrumentation.cpp",
     "InspectorInstrumentation.h",
-    "InspectorInstrumentationCustomInl.h",
     "InspectorLayerTreeAgent.cpp",
     "InspectorLayerTreeAgent.h",
     "InspectorLogAgent.cpp",
diff --git a/third_party/WebKit/Source/core/inspector/InspectorInstrumentation.cpp b/third_party/WebKit/Source/core/inspector/InspectorInstrumentation.cpp
index 0470465..079454d 100644
--- a/third_party/WebKit/Source/core/inspector/InspectorInstrumentation.cpp
+++ b/third_party/WebKit/Source/core/inspector/InspectorInstrumentation.cpp
@@ -57,7 +57,18 @@
 
 AsyncTask::AsyncTask(ExecutionContext* context, void* task, bool enabled)
     : m_debugger(enabled ? ThreadDebugger::from(toIsolate(context)) : nullptr),
-      m_task(task) {
+      m_task(task),
+      m_breakpoint(nullptr, nullptr) {
+  if (m_debugger)
+    m_debugger->asyncTaskStarted(m_task);
+}
+
+AsyncTask::AsyncTask(ExecutionContext* context,
+                     void* task,
+                     const char* breakpointName)
+    : m_debugger(ThreadDebugger::from(toIsolate(context))),
+      m_task(task),
+      m_breakpoint(context, breakpointName) {
   if (m_debugger)
     m_debugger->asyncTaskStarted(m_task);
 }
@@ -69,45 +80,62 @@
 
 void asyncTaskScheduled(ExecutionContext* context,
                         const String& name,
-                        void* task) {
-  if (ThreadDebugger* debugger = ThreadDebugger::from(toIsolate(context)))
-    debugger->asyncTaskScheduled(name, task, false);
-}
-
-void asyncTaskScheduled(ExecutionContext* context,
-                        const String& name,
                         void* task,
                         bool recurring) {
   if (ThreadDebugger* debugger = ThreadDebugger::from(toIsolate(context)))
     debugger->asyncTaskScheduled(name, task, recurring);
 }
 
+void asyncTaskScheduledBreakable(ExecutionContext* context,
+                                 const char* name,
+                                 void* task,
+                                 bool recurring) {
+  asyncTaskScheduled(context, name, task, recurring);
+  breakIfNeeded(context, name);
+}
+
 void asyncTaskCanceled(ExecutionContext* context, void* task) {
   if (ThreadDebugger* debugger = ThreadDebugger::from(toIsolate(context)))
     debugger->asyncTaskCanceled(task);
 }
 
+void asyncTaskCanceledBreakable(ExecutionContext* context,
+                                const char* name,
+                                void* task) {
+  asyncTaskCanceled(context, task);
+  breakIfNeeded(context, name);
+}
+
 void allAsyncTasksCanceled(ExecutionContext* context) {
   if (ThreadDebugger* debugger = ThreadDebugger::from(toIsolate(context)))
     debugger->allAsyncTasksCanceled();
 }
 
-NativeBreakpoint::NativeBreakpoint(ExecutionContext* context,
-                                   const char* name,
-                                   bool sync)
-    : m_instrumentingAgents(instrumentingAgentsFor(context)), m_sync(sync) {
+void breakIfNeeded(ExecutionContext* context, const char* name) {
+  InstrumentingAgents* instrumentingAgents = instrumentingAgentsFor(context);
+  if (!instrumentingAgents ||
+      !instrumentingAgents->hasInspectorDOMDebuggerAgents())
+    return;
+  for (InspectorDOMDebuggerAgent* domDebuggerAgent :
+       instrumentingAgents->inspectorDOMDebuggerAgents()) {
+    domDebuggerAgent->allowNativeBreakpoint(name, nullptr, true);
+  }
+}
+
+NativeBreakpoint::NativeBreakpoint(ExecutionContext* context, const char* name)
+    : m_instrumentingAgents(instrumentingAgentsFor(context)) {
   if (!m_instrumentingAgents ||
       !m_instrumentingAgents->hasInspectorDOMDebuggerAgents())
     return;
   for (InspectorDOMDebuggerAgent* domDebuggerAgent :
        m_instrumentingAgents->inspectorDOMDebuggerAgents())
-    domDebuggerAgent->allowNativeBreakpoint(name, nullptr, m_sync);
+    domDebuggerAgent->allowNativeBreakpoint(name, nullptr, false);
 }
 
 NativeBreakpoint::NativeBreakpoint(ExecutionContext* context,
                                    EventTarget* eventTarget,
                                    Event* event)
-    : m_instrumentingAgents(instrumentingAgentsFor(context)), m_sync(false) {
+    : m_instrumentingAgents(instrumentingAgentsFor(context)) {
   if (!m_instrumentingAgents ||
       !m_instrumentingAgents->hasInspectorDOMDebuggerAgents())
     return;
@@ -115,11 +143,11 @@
   String targetName = node ? node->nodeName() : eventTarget->interfaceName();
   for (InspectorDOMDebuggerAgent* domDebuggerAgent :
        m_instrumentingAgents->inspectorDOMDebuggerAgents())
-    domDebuggerAgent->allowNativeBreakpoint(event->type(), &targetName, m_sync);
+    domDebuggerAgent->allowNativeBreakpoint(event->type(), &targetName, false);
 }
 
 NativeBreakpoint::~NativeBreakpoint() {
-  if (m_sync || !m_instrumentingAgents ||
+  if (!m_instrumentingAgents ||
       !m_instrumentingAgents->hasInspectorDOMDebuggerAgents())
     return;
   for (InspectorDOMDebuggerAgent* domDebuggerAgent :
@@ -127,10 +155,6 @@
     domDebuggerAgent->cancelNativeBreakpoint();
 }
 
-bool isDebuggerPaused(LocalFrame*) {
-  return MainThreadDebugger::instance()->isPaused();
-}
-
 void didReceiveResourceResponseButCanceled(LocalFrame* frame,
                                            DocumentLoader* loader,
                                            unsigned long identifier,
diff --git a/third_party/WebKit/Source/core/inspector/InspectorInstrumentation.h b/third_party/WebKit/Source/core/inspector/InspectorInstrumentation.h
index 3ccaf34d..7859987 100644
--- a/third_party/WebKit/Source/core/inspector/InspectorInstrumentation.h
+++ b/third_party/WebKit/Source/core/inspector/InspectorInstrumentation.h
@@ -40,35 +40,37 @@
 namespace blink {
 
 class InstrumentingAgents;
+class Resource;
 class ThreadDebugger;
 class WorkerGlobalScope;
 
 namespace InspectorInstrumentation {
 
+class CORE_EXPORT NativeBreakpoint {
+  STACK_ALLOCATED();
+
+ public:
+  NativeBreakpoint(ExecutionContext*, const char* name);
+  NativeBreakpoint(ExecutionContext*, EventTarget*, Event*);
+  ~NativeBreakpoint();
+
+ private:
+  Member<InstrumentingAgents> m_instrumentingAgents;
+};
+
 class CORE_EXPORT AsyncTask {
   STACK_ALLOCATED();
 
  public:
   AsyncTask(ExecutionContext*, void* task);
   AsyncTask(ExecutionContext*, void* task, bool enabled);
+  AsyncTask(ExecutionContext*, void* task, const char* breakpointName);
   ~AsyncTask();
 
  private:
   ThreadDebugger* m_debugger;
   void* m_task;
-};
-
-class CORE_EXPORT NativeBreakpoint {
-  STACK_ALLOCATED();
-
- public:
-  NativeBreakpoint(ExecutionContext*, const char* name, bool sync);
-  NativeBreakpoint(ExecutionContext*, EventTarget*, Event*);
-  ~NativeBreakpoint();
-
- private:
-  Member<InstrumentingAgents> m_instrumentingAgents;
-  bool m_sync;
+  NativeBreakpoint m_breakpoint;
 };
 
 // Called from generated instrumentation code.
@@ -109,13 +111,38 @@
              : nullptr;
 }
 
+CORE_EXPORT void breakIfNeeded(ExecutionContext*, const char* name);
+
+CORE_EXPORT void asyncTaskScheduled(ExecutionContext*,
+                                    const String& name,
+                                    void*,
+                                    bool recurring = false);
+CORE_EXPORT void asyncTaskScheduledBreakable(ExecutionContext*,
+                                             const char* name,
+                                             void*,
+                                             bool recurring = false);
+CORE_EXPORT void asyncTaskCanceled(ExecutionContext*, void*);
+CORE_EXPORT void asyncTaskCanceledBreakable(ExecutionContext*,
+                                            const char* name,
+                                            void*);
+
+CORE_EXPORT void allAsyncTasksCanceled(ExecutionContext*);
+CORE_EXPORT void canceledAfterReceivedResourceResponse(LocalFrame*,
+                                                       DocumentLoader*,
+                                                       unsigned long identifier,
+                                                       const ResourceResponse&,
+                                                       Resource*);
+CORE_EXPORT void continueWithPolicyIgnore(LocalFrame*,
+                                          DocumentLoader*,
+                                          unsigned long identifier,
+                                          const ResourceResponse&,
+                                          Resource*);
+
 }  // namespace InspectorInstrumentation
 }  // namespace blink
 
 #include "core/InspectorInstrumentationInl.h"
 
-#include "core/inspector/InspectorInstrumentationCustomInl.h"
-
 #include "core/InspectorOverridesInl.h"
 
 #endif  // !defined(InspectorInstrumentation_h)
diff --git a/third_party/WebKit/Source/core/inspector/InspectorInstrumentationCustomInl.h b/third_party/WebKit/Source/core/inspector/InspectorInstrumentationCustomInl.h
index 7dadc035..bd80fca 100644
--- a/third_party/WebKit/Source/core/inspector/InspectorInstrumentationCustomInl.h
+++ b/third_party/WebKit/Source/core/inspector/InspectorInstrumentationCustomInl.h
@@ -37,26 +37,6 @@
 
 namespace InspectorInstrumentation {
 
-CORE_EXPORT bool isDebuggerPaused(LocalFrame*);
-CORE_EXPORT void asyncTaskScheduled(ExecutionContext*,
-                                    const String& name,
-                                    void*);
-CORE_EXPORT void asyncTaskScheduled(ExecutionContext*,
-                                    const String& name,
-                                    void*,
-                                    bool recurring);
-CORE_EXPORT void asyncTaskCanceled(ExecutionContext*, void*);
-CORE_EXPORT void allAsyncTasksCanceled(ExecutionContext*);
-CORE_EXPORT void canceledAfterReceivedResourceResponse(LocalFrame*,
-                                                       DocumentLoader*,
-                                                       unsigned long identifier,
-                                                       const ResourceResponse&,
-                                                       Resource*);
-CORE_EXPORT void continueWithPolicyIgnore(LocalFrame*,
-                                          DocumentLoader*,
-                                          unsigned long identifier,
-                                          const ResourceResponse&,
-                                          Resource*);
 
 }  // namespace InspectorInstrumentation
 
diff --git a/third_party/WebKit/Source/core/layout/svg/LayoutSVGResourceContainer.cpp b/third_party/WebKit/Source/core/layout/svg/LayoutSVGResourceContainer.cpp
index f3f7f85..3ad812c 100644
--- a/third_party/WebKit/Source/core/layout/svg/LayoutSVGResourceContainer.cpp
+++ b/third_party/WebKit/Source/core/layout/svg/LayoutSVGResourceContainer.cpp
@@ -101,7 +101,7 @@
     // Add a pending resolution based on the id of the old resource.
     Element* clientElement = toElement(client->node());
     svgTreeScopeResourcesFromElement(clientElement)
-        .addPendingResource(m_id, clientElement);
+        .addPendingResource(m_id, *clientElement);
   }
 
   removeAllClientsFromCache();
diff --git a/third_party/WebKit/Source/core/layout/svg/SVGResources.cpp b/third_party/WebKit/Source/core/layout/svg/SVGResources.cpp
index 18af003d..f33e7b8 100644
--- a/third_party/WebKit/Source/core/layout/svg/SVGResources.cpp
+++ b/third_party/WebKit/Source/core/layout/svg/SVGResources.cpp
@@ -181,13 +181,12 @@
   ASSERT(node);
   SECURITY_DCHECK(node->isSVGElement());
 
-  SVGElement* element = toSVGElement(node);
-  ASSERT(element);
+  SVGElement& element = toSVGElement(*node);
 
-  const AtomicString& tagName = element->localName();
+  const AtomicString& tagName = element.localName();
   ASSERT(!tagName.isNull());
 
-  TreeScope& treeScope = element->treeScopeForIdResolution();
+  TreeScope& treeScope = element.treeScopeForIdResolution();
   SVGTreeScopeResources& treeScopeResources =
       treeScope.ensureSVGTreeScopedResources();
 
@@ -235,7 +234,7 @@
     }
   }
 
-  if (style.hasMarkers() && supportsMarkers(*element)) {
+  if (style.hasMarkers() && supportsMarkers(element)) {
     const AtomicString& markerStartId = style.markerStartResource();
     if (!ensureResources(resources).setMarkerStart(
             getLayoutSVGResourceById<LayoutSVGResourceMarker>(
@@ -278,7 +277,7 @@
   }
 
   if (chainableResourceTags().contains(tagName)) {
-    AtomicString id = targetReferenceFromResource(*element);
+    AtomicString id = targetReferenceFromResource(element);
     if (!ensureResources(resources).setLinkedResource(
             treeScopeResources.resourceById(id)))
       treeScopeResources.addPendingResource(id, element);
diff --git a/third_party/WebKit/Source/core/loader/DocumentLoader.cpp b/third_party/WebKit/Source/core/loader/DocumentLoader.cpp
index bf36005..cf004854 100644
--- a/third_party/WebKit/Source/core/loader/DocumentLoader.cpp
+++ b/third_party/WebKit/Source/core/loader/DocumentLoader.cpp
@@ -29,6 +29,8 @@
 
 #include "core/loader/DocumentLoader.h"
 
+#include <memory>
+
 #include "core/dom/Document.h"
 #include "core/dom/WeakIdentifierMap.h"
 #include "core/events/Event.h"
@@ -42,6 +44,7 @@
 #include "core/html/parser/TextResourceDecoder.h"
 #include "core/inspector/ConsoleMessage.h"
 #include "core/inspector/InspectorInstrumentation.h"
+#include "core/inspector/MainThreadDebugger.h"
 #include "core/loader/FrameFetchContext.h"
 #include "core/loader/FrameLoader.h"
 #include "core/loader/FrameLoaderClient.h"
@@ -74,7 +77,6 @@
 #include "wtf/Assertions.h"
 #include "wtf/AutoReset.h"
 #include "wtf/text/WTFString.h"
-#include <memory>
 
 namespace blink {
 
@@ -302,7 +304,7 @@
 void DocumentLoader::finishedLoading(double finishTime) {
   DCHECK(m_frame->loader().stateMachine()->creatingInitialEmptyDocument() ||
          !m_frame->page()->suspended() ||
-         InspectorInstrumentation::isDebuggerPaused(m_frame));
+         MainThreadDebugger::instance()->isPaused());
 
   double responseEndTime = finishTime;
   if (!responseEndTime)
diff --git a/third_party/WebKit/Source/core/svg/SVGFEImageElement.cpp b/third_party/WebKit/Source/core/svg/SVGFEImageElement.cpp
index d0154e8..c0127df 100644
--- a/third_party/WebKit/Source/core/svg/SVGFEImageElement.cpp
+++ b/third_party/WebKit/Source/core/svg/SVGFEImageElement.cpp
@@ -93,7 +93,7 @@
     if (id.isEmpty()) {
       fetchImageResource();
     } else {
-      treeScope().ensureSVGTreeScopedResources().addPendingResource(id, this);
+      treeScope().ensureSVGTreeScopedResources().addPendingResource(id, *this);
       DCHECK(hasPendingResources());
     }
   } else if (target->isSVGElement()) {
diff --git a/third_party/WebKit/Source/core/svg/SVGMPathElement.cpp b/third_party/WebKit/Source/core/svg/SVGMPathElement.cpp
index a3a6ae5..5ce2f93 100644
--- a/third_party/WebKit/Source/core/svg/SVGMPathElement.cpp
+++ b/third_party/WebKit/Source/core/svg/SVGMPathElement.cpp
@@ -51,10 +51,10 @@
   if (!target) {
     // Do not register as pending if we are already pending this resource.
     if (treeScope().ensureSVGTreeScopedResources().isElementPendingResource(
-            this, id))
+            *this, id))
       return;
     if (!id.isEmpty()) {
-      treeScope().ensureSVGTreeScopedResources().addPendingResource(id, this);
+      treeScope().ensureSVGTreeScopedResources().addPendingResource(id, *this);
       DCHECK(hasPendingResources());
     }
   } else if (isSVGPathElement(target)) {
diff --git a/third_party/WebKit/Source/core/svg/SVGTextPathElement.cpp b/third_party/WebKit/Source/core/svg/SVGTextPathElement.cpp
index f4fa07b5..f6d4b233 100644
--- a/third_party/WebKit/Source/core/svg/SVGTextPathElement.cpp
+++ b/third_party/WebKit/Source/core/svg/SVGTextPathElement.cpp
@@ -128,10 +128,10 @@
   if (!target) {
     // Do not register as pending if we are already pending this resource.
     if (treeScope().ensureSVGTreeScopedResources().isElementPendingResource(
-            this, id))
+            *this, id))
       return;
     if (!id.isEmpty()) {
-      treeScope().ensureSVGTreeScopedResources().addPendingResource(id, this);
+      treeScope().ensureSVGTreeScopedResources().addPendingResource(id, *this);
       DCHECK(hasPendingResources());
     }
   } else if (isSVGPathElement(*target)) {
diff --git a/third_party/WebKit/Source/core/svg/SVGTreeScopeResources.cpp b/third_party/WebKit/Source/core/svg/SVGTreeScopeResources.cpp
index e6b9c97..2dddc1b 100644
--- a/third_party/WebKit/Source/core/svg/SVGTreeScopeResources.cpp
+++ b/third_party/WebKit/Source/core/svg/SVGTreeScopeResources.cpp
@@ -38,7 +38,7 @@
   // Update cached resources of pending clients.
   for (Element* clientElement : *pendingElements) {
     DCHECK(clientElement->hasPendingResources());
-    clearHasPendingResourcesIfPossible(clientElement);
+    clearHasPendingResourcesIfPossible(*clientElement);
 
     LayoutObject* layoutObject = clientElement->layoutObject();
     if (!layoutObject)
@@ -68,89 +68,61 @@
 }
 
 void SVGTreeScopeResources::addPendingResource(const AtomicString& id,
-                                               Element* element) {
-  DCHECK(element);
-  DCHECK(element->isConnected());
+                                               Element& element) {
+  DCHECK(element.isConnected());
 
   if (id.isEmpty())
     return;
-
-  HeapHashMap<AtomicString, Member<SVGPendingElements>>::AddResult result =
-      m_pendingResources.insert(id, nullptr);
+  auto result = m_pendingResources.insert(id, nullptr);
   if (result.isNewEntry)
     result.storedValue->value = new SVGPendingElements;
-  result.storedValue->value->insert(element);
+  result.storedValue->value->insert(&element);
 
-  element->setHasPendingResources();
+  element.setHasPendingResources();
 }
 
-bool SVGTreeScopeResources::hasPendingResource(const AtomicString& id) const {
+bool SVGTreeScopeResources::isElementPendingResource(
+    Element& element,
+    const AtomicString& id) const {
   if (id.isEmpty())
     return false;
-  return m_pendingResources.contains(id);
+  const SVGPendingElements* pendingElements = m_pendingResources.at(id);
+  return pendingElements && pendingElements->contains(&element);
 }
 
-bool SVGTreeScopeResources::isElementPendingResources(Element* element) const {
+void SVGTreeScopeResources::clearHasPendingResourcesIfPossible(
+    Element& element) {
   // This algorithm takes time proportional to the number of pending resources
   // and need not.
   // If performance becomes an issue we can keep a counted set of elements and
   // answer the question efficiently.
-  DCHECK(element);
-
   for (const auto& entry : m_pendingResources) {
     SVGPendingElements* elements = entry.value.get();
     DCHECK(elements);
-    if (elements->contains(element))
-      return true;
+    if (elements->contains(&element))
+      return;
   }
-  return false;
-}
-
-bool SVGTreeScopeResources::isElementPendingResource(
-    Element* element,
-    const AtomicString& id) const {
-  DCHECK(element);
-  if (!hasPendingResource(id))
-    return false;
-  return m_pendingResources.at(id)->contains(element);
-}
-
-void SVGTreeScopeResources::clearHasPendingResourcesIfPossible(
-    Element* element) {
-  if (!isElementPendingResources(element))
-    element->clearHasPendingResources();
+  element.clearHasPendingResources();
 }
 
 void SVGTreeScopeResources::removeElementFromPendingResources(
-    Element* element) {
-  DCHECK(element);
-
+    Element& element) {
+  if (m_pendingResources.isEmpty() || !element.hasPendingResources())
+    return;
   // Remove the element from pending resources.
-  if (!m_pendingResources.isEmpty() && element->hasPendingResources()) {
-    Vector<AtomicString> toBeRemoved;
-    for (const auto& entry : m_pendingResources) {
-      SVGPendingElements* elements = entry.value.get();
-      DCHECK(elements);
-      DCHECK(!elements->isEmpty());
+  Vector<AtomicString> toBeRemoved;
+  for (const auto& entry : m_pendingResources) {
+    SVGPendingElements* elements = entry.value.get();
+    DCHECK(elements);
+    DCHECK(!elements->isEmpty());
 
-      elements->erase(element);
-      if (elements->isEmpty())
-        toBeRemoved.push_back(entry.key);
-    }
-
-    clearHasPendingResourcesIfPossible(element);
-
-    // We use the removePendingResource function here because it deals with set
-    // lifetime correctly.
-    for (const AtomicString& id : toBeRemoved)
-      removePendingResource(id);
+    elements->erase(&element);
+    if (elements->isEmpty())
+      toBeRemoved.push_back(entry.key);
   }
-}
+  m_pendingResources.removeAll(toBeRemoved);
 
-SVGTreeScopeResources::SVGPendingElements*
-SVGTreeScopeResources::removePendingResource(const AtomicString& id) {
-  DCHECK(m_pendingResources.contains(id));
-  return m_pendingResources.take(id);
+  clearHasPendingResourcesIfPossible(element);
 }
 
 void SVGTreeScopeResources::notifyResourceAvailable(const AtomicString& id) {
@@ -174,7 +146,7 @@
     else
       clientElement->buildPendingResource();
 
-    clearHasPendingResourcesIfPossible(clientElement);
+    clearHasPendingResourcesIfPossible(*clientElement);
   }
 }
 
diff --git a/third_party/WebKit/Source/core/svg/SVGTreeScopeResources.h b/third_party/WebKit/Source/core/svg/SVGTreeScopeResources.h
index c5a54a22..8b9c003 100644
--- a/third_party/WebKit/Source/core/svg/SVGTreeScopeResources.h
+++ b/third_party/WebKit/Source/core/svg/SVGTreeScopeResources.h
@@ -25,8 +25,6 @@
   WTF_MAKE_NONCOPYABLE(SVGTreeScopeResources);
 
  public:
-  typedef HeapHashSet<Member<Element>> SVGPendingElements;
-
   explicit SVGTreeScopeResources(TreeScope*);
   ~SVGTreeScopeResources();
 
@@ -37,18 +35,18 @@
   // Pending resources are such which are referenced by any object in the SVG
   // document, but do NOT exist yet. For instance, dynamically built gradients
   // / patterns / clippers...
-  void addPendingResource(const AtomicString& id, Element*);
-  bool hasPendingResource(const AtomicString& id) const;
-  bool isElementPendingResources(Element*) const;
-  bool isElementPendingResource(Element*, const AtomicString& id) const;
+  void addPendingResource(const AtomicString& id, Element&);
+  bool isElementPendingResource(Element&, const AtomicString& id) const;
   void notifyResourceAvailable(const AtomicString& id);
-  void clearHasPendingResourcesIfPossible(Element*);
-  void removeElementFromPendingResources(Element*);
-  SVGPendingElements* removePendingResource(const AtomicString& id);
+  void removeElementFromPendingResources(Element&);
 
   DECLARE_TRACE();
 
  private:
+  void clearHasPendingResourcesIfPossible(Element&);
+
+  using SVGPendingElements = HeapHashSet<Member<Element>>;
+
   HashMap<AtomicString, LayoutSVGResourceContainer*> m_resources;
   // Resources that are pending.
   HeapHashMap<AtomicString, Member<SVGPendingElements>> m_pendingResources;
diff --git a/third_party/WebKit/Source/core/svg/SVGUseElement.cpp b/third_party/WebKit/Source/core/svg/SVGUseElement.cpp
index 89e88c0..947e974 100644
--- a/third_party/WebKit/Source/core/svg/SVGUseElement.cpp
+++ b/third_party/WebKit/Source/core/svg/SVGUseElement.cpp
@@ -317,7 +317,7 @@
   // Don't record any pending references for external resources.
   if (!m_resource) {
     treeScope().ensureSVGTreeScopedResources().addPendingResource(
-        m_elementIdentifier, this);
+        m_elementIdentifier, *this);
     DCHECK(hasPendingResources());
   }
   return nullptr;
diff --git a/third_party/WebKit/Source/core/svg/animation/SVGSMILElement.cpp b/third_party/WebKit/Source/core/svg/animation/SVGSMILElement.cpp
index 8d8ba57..b7f10aa 100644
--- a/third_party/WebKit/Source/core/svg/animation/SVGSMILElement.cpp
+++ b/third_party/WebKit/Source/core/svg/animation/SVGSMILElement.cpp
@@ -209,10 +209,10 @@
   if (!svgTarget) {
     // Do not register as pending if we are already pending this resource.
     if (treeScope().ensureSVGTreeScopedResources().isElementPendingResource(
-            this, id))
+            *this, id))
       return;
     if (!id.isEmpty()) {
-      treeScope().ensureSVGTreeScopedResources().addPendingResource(id, this);
+      treeScope().ensureSVGTreeScopedResources().addPendingResource(id, *this);
       DCHECK(hasPendingResources());
     }
   } else {
@@ -572,10 +572,11 @@
         if (!condition->baseID().isEmpty() &&
             !treeScope()
                  .ensureSVGTreeScopedResources()
-                 .isElementPendingResource(this,
-                                           AtomicString(condition->baseID())))
+                 .isElementPendingResource(*this,
+                                           AtomicString(condition->baseID()))) {
           treeScope().ensureSVGTreeScopedResources().addPendingResource(
-              AtomicString(condition->baseID()), this);
+              AtomicString(condition->baseID()), *this);
+        }
         continue;
       }
       ASSERT(!condition->eventListener());
diff --git a/third_party/WebKit/Source/devtools/BUILD.gn b/third_party/WebKit/Source/devtools/BUILD.gn
index 847b2ad..64ca3e5 100644
--- a/third_party/WebKit/Source/devtools/BUILD.gn
+++ b/third_party/WebKit/Source/devtools/BUILD.gn
@@ -136,11 +136,11 @@
   "front_end/console/module.json",
   "front_end/cookie_table/CookiesTable.js",
   "front_end/cookie_table/module.json",
-  "front_end/css_tracker/cssTrackerListView.css",
-  "front_end/css_tracker/CSSTrackerListView.js",
-  "front_end/css_tracker/cssTrackerView.css",
-  "front_end/css_tracker/CSSTrackerView.js",
-  "front_end/css_tracker/module.json",
+  "front_end/coverage/coverageListView.css",
+  "front_end/coverage/CoverageListView.js",
+  "front_end/coverage/coverageView.css",
+  "front_end/coverage/CoverageView.js",
+  "front_end/coverage/module.json",
   "front_end/data_grid/dataGrid.css",
   "front_end/data_grid/DataGrid.js",
   "front_end/data_grid/module.json",
@@ -827,7 +827,7 @@
   "$resources_out_dir/color_picker/color_picker_module.js",
   "$resources_out_dir/console/console_module.js",
   "$resources_out_dir/cookie_table/cookie_table_module.js",
-  "$resources_out_dir/css_tracker/css_tracker_module.js",
+  "$resources_out_dir/coverage/coverage_module.js",
   "$resources_out_dir/data_grid/data_grid_module.js",
   "$resources_out_dir/devices/devices_module.js",
   "$resources_out_dir/diff/diff_module.js",
diff --git a/third_party/WebKit/Source/devtools/front_end/Runtime.js b/third_party/WebKit/Source/devtools/front_end/Runtime.js
index 1e8f5a1..aa52da34 100644
--- a/third_party/WebKit/Source/devtools/front_end/Runtime.js
+++ b/third_party/WebKit/Source/devtools/front_end/Runtime.js
@@ -781,7 +781,6 @@
       'ui': 'UI',
       'object_ui': 'ObjectUI',
       'perf_ui': 'PerfUI',
-      'css_tracker': 'CSSTracker'
     };
     var namespace = specialCases[this._name] || this._name.split('_').map(a => a.substring(0, 1).toUpperCase() + a.substring(1)).join('');
     self[namespace] = self[namespace] || {};
diff --git a/third_party/WebKit/Source/devtools/front_end/css_tracker/CSSTrackerListView.js b/third_party/WebKit/Source/devtools/front_end/coverage/CoverageListView.js
similarity index 85%
rename from third_party/WebKit/Source/devtools/front_end/css_tracker/CSSTrackerListView.js
rename to third_party/WebKit/Source/devtools/front_end/coverage/CoverageListView.js
index 2233e90..330fa2d 100644
--- a/third_party/WebKit/Source/devtools/front_end/css_tracker/CSSTrackerListView.js
+++ b/third_party/WebKit/Source/devtools/front_end/coverage/CoverageListView.js
@@ -2,10 +2,10 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-CSSTracker.CSSTrackerListView = class extends UI.VBox {
+Coverage.CoverageListView = class extends UI.VBox {
   constructor() {
     super(true);
-    this.registerRequiredCSS('css_tracker/cssTrackerListView.css');
+    this.registerRequiredCSS('coverage/coverageListView.css');
     var columns = [
       {id: 'url', title: Common.UIString('URL'), width: '300px', fixedWidth: false, sortable: true}, {
         id: 'size',
@@ -38,14 +38,14 @@
   }
 
   /**
-   * @param {!Array<!CSSTracker.CoverageInfo>} coverageInfo
+   * @param {!Array<!Coverage.CoverageInfo>} coverageInfo
    */
   update(coverageInfo) {
     var maxSize = coverageInfo.reduce((acc, entry) => Math.max(acc, entry.size), 0);
     var rootNode = this._dataGrid.rootNode();
     rootNode.removeChildren();
     for (var entry of coverageInfo)
-      rootNode.appendChild(new CSSTracker.CSSTrackerListView.GridNode(entry, maxSize));
+      rootNode.appendChild(new Coverage.CoverageListView.GridNode(entry, maxSize));
     this._sortingChanged();
   }
 
@@ -75,7 +75,7 @@
   _revealSourceForNode(node) {
     if (!node)
       return;
-    var coverageInfo = /** @type {!CSSTracker.CSSTrackerListView.GridNode} */ (node)._coverageInfo;
+    var coverageInfo = /** @type {!Coverage.CoverageListView.GridNode} */ (node)._coverageInfo;
     var sourceCode = coverageInfo && Workspace.workspace.uiSourceCodeForURL(coverageInfo.url);
     if (!sourceCode)
       return;
@@ -110,8 +110,8 @@
      * @return {number}
      */
     function compareURL(a, b) {
-      var nodeA = /** @type {!CSSTracker.CSSTrackerListView.GridNode} */ (a);
-      var nodeB = /** @type {!CSSTracker.CSSTrackerListView.GridNode} */ (b);
+      var nodeA = /** @type {!Coverage.CoverageListView.GridNode} */ (a);
+      var nodeB = /** @type {!Coverage.CoverageListView.GridNode} */ (b);
 
       return nodeA._coverageInfo.url.localeCompare(nodeB._coverageInfo.url);
     }
@@ -123,17 +123,17 @@
      * @return {number}
      */
     function compareNumericField(fieldName, a, b) {
-      var nodeA = /** @type {!CSSTracker.CSSTrackerListView.GridNode} */ (a);
-      var nodeB = /** @type {!CSSTracker.CSSTrackerListView.GridNode} */ (b);
+      var nodeA = /** @type {!Coverage.CoverageListView.GridNode} */ (a);
+      var nodeB = /** @type {!Coverage.CoverageListView.GridNode} */ (b);
 
       return nodeA._coverageInfo[fieldName] - nodeB._coverageInfo[fieldName];
     }
   }
 };
 
-CSSTracker.CSSTrackerListView.GridNode = class extends DataGrid.SortableDataGridNode {
+Coverage.CoverageListView.GridNode = class extends DataGrid.SortableDataGridNode {
   /**
-   * @param {!CSSTracker.CoverageInfo} coverageInfo
+   * @param {!Coverage.CoverageInfo} coverageInfo
    * @param {number} maxSize
    */
   constructor(coverageInfo, maxSize) {
diff --git a/third_party/WebKit/Source/devtools/front_end/css_tracker/CSSTrackerView.js b/third_party/WebKit/Source/devtools/front_end/coverage/CoverageView.js
similarity index 75%
rename from third_party/WebKit/Source/devtools/front_end/css_tracker/CSSTrackerView.js
rename to third_party/WebKit/Source/devtools/front_end/coverage/CoverageView.js
index 155332f..08ccde4 100644
--- a/third_party/WebKit/Source/devtools/front_end/css_tracker/CSSTrackerView.js
+++ b/third_party/WebKit/Source/devtools/front_end/coverage/CoverageView.js
@@ -3,23 +3,23 @@
 // found in the LICENSE file.
 
 /** @typedef {{range: !Protocol.CSS.SourceRange, wasUsed: boolean}} */
-CSSTracker.RangeUsage;
+Coverage.RangeUsage;
 
-/** @typedef {{styleSheetHeader: !SDK.CSSStyleSheetHeader, ranges: !Array<!CSSTracker.RangeUsage>}} */
-CSSTracker.StyleSheetUsage;
+/** @typedef {{styleSheetHeader: !SDK.CSSStyleSheetHeader, ranges: !Array<!Coverage.RangeUsage>}} */
+Coverage.StyleSheetUsage;
 
 /** @typedef {{url: string, totalSize: number, unusedSize: number, usedSize: number,
- *      ranges: !Array<!CSSTracker.RangeUsage>}} */
-CSSTracker.CoverageInfo;
+ *      ranges: !Array<!Coverage.RangeUsage>}} */
+Coverage.CoverageInfo;
 
-CSSTracker.CSSTrackerView = class extends UI.VBox {
+Coverage.CoverageView = class extends UI.VBox {
   constructor() {
     super(true);
 
-    this.registerRequiredCSS('css_tracker/cssTrackerView.css');
+    this.registerRequiredCSS('coverage/coverageView.css');
 
-    var toolbarContainer = this.contentElement.createChild('div', 'css-tracker-toolbar-container');
-    var topToolbar = new UI.Toolbar('css-tracker-toolbar', toolbarContainer);
+    var toolbarContainer = this.contentElement.createChild('div', 'coverage-toolbar-container');
+    var topToolbar = new UI.Toolbar('coverage-toolbar', toolbarContainer);
 
     this._recordButton =
         new UI.ToolbarToggle(Common.UIString('Start recording'), 'largeicon-resume', 'largeicon-pause');
@@ -30,24 +30,24 @@
     clearButton.addEventListener(UI.ToolbarButton.Events.Click, this._reset.bind(this));
     topToolbar.appendToolbarItem(clearButton);
 
-    this._cssResultsElement = this.contentElement.createChild('div', 'css-results');
-    this._progressElement = this._cssResultsElement.createChild('div', 'progress-view');
-    this._listView = new CSSTracker.CSSTrackerListView();
+    this._coverageResultsElement = this.contentElement.createChild('div', 'coverage-results');
+    this._progressElement = this._coverageResultsElement.createChild('div', 'progress-view');
+    this._listView = new Coverage.CoverageListView();
 
-    this._statusToolbarElement = this.contentElement.createChild('div', 'css-toolbar-summary');
-    this._statusMessageElement = this._statusToolbarElement.createChild('div', 'css-message');
+    this._statusToolbarElement = this.contentElement.createChild('div', 'coverage-toolbar-summary');
+    this._statusMessageElement = this._statusToolbarElement.createChild('div', 'coverage-message');
 
     this._isRecording = false;
   }
 
   _reset() {
     Workspace.workspace.uiSourceCodes().forEach(
-        uiSourceCode => uiSourceCode.removeDecorationsForType(CSSTracker.CSSTrackerView.LineDecorator.type));
+        uiSourceCode => uiSourceCode.removeDecorationsForType(Coverage.CoverageView.LineDecorator.type));
 
     this._listView.detach();
-    this._cssResultsElement.removeChildren();
+    this._coverageResultsElement.removeChildren();
     this._progressElement.textContent = '';
-    this._cssResultsElement.appendChild(this._progressElement);
+    this._coverageResultsElement.appendChild(this._progressElement);
 
     this._statusMessageElement.textContent = '';
   }
@@ -98,11 +98,11 @@
 
     /**
      * @param {!Array<!SDK.CSSModel.RuleUsage>} ruleUsageList
-     * @this {!CSSTracker.CSSTrackerView}
-     * @return {!Promise<!Array<!CSSTracker.CoverageInfo>>}
+     * @this {!Coverage.CoverageView}
+     * @return {!Promise<!Array<!Coverage.CoverageInfo>>}
      */
     function processRuleList(ruleUsageList) {
-      /** @type {!Map<?SDK.CSSStyleSheetHeader, !Array<!CSSTracker.RangeUsage>>} */
+      /** @type {!Map<?SDK.CSSStyleSheetHeader, !Array<!Coverage.RangeUsage>>} */
       var rulesByStyleSheet = new Map();
       for (var rule of ruleUsageList) {
         var styleSheetHeader = cssModel.styleSheetHeaderForId(rule.styleSheetId);
@@ -118,21 +118,21 @@
     }
 
     /**
-     * @param {!Array<!CSSTracker.CoverageInfo>} coverageInfo
-     * @this {!CSSTracker.CSSTrackerView}
+     * @param {!Array<!Coverage.CoverageInfo>} coverageInfo
+     * @this {!Coverage.CoverageView}
      */
     function updateViews(coverageInfo) {
       coverageInfo = coalesceByURL(coverageInfo);
       this._updateStats(coverageInfo);
       this._updateGutter(coverageInfo);
-      this._cssResultsElement.removeChildren();
+      this._coverageResultsElement.removeChildren();
       this._listView.update(coverageInfo);
-      this._listView.show(this._cssResultsElement);
+      this._listView.show(this._coverageResultsElement);
     }
 
     /**
-     * @param {!Array<!CSSTracker.CoverageInfo>} coverageInfo
-     * @return {!Array<!CSSTracker.CoverageInfo>}
+     * @param {!Array<!Coverage.CoverageInfo>} coverageInfo
+     * @return {!Array<!Coverage.CoverageInfo>}
      */
     function coalesceByURL(coverageInfo) {
       coverageInfo.sort((a, b) => (a.url || '').localeCompare(b.url));
@@ -155,8 +155,8 @@
 
   /**
    * @param {!SDK.CSSStyleSheetHeader} styleSheetHeader
-   * @param {!Array<!CSSTracker.RangeUsage>} ranges
-   * @return {!Promise<!CSSTracker.CoverageInfo>}
+   * @param {!Array<!Coverage.RangeUsage>} ranges
+   * @return {!Promise<!Coverage.CoverageInfo>}
    */
   _convertToCoverageInfo(styleSheetHeader, ranges) {
     var coverageInfo = {
@@ -187,7 +187,7 @@
   }
 
   /**
-   * @param {!Array<!CSSTracker.CoverageInfo>} coverageInfo
+   * @param {!Array<!Coverage.CoverageInfo>} coverageInfo
    */
   _updateStats(coverageInfo) {
     var total = 0;
@@ -203,7 +203,7 @@
   }
 
   /**
-   * @param {!Array<!CSSTracker.CoverageInfo>} coverageInfo
+   * @param {!Array<!Coverage.CoverageInfo>} coverageInfo
    */
   _updateGutter(coverageInfo) {
     for (var info of coverageInfo) {
@@ -214,7 +214,7 @@
         var gutterRange = Common.TextRange.fromObject(range.range);
         if (gutterRange.startColumn)
           gutterRange.startColumn--;
-        uiSourceCode.addDecoration(gutterRange, CSSTracker.CSSTrackerView.LineDecorator.type, range.wasUsed);
+        uiSourceCode.addDecoration(gutterRange, Coverage.CoverageView.LineDecorator.type, range.wasUsed);
       }
     }
   }
@@ -223,7 +223,7 @@
 /**
  * @implements {SourceFrame.UISourceCodeFrame.LineDecorator}
  */
-CSSTracker.CSSTrackerView.LineDecorator = class {
+Coverage.CoverageView.LineDecorator = class {
   /**
    * @override
    * @param {!Workspace.UISourceCode} uiSourceCode
@@ -232,7 +232,7 @@
   decorate(uiSourceCode, textEditor) {
     var gutterType = 'CodeMirror-gutter-coverage';
 
-    var decorations = uiSourceCode.decorationsForType(CSSTracker.CSSTrackerView.LineDecorator.type);
+    var decorations = uiSourceCode.decorationsForType(Coverage.CoverageView.LineDecorator.type);
     textEditor.uninstallGutter(gutterType);
     if (!decorations || !decorations.size)
       return;
@@ -243,9 +243,9 @@
       for (var line = decoration.range().startLine; line <= decoration.range().endLine; ++line) {
         var element = createElementWithClass('div');
         if (decoration.data())
-          element.className = 'text-editor-css-rule-used-marker';
+          element.className = 'text-editor-coverage-used-marker';
         else
-          element.className = 'text-editor-css-rule-unused-marker';
+          element.className = 'text-editor-coverage-unused-marker';
 
         textEditor.setGutterDecoration(line, gutterType, element);
       }
@@ -253,4 +253,4 @@
   }
 };
 
-CSSTracker.CSSTrackerView.LineDecorator.type = 'coverage';
+Coverage.CoverageView.LineDecorator.type = 'coverage';
diff --git a/third_party/WebKit/Source/devtools/front_end/coverage/coverageListView.css b/third_party/WebKit/Source/devtools/front_end/coverage/coverageListView.css
new file mode 100644
index 0000000..50e33bb
--- /dev/null
+++ b/third_party/WebKit/Source/devtools/front_end/coverage/coverageListView.css
@@ -0,0 +1,20 @@
+.data-grid td .bar {
+  display: inline-block;
+  height: 8px;
+}
+
+.data-grid .selected td .bar {
+  border: 0.5px solid white;
+}
+
+.data-grid td .bar-slack-size {
+  background-color: rgb(150, 150, 200);
+}
+
+.data-grid td .bar-unused-size {
+  background-color: #E57373;
+}
+
+.data-grid td .bar-used-size {
+  background-color: #81C784;
+}
diff --git a/third_party/WebKit/Source/devtools/front_end/css_tracker/cssTrackerView.css b/third_party/WebKit/Source/devtools/front_end/coverage/coverageView.css
similarity index 82%
rename from third_party/WebKit/Source/devtools/front_end/css_tracker/cssTrackerView.css
rename to third_party/WebKit/Source/devtools/front_end/coverage/coverageView.css
index acfab6d..9237343 100644
--- a/third_party/WebKit/Source/devtools/front_end/css_tracker/cssTrackerView.css
+++ b/third_party/WebKit/Source/devtools/front_end/coverage/coverageView.css
@@ -8,17 +8,17 @@
     overflow: hidden;
 }
 
-.css-tracker-toolbar-container {
+.coverage-toolbar-container {
     display: flex;
     border-bottom: 1px solid #ccc;
     flex: 0 0;
 }
 
-.css-tracker-toolbar {
+.coverage-toolbar {
     display: inline-block;
 }
 
-.css-toolbar-summary {
+.coverage-toolbar-summary {
     background-color: #eee;
     border-top: 1px solid #ccc;
     padding-left: 5px;
@@ -27,7 +27,7 @@
     padding-right: 5px;
 }
 
-.css-toolbar-summary .css-message {
+.coverage-toolbar-summary .coverage-message {
     padding-top: 2px;
     padding-left: 1ex;
     text-overflow: ellipsis;
@@ -35,7 +35,7 @@
     overflow: hidden;
 }
 
-.css-results {
+.coverage-results {
     overflow-y: auto;
     display: flex;
     flex: auto;
@@ -49,6 +49,6 @@
     font-size: 30px;
 }
 
-.css-results > div {
+.coverage-results > div {
     flex: auto;
 }
diff --git a/third_party/WebKit/Source/devtools/front_end/css_tracker/module.json b/third_party/WebKit/Source/devtools/front_end/coverage/module.json
similarity index 61%
rename from third_party/WebKit/Source/devtools/front_end/css_tracker/module.json
rename to third_party/WebKit/Source/devtools/front_end/coverage/module.json
index d40d46d5..2998cd7 100644
--- a/third_party/WebKit/Source/devtools/front_end/css_tracker/module.json
+++ b/third_party/WebKit/Source/devtools/front_end/coverage/module.json
@@ -3,16 +3,16 @@
         {
             "type": "view",
             "location": "drawer-view",
-            "id": "css_tracker",
-            "title": "CSS Tracker",
+            "id": "coverage",
+            "title": "Coverage",
             "persistence": "closeable",
             "order": 0,
-            "className": "CSSTracker.CSSTrackerView",
+            "className": "Coverage.CoverageView",
             "experiment": "cssTrackerPanel"
         },
         {
             "type": "@SourceFrame.UISourceCodeFrame.LineDecorator",
-            "className": "CSSTracker.CSSTrackerView.LineDecorator",
+            "className": "Coverage.CoverageView.LineDecorator",
             "decoratorType": "coverage"
         }
     ],
@@ -23,11 +23,11 @@
         "data_grid"
     ],
     "scripts": [
-        "CSSTrackerListView.js",
-        "CSSTrackerView.js"
+        "CoverageListView.js",
+        "CoverageView.js"
     ],
     "resources": [
-        "cssTrackerListView.css",
-        "cssTrackerView.css"
+        "coverageListView.css",
+        "coverageView.css"
     ]
 }
diff --git a/third_party/WebKit/Source/devtools/front_end/inspector.json b/third_party/WebKit/Source/devtools/front_end/inspector.json
index 1440912..f63d9ddd 100644
--- a/third_party/WebKit/Source/devtools/front_end/inspector.json
+++ b/third_party/WebKit/Source/devtools/front_end/inspector.json
@@ -42,7 +42,7 @@
         { "name": "sass", "condition": "!v8only" },
         { "name": "accessibility", "condition": "!v8only", "type": "remote" },
         { "name": "animation", "condition": "!v8only" },
-        { "name": "css_tracker", "condition": "!v8only" },
+        { "name": "coverage", "condition": "!v8only" },
         { "name": "screencast", "condition": "remoteFrontend", "type": "remote" },
         { "name": "emulated_devices", "condition": "!v8only" , "type": "remote" },
         { "name": "perf_ui" },
diff --git a/third_party/WebKit/Source/devtools/front_end/main/Main.js b/third_party/WebKit/Source/devtools/front_end/main/Main.js
index 1b3f3984..3e144fe 100644
--- a/third_party/WebKit/Source/devtools/front_end/main/Main.js
+++ b/third_party/WebKit/Source/devtools/front_end/main/Main.js
@@ -126,7 +126,7 @@
       // Enable experiments for testing.
       if (testPath.indexOf('accessibility/') !== -1)
         Runtime.experiments.enableForTest('accessibilityInspection');
-      if (testPath.indexOf('css_tracker') !== -1)
+      if (testPath.indexOf('coverage') !== -1)
         Runtime.experiments.enableForTest('cssTrackerPanel');
       if (testPath.indexOf('audits2/') !== -1)
         Runtime.experiments.enableForTest('audits2');
diff --git a/third_party/WebKit/Source/devtools/front_end/profiler/HeapProfileView.js b/third_party/WebKit/Source/devtools/front_end/profiler/HeapProfileView.js
index 5e8d5b2d..9a4962a 100644
--- a/third_party/WebKit/Source/devtools/front_end/profiler/HeapProfileView.js
+++ b/third_party/WebKit/Source/devtools/front_end/profiler/HeapProfileView.js
@@ -96,16 +96,16 @@
   }
 
   startRecordingProfile() {
-    var target = UI.context.flavor(SDK.Target);
-    if (this.profileBeingRecorded() || !target)
+    var heapProfilerModel = UI.context.flavor(SDK.HeapProfilerModel);
+    if (this.profileBeingRecorded() || !heapProfilerModel)
       return;
-    var profile = new Profiler.SamplingHeapProfileHeader(target, this);
+    var profile = new Profiler.SamplingHeapProfileHeader(heapProfilerModel.target(), this);
     this.setProfileBeingRecorded(profile);
     SDK.targetManager.suspendAllTargets();
     this.addProfile(profile);
     profile.updateStatus(Common.UIString('Recording\u2026'));
     this._recording = true;
-    target.heapProfilerModel.startSampling();
+    heapProfilerModel.startSampling();
   }
 
   stopRecordingProfile() {
@@ -136,8 +136,9 @@
       this.dispatchEventToListeners(Profiler.ProfileType.Events.ProfileComplete, recordedProfile);
     }
 
-    this.profileBeingRecorded().target()
-        .heapProfilerModel.stopSampling()
+    var heapProfilerModel =
+        /** @type {!SDK.HeapProfilerModel} */ (this.profileBeingRecorded().target().model(SDK.HeapProfilerModel));
+    heapProfilerModel.stopSampling()
         .then(didStopProfiling.bind(this))
         .then(SDK.targetManager.resumeAllTargets.bind(SDK.targetManager))
         .then(fireEvent.bind(this));
diff --git a/third_party/WebKit/Source/devtools/front_end/profiler/HeapProfilerPanel.js b/third_party/WebKit/Source/devtools/front_end/profiler/HeapProfilerPanel.js
index 95633d0..fdb7fc82 100644
--- a/third_party/WebKit/Source/devtools/front_end/profiler/HeapProfilerPanel.js
+++ b/third_party/WebKit/Source/devtools/front_end/profiler/HeapProfilerPanel.js
@@ -37,7 +37,7 @@
     if (!heapProfiles.length)
       return;
 
-    var heapProfilerModel = object.target().heapProfilerModel;
+    var heapProfilerModel = object.target().model(SDK.HeapProfilerModel);
     if (!heapProfilerModel)
       return;
 
@@ -46,7 +46,7 @@
      * @this {Profiler.ProfilesPanel}
      */
     function revealInView(viewName) {
-      heapProfilerModel.snapshotObjectIdForObjectId(objectId, result => {
+      heapProfilerModel.snapshotObjectIdForObjectId(objectId).then(result => {
         if (this.isShowing() && result)
           this.showObject(result, viewName);
       });
diff --git a/third_party/WebKit/Source/devtools/front_end/profiler/HeapSnapshotGridNodes.js b/third_party/WebKit/Source/devtools/front_end/profiler/HeapSnapshotGridNodes.js
index 5fffd50..6ded00de 100644
--- a/third_party/WebKit/Source/devtools/front_end/profiler/HeapSnapshotGridNodes.js
+++ b/third_party/WebKit/Source/devtools/front_end/profiler/HeapSnapshotGridNodes.js
@@ -618,7 +618,7 @@
           target.runtimeModel.createRemoteObjectFromPrimitiveValue(Common.UIString('Preview is not available')));
     }
 
-    var heapProfilerModel = target.heapProfilerModel;
+    var heapProfilerModel = target.model(SDK.HeapProfilerModel);
     if (this._type === 'string')
       onResult(target.runtimeModel.createRemoteObjectFromPrimitiveValue(this._name));
     else if (!heapProfilerModel)
diff --git a/third_party/WebKit/Source/devtools/front_end/profiler/HeapSnapshotView.js b/third_party/WebKit/Source/devtools/front_end/profiler/HeapSnapshotView.js
index 78bc3a0..5d0d915 100644
--- a/third_party/WebKit/Source/devtools/front_end/profiler/HeapSnapshotView.js
+++ b/third_party/WebKit/Source/devtools/front_end/profiler/HeapSnapshotView.js
@@ -463,8 +463,9 @@
   _inspectedObjectChanged(event) {
     var selectedNode = /** @type {!DataGrid.DataGridNode} */ (event.data);
     var target = this._profile.target();
-    if (target && selectedNode instanceof Profiler.HeapSnapshotGenericObjectNode)
-      target.heapProfilerModel.addInspectedHeapObject(String(selectedNode.snapshotNodeId));
+    var heapProfilerModel = target ? target.model(SDK.HeapProfilerModel) : null;
+    if (heapProfilerModel && selectedNode instanceof Profiler.HeapSnapshotGenericObjectNode)
+      heapProfilerModel.addInspectedHeapObject(String(selectedNode.snapshotNodeId));
   }
 
   /**
@@ -953,7 +954,7 @@
 };
 
 /**
- * @implements {SDK.TargetManager.Observer}
+ * @implements {SDK.SDKModelObserver<!SDK.HeapProfilerModel>}
  * @unrestricted
  */
 Profiler.HeapSnapshotProfileType = class extends Profiler.ProfileType {
@@ -963,7 +964,7 @@
    */
   constructor(id, title) {
     super(id || Profiler.HeapSnapshotProfileType.TypeId, title || Common.UIString('Take Heap Snapshot'));
-    SDK.targetManager.observeTargets(this);
+    SDK.targetManager.observeModels(SDK.HeapProfilerModel, this);
     SDK.targetManager.addModelListener(
         SDK.HeapProfilerModel, SDK.HeapProfilerModel.Events.ResetProfiles, this._resetProfiles, this);
     SDK.targetManager.addModelListener(
@@ -975,17 +976,17 @@
 
   /**
    * @override
-   * @param {!SDK.Target} target
+   * @param {!SDK.HeapProfilerModel} heapProfilerModel
    */
-  targetAdded(target) {
-    target.heapProfilerModel.enable();
+  modelAdded(heapProfilerModel) {
+    heapProfilerModel.enable();
   }
 
   /**
    * @override
-   * @param {!SDK.Target} target
+   * @param {!SDK.HeapProfilerModel} heapProfilerModel
    */
-  targetRemoved(target) {
+  modelRemoved(heapProfilerModel) {
   }
 
   /**
@@ -1039,13 +1040,16 @@
   _takeHeapSnapshot(callback) {
     if (this.profileBeingRecorded())
       return;
-    var target = /** @type {!SDK.Target} */ (UI.context.flavor(SDK.Target));
-    var profile = new Profiler.HeapProfileHeader(target, this);
+    var heapProfilerModel = UI.context.flavor(SDK.HeapProfilerModel);
+    if (!heapProfilerModel)
+      return;
+
+    var profile = new Profiler.HeapProfileHeader(heapProfilerModel.target(), this);
     this.setProfileBeingRecorded(profile);
     this.addProfile(profile);
     profile.updateStatus(Common.UIString('Snapshotting\u2026'));
 
-    target.heapProfilerModel.takeHeapSnapshot(true).then(success => {
+    heapProfilerModel.takeHeapSnapshot(true).then(success => {
       var profile = this.profileBeingRecorded();
       profile.title = Common.UIString('Snapshot %d', profile.uid);
       profile._finishLoad();
@@ -1078,8 +1082,12 @@
       profile._prepareToLoad();
   }
 
-  _resetProfiles() {
-    this.reset();
+  /**
+   * @param {!Common.Event} event
+   */
+  _resetProfiles(event) {
+    var heapProfilerModel = /** @type {!SDK.HeapProfilerModel} */ (event.data);
+    this.reset(heapProfilerModel.target());
   }
 
   _snapshotReceived(profile) {
@@ -1102,26 +1110,22 @@
 
   /**
    * @override
-   * @param {!SDK.Target} target
+   * @param {!SDK.HeapProfilerModel} heapProfilerModel
    */
-  targetAdded(target) {
-    super.targetAdded(target);
-    target.heapProfilerModel.addEventListener(
-        SDK.HeapProfilerModel.Events.HeapStatsUpdate, this._heapStatsUpdate, this);
-    target.heapProfilerModel.addEventListener(
-        SDK.HeapProfilerModel.Events.LastSeenObjectId, this._lastSeenObjectId, this);
+  modelAdded(heapProfilerModel) {
+    super.modelAdded(heapProfilerModel);
+    heapProfilerModel.addEventListener(SDK.HeapProfilerModel.Events.HeapStatsUpdate, this._heapStatsUpdate, this);
+    heapProfilerModel.addEventListener(SDK.HeapProfilerModel.Events.LastSeenObjectId, this._lastSeenObjectId, this);
   }
 
   /**
    * @override
-   * @param {!SDK.Target} target
+   * @param {!SDK.HeapProfilerModel} heapProfilerModel
    */
-  targetRemoved(target) {
-    super.targetRemoved(target);
-    target.heapProfilerModel.removeEventListener(
-        SDK.HeapProfilerModel.Events.HeapStatsUpdate, this._heapStatsUpdate, this);
-    target.heapProfilerModel.removeEventListener(
-        SDK.HeapProfilerModel.Events.LastSeenObjectId, this._lastSeenObjectId, this);
+  modelRemoved(heapProfilerModel) {
+    super.modelRemoved(heapProfilerModel);
+    heapProfilerModel.removeEventListener(SDK.HeapProfilerModel.Events.HeapStatsUpdate, this._heapStatsUpdate, this);
+    heapProfilerModel.removeEventListener(SDK.HeapProfilerModel.Events.LastSeenObjectId, this._lastSeenObjectId, this);
   }
 
   /**
@@ -1194,23 +1198,33 @@
   _startRecordingProfile() {
     if (this.profileBeingRecorded())
       return;
-    this._addNewProfile();
+    var heapProfilerModel = this._addNewProfile();
+    if (!heapProfilerModel)
+      return;
     var recordAllocationStacks = Common.moduleSetting('recordAllocationStacks').get();
-    this.profileBeingRecorded().target().heapProfilerModel.startTrackingHeapObjects(recordAllocationStacks);
+    heapProfilerModel.startTrackingHeapObjects(recordAllocationStacks);
   }
 
+  /**
+   * @return {?SDK.HeapProfilerModel}
+   */
   _addNewProfile() {
-    var target = UI.context.flavor(SDK.Target);
-    this.setProfileBeingRecorded(new Profiler.HeapProfileHeader(target, this, undefined));
+    var heapProfilerModel = UI.context.flavor(SDK.HeapProfilerModel);
+    if (!heapProfilerModel)
+      return null;
+    this.setProfileBeingRecorded(new Profiler.HeapProfileHeader(heapProfilerModel.target(), this, undefined));
     this._profileSamples = new Profiler.TrackingHeapSnapshotProfileType.Samples();
     this.profileBeingRecorded()._profileSamples = this._profileSamples;
     this._recording = true;
     this.addProfile(/** @type {!Profiler.ProfileHeader} */ (this.profileBeingRecorded()));
     this.profileBeingRecorded().updateStatus(Common.UIString('Recording\u2026'));
     this.dispatchEventToListeners(Profiler.TrackingHeapSnapshotProfileType.TrackingStarted);
+    return heapProfilerModel;
   }
 
   _stopRecordingProfile() {
+    var heapProfilerModel =
+        /** @type {!SDK.HeapProfilerModel} */ (this.profileBeingRecorded().target().model(SDK.HeapProfilerModel));
     this.profileBeingRecorded().updateStatus(Common.UIString('Snapshotting\u2026'));
     /**
      * @param {boolean} success
@@ -1226,8 +1240,7 @@
       this.dispatchEventToListeners(Profiler.ProfileType.Events.ProfileComplete, profile);
     }
 
-    this.profileBeingRecorded().target().heapProfilerModel.stopTrackingHeapObjects(true).then(
-        didTakeHeapSnapshot.bind(this));
+    heapProfilerModel.stopTrackingHeapObjects(true).then(didTakeHeapSnapshot.bind(this));
     this._recording = false;
     this.dispatchEventToListeners(Profiler.TrackingHeapSnapshotProfileType.TrackingStopped);
   }
@@ -1259,12 +1272,13 @@
 
   /**
    * @override
+   * @param {!Common.Event} event
    */
-  _resetProfiles() {
+  _resetProfiles(event) {
     var wasRecording = this._recording;
     // Clear current profile to avoid stopping backend.
     this.setProfileBeingRecorded(null);
-    super._resetProfiles();
+    super._resetProfiles(event);
     this._profileSamples = null;
     if (wasRecording)
       this._addNewProfile();
diff --git a/third_party/WebKit/Source/devtools/front_end/profiler/ProfileLauncherView.js b/third_party/WebKit/Source/devtools/front_end/profiler/ProfileLauncherView.js
index 9dcb217..626ebe9 100644
--- a/third_party/WebKit/Source/devtools/front_end/profiler/ProfileLauncherView.js
+++ b/third_party/WebKit/Source/devtools/front_end/profiler/ProfileLauncherView.js
@@ -155,17 +155,12 @@
    */
   constructor(profilesPanel) {
     super(profilesPanel);
-
     this._selectedProfileTypeSetting = Common.settings.createSetting('selectedProfileType', 'CPU');
-
-    var header = this._innerContentElement.createChild('h1');
-    header.textContent = Common.UIString('Select profiling type');
-
+    this._header = this._innerContentElement.createChild('h1');
     this._profileTypeSelectorForm = this._innerContentElement.createChild('form');
-
     this._innerContentElement.createChild('div', 'flexible-space');
-
-    this._typeIdToOptionElement = {};
+    /** @type {!Map<string, !HTMLOptionElement>} */
+    this._typeIdToOptionElement = new Map();
   }
 
   /**
@@ -176,23 +171,27 @@
     var labelElement = UI.createRadioLabel('profile-type', profileType.name);
     this._profileTypeSelectorForm.appendChild(labelElement);
     var optionElement = labelElement.radioElement;
-    this._typeIdToOptionElement[profileType.id] = optionElement;
+    this._typeIdToOptionElement.set(profileType.id, optionElement);
     optionElement._profileType = profileType;
     optionElement.style.hidden = true;
     optionElement.addEventListener('change', this._profileTypeChanged.bind(this, profileType), false);
-    var descriptionElement = labelElement.createChild('p');
+    var descriptionElement = this._profileTypeSelectorForm.createChild('p');
     descriptionElement.textContent = profileType.description;
     var decorationElement = profileType.decorationElement();
     if (decorationElement)
       labelElement.appendChild(decorationElement);
+    if (this._typeIdToOptionElement.size > 1)
+      this._header.textContent = Common.UIString('Select profiling type');
+    else
+      this._header.textContent = profileType.name;
   }
 
   restoreSelectedProfileType() {
     var typeId = this._selectedProfileTypeSetting.get();
-    if (!(typeId in this._typeIdToOptionElement))
-      typeId = Object.keys(this._typeIdToOptionElement)[0];
-    this._typeIdToOptionElement[typeId].checked = true;
-    var type = this._typeIdToOptionElement[typeId]._profileType;
+    if (!this._typeIdToOptionElement.has(typeId))
+      typeId = this._typeIdToOptionElement.keys().next().value;
+    this._typeIdToOptionElement.get(typeId).checked = true;
+    var type = this._typeIdToOptionElement.get(typeId)._profileType;
     this.dispatchEventToListeners(Profiler.MultiProfileLauncherView.Events.ProfileTypeSelected, type);
   }
 
diff --git a/third_party/WebKit/Source/devtools/front_end/profiler/ProfileType.js b/third_party/WebKit/Source/devtools/front_end/profiler/ProfileType.js
index d8b6f75..f7a9601 100644
--- a/third_party/WebKit/Source/devtools/front_end/profiler/ProfileType.js
+++ b/third_party/WebKit/Source/devtools/front_end/profiler/ProfileType.js
@@ -199,9 +199,18 @@
   profileBeingRecordedRemoved() {
   }
 
-  reset() {
-    this._profiles.slice(0).forEach(this._disposeProfile.bind(this));
-    this._profiles = [];
+  /**
+   * @param {!SDK.Target=} target
+   */
+  reset(target) {
+    var profilesLeft = [];
+    for (var profile of this._profiles.slice()) {
+      if (!target || profile.target() === target)
+        this._disposeProfile(profile);
+      else
+        profilesLeft.push(profile);
+    }
+    this._profiles = profilesLeft;
     this._nextProfileUid = 1;
   }
 
diff --git a/third_party/WebKit/Source/devtools/front_end/profiler/TargetsComboBoxController.js b/third_party/WebKit/Source/devtools/front_end/profiler/TargetsComboBoxController.js
index ec209c5..9326f19 100644
--- a/third_party/WebKit/Source/devtools/front_end/profiler/TargetsComboBoxController.js
+++ b/third_party/WebKit/Source/devtools/front_end/profiler/TargetsComboBoxController.js
@@ -32,8 +32,11 @@
     option.text = target.name();
     option.__target = target;
     this._targetToOption.set(target, option);
-    if (UI.context.flavor(SDK.Target) === target)
+    if (UI.context.flavor(SDK.Target) === target) {
       this._selectElement.selectedIndex = Array.prototype.indexOf.call(/** @type {?} */ (this._selectElement), option);
+      UI.context.setFlavor(SDK.HeapProfilerModel, target.model(SDK.HeapProfilerModel));
+      UI.context.setFlavor(SDK.CPUProfilerModel, target.model(SDK.CPUProfilerModel));
+    }
 
     this._updateVisibility();
   }
@@ -63,6 +66,8 @@
       return;
 
     UI.context.setFlavor(SDK.Target, selectedOption.__target);
+    UI.context.setFlavor(SDK.HeapProfilerModel, selectedOption.__target.model(SDK.HeapProfilerModel));
+    UI.context.setFlavor(SDK.CPUProfilerModel, selectedOption.__target.model(SDK.CPUProfilerModel));
   }
 
   _updateVisibility() {
@@ -75,10 +80,14 @@
    */
   _targetChangedExternally(event) {
     var target = /** @type {?SDK.Target} */ (event.data);
-    if (target) {
-      var option = /** @type {!Element} */ (this._targetToOption.get(target));
-      this._select(option);
-    }
+    if (!target)
+      return;
+    var option = this._targetToOption.get(target);
+    if (!option)
+      return;
+    this._select(option);
+    UI.context.setFlavor(SDK.HeapProfilerModel, target.model(SDK.HeapProfilerModel));
+    UI.context.setFlavor(SDK.CPUProfilerModel, target.model(SDK.CPUProfilerModel));
   }
 
   /**
diff --git a/third_party/WebKit/Source/devtools/front_end/sdk/DebuggerModel.js b/third_party/WebKit/Source/devtools/front_end/sdk/DebuggerModel.js
index 240f118..33c71ee 100644
--- a/third_party/WebKit/Source/devtools/front_end/sdk/DebuggerModel.js
+++ b/third_party/WebKit/Source/devtools/front_end/sdk/DebuggerModel.js
@@ -274,7 +274,8 @@
   getPossibleBreakpoints(startLocation, endLocation) {
     var fulfill;
     var promise = new Promise(resolve => fulfill = resolve);
-    this._agent.getPossibleBreakpoints(startLocation.payload(), endLocation.payload(), checkErrorAndReturn.bind(this));
+    this._agent.invoke_getPossibleBreakpoints(
+        {start: startLocation.payload(), end: endLocation.payload()}, checkErrorAndReturn.bind(this));
     return promise;
 
     /**
diff --git a/third_party/WebKit/Source/devtools/front_end/sdk/HeapProfilerModel.js b/third_party/WebKit/Source/devtools/front_end/sdk/HeapProfilerModel.js
index 96e1c663..a62dbe4 100644
--- a/third_party/WebKit/Source/devtools/front_end/sdk/HeapProfilerModel.js
+++ b/third_party/WebKit/Source/devtools/front_end/sdk/HeapProfilerModel.js
@@ -25,7 +25,7 @@
   }
 
   /**
-   * @return {!Promise.<?Protocol.Profiler.Profile>}
+   * @return {!Promise.<?Protocol.HeapProfiler.SamplingHeapProfile>}
    */
   stopSampling() {
     this._isRecording = false;
@@ -126,12 +126,11 @@
   }
 
   resetProfiles() {
-    this.dispatchEventToListeners(SDK.HeapProfilerModel.Events.ResetProfiles);
+    this.dispatchEventToListeners(SDK.HeapProfilerModel.Events.ResetProfiles, this);
   }
 };
 
-// TODO(dgozman): should be JS.
-SDK.SDKModel.register(SDK.HeapProfilerModel, SDK.Target.Capability.None);
+SDK.SDKModel.register(SDK.HeapProfilerModel, SDK.Target.Capability.JS);
 
 /** @enum {symbol} */
 SDK.HeapProfilerModel.Events = {
diff --git a/third_party/WebKit/Source/devtools/front_end/sdk/TargetManager.js b/third_party/WebKit/Source/devtools/front_end/sdk/TargetManager.js
index 6c3d7dd8..9ab919f 100644
--- a/third_party/WebKit/Source/devtools/front_end/sdk/TargetManager.js
+++ b/third_party/WebKit/Source/devtools/front_end/sdk/TargetManager.js
@@ -257,8 +257,6 @@
     target.subTargetsManager = target.model(SDK.SubTargetsManager);
     /** @type {!SDK.CPUProfilerModel} */
     target.cpuProfilerModel = /** @type {!SDK.CPUProfilerModel} */ (target.model(SDK.CPUProfilerModel));
-    /** @type {!SDK.HeapProfilerModel} */
-    target.heapProfilerModel = /** @type {!SDK.HeapProfilerModel} */ (target.model(SDK.HeapProfilerModel));
 
     target.tracingManager = new SDK.TracingManager(target);
 
diff --git a/third_party/WebKit/Source/devtools/front_end/sources/EventListenerBreakpointsSidebarPane.js b/third_party/WebKit/Source/devtools/front_end/sources/EventListenerBreakpointsSidebarPane.js
index 92761c6..c3a0c340 100644
--- a/third_party/WebKit/Source/devtools/front_end/sources/EventListenerBreakpointsSidebarPane.js
+++ b/third_party/WebKit/Source/devtools/front_end/sources/EventListenerBreakpointsSidebarPane.js
@@ -23,8 +23,7 @@
     // Otherwise, inspector page reacts on drop event and tries to load the event data.
     // this._createCategory(Common.UIString("Drag"), ["drag", "drop", "dragstart", "dragend", "dragenter", "dragleave", "dragover"]);
     this._createCategory(
-        Common.UIString('Animation'),
-        ['DOMWindow.requestAnimationFrame', 'DOMWindow.cancelAnimationFrame', 'animationFrameFired'], true);
+        Common.UIString('Animation'), ['requestAnimationFrame', 'cancelAnimationFrame', 'animationFrameFired'], true);
     this._createCategory(
         Common.UIString('Canvas'), ['canvasContextCreated', 'webglErrorFired', 'webglWarningFired'], true);
     this._createCategory(
@@ -64,7 +63,8 @@
       'pointercancel', 'gotpointercapture', 'lostpointercapture'
     ]);
     this._createCategory(Common.UIString('Script'), ['scriptFirstStatement', 'scriptBlockedByCSP'], true);
-    this._createCategory(Common.UIString('Timer'), ['setTimer', 'clearTimer', 'timerFired'], true);
+    this._createCategory(
+        Common.UIString('Timer'), ['setTimeout', 'clearTimeout', 'setInterval', 'clearInterval', 'timerFired'], true);
     this._createCategory(Common.UIString('Touch'), ['touchstart', 'touchmove', 'touchend', 'touchcancel']);
     this._createCategory(Common.UIString('Window'), ['DOMWindow.close'], true);
     this._createCategory(
@@ -86,14 +86,12 @@
   static eventNameForUI(eventName, auxData) {
     if (!Sources.EventListenerBreakpointsSidebarPane._eventNamesForUI) {
       Sources.EventListenerBreakpointsSidebarPane._eventNamesForUI = {
-        'instrumentation:setTimer': Common.UIString('Set Timer'),
-        'instrumentation:clearTimer': Common.UIString('Clear Timer'),
         'instrumentation:timerFired': Common.UIString('Timer Fired'),
         'instrumentation:scriptFirstStatement': Common.UIString('Script First Statement'),
         'instrumentation:scriptBlockedByCSP': Common.UIString('Script Blocked by Content Security Policy'),
-        'instrumentation:DOMWindow.requestAnimationFrame': Common.UIString('Request Animation Frame'),
-        'instrumentation:DOMWindow.cancelAnimationFrame': Common.UIString('Cancel Animation Frame'),
-        'instrumentation:DOMWindow.requestAnimationFrame.callback': Common.UIString('Animation Frame Fired'),
+        'instrumentation:requestAnimationFrame': Common.UIString('Request Animation Frame'),
+        'instrumentation:cancelAnimationFrame': Common.UIString('Cancel Animation Frame'),
+        'instrumentation:requestAnimationFrame.callback': Common.UIString('Animation Frame Fired'),
         'instrumentation:webglErrorFired': Common.UIString('WebGL Error Fired'),
         'instrumentation:webglWarningFired': Common.UIString('WebGL Warning Fired'),
         'instrumentation:Element.setInnerHTML': Common.UIString('Set innerHTML'),
diff --git a/third_party/WebKit/Source/devtools/front_end/text_editor/cmdevtools.css b/third_party/WebKit/Source/devtools/front_end/text_editor/cmdevtools.css
index 2a3df9d..01a4f39 100644
--- a/third_party/WebKit/Source/devtools/front_end/text_editor/cmdevtools.css
+++ b/third_party/WebKit/Source/devtools/front_end/text_editor/cmdevtools.css
@@ -315,23 +315,23 @@
     padding-right: 3px;
 }
 
-.CodeMirror .text-editor-css-rule-unused-marker {
+.CodeMirror .text-editor-coverage-unused-marker {
     text-align: right;
     padding-right: 2px;
     background-color: #E57373;
 }
 
-.CodeMirror .text-editor-css-rule-unused-marker::after {
+.CodeMirror .text-editor-coverage-unused-marker::after {
     content: " ";
 }
 
-.CodeMirror .text-editor-css-rule-used-marker {
+.CodeMirror .text-editor-coverage-used-marker {
     text-align: right;
     padding-right: 2px;
     background-color: #81C784;
 }
 
-.CodeMirror .text-editor-css-rule-used-marker::after {
+.CodeMirror .text-editor-coverage-used-marker::after {
     content: " ";
 }
 
diff --git a/third_party/WebKit/Source/devtools/front_end/ui/panelEnablerView.css b/third_party/WebKit/Source/devtools/front_end/ui/panelEnablerView.css
index eb22a42..ebfbb92e 100644
--- a/third_party/WebKit/Source/devtools/front_end/ui/panelEnablerView.css
+++ b/third_party/WebKit/Source/devtools/front_end/ui/panelEnablerView.css
@@ -81,3 +81,7 @@
     word-break: break-word;
     margin: 0 0 5px 20px;
 }
+
+.panel-enabler-view label:only-of-type {
+    display: none;
+}
diff --git a/third_party/WebKit/Source/modules/accessibility/AXObjectCacheImpl.cpp b/third_party/WebKit/Source/modules/accessibility/AXObjectCacheImpl.cpp
index 4716ca7..6d22ed96 100644
--- a/third_party/WebKit/Source/modules/accessibility/AXObjectCacheImpl.cpp
+++ b/third_party/WebKit/Source/modules/accessibility/AXObjectCacheImpl.cpp
@@ -100,7 +100,9 @@
           &AXObjectCacheImpl::notificationPostTimerFired) {}
 
 AXObjectCacheImpl::~AXObjectCacheImpl() {
-  ASSERT(m_hasBeenDisposed);
+#if DCHECK_IS_ON()
+  DCHECK(m_hasBeenDisposed);
+#endif
 }
 
 void AXObjectCacheImpl::dispose() {
@@ -190,7 +192,7 @@
     return 0;
 
   AXID axID = m_layoutObjectMapping.at(layoutObject);
-  ASSERT(!HashTraits<AXID>::isDeletedValue(axID));
+  DCHECK(!HashTraits<AXID>::isDeletedValue(axID));
   if (!axID)
     return 0;
 
@@ -220,10 +222,10 @@
     layoutObject = nullptr;
 
   AXID layoutID = layoutObject ? m_layoutObjectMapping.at(layoutObject) : 0;
-  ASSERT(!HashTraits<AXID>::isDeletedValue(layoutID));
+  DCHECK(!HashTraits<AXID>::isDeletedValue(layoutID));
 
   AXID nodeID = m_nodeObjectMapping.at(node);
-  ASSERT(!HashTraits<AXID>::isDeletedValue(nodeID));
+  DCHECK(!HashTraits<AXID>::isDeletedValue(nodeID));
 
   if (layoutObject && nodeID && !layoutID) {
     // This can happen if an AXNodeObject is created for a node that's not
@@ -247,7 +249,7 @@
     return 0;
 
   AXID axID = m_inlineTextBoxObjectMapping.at(inlineTextBox);
-  ASSERT(!HashTraits<AXID>::isDeletedValue(axID));
+  DCHECK(!HashTraits<AXID>::isDeletedValue(axID));
   if (!axID)
     return 0;
 
@@ -362,12 +364,11 @@
   AXObject* newObj = createFromNode(node);
 
   // Will crash later if we have two objects for the same node.
-  ASSERT(!get(node));
+  DCHECK(!get(node));
 
-  getAXID(newObj);
+  const AXID axID = getOrCreateAXID(newObj);
 
-  m_nodeObjectMapping.set(node, newObj->axObjectID());
-  m_objects.set(newObj->axObjectID(), newObj);
+  m_nodeObjectMapping.set(node, axID);
   newObj->init();
   newObj->setLastKnownIsIgnoredValue(newObj->accessibilityIsIgnored());
 
@@ -387,12 +388,11 @@
   AXObject* newObj = createFromRenderer(layoutObject);
 
   // Will crash later if we have two objects for the same layoutObject.
-  ASSERT(!get(layoutObject));
+  DCHECK(!get(layoutObject));
 
-  getAXID(newObj);
+  const AXID axid = getOrCreateAXID(newObj);
 
-  m_layoutObjectMapping.set(layoutObject, newObj->axObjectID());
-  m_objects.set(newObj->axObjectID(), newObj);
+  m_layoutObjectMapping.set(layoutObject, axid);
   newObj->init();
   newObj->setLastKnownIsIgnoredValue(newObj->accessibilityIsIgnored());
 
@@ -409,25 +409,17 @@
   AXObject* newObj = createFromInlineTextBox(inlineTextBox);
 
   // Will crash later if we have two objects for the same inlineTextBox.
-  ASSERT(!get(inlineTextBox));
+  DCHECK(!get(inlineTextBox));
 
-  getAXID(newObj);
+  const AXID axid = getOrCreateAXID(newObj);
 
-  m_inlineTextBoxObjectMapping.set(inlineTextBox, newObj->axObjectID());
-  m_objects.set(newObj->axObjectID(), newObj);
+  m_inlineTextBoxObjectMapping.set(inlineTextBox, axid);
   newObj->init();
   newObj->setLastKnownIsIgnoredValue(newObj->accessibilityIsIgnored());
 
   return newObj;
 }
 
-AXObject* AXObjectCacheImpl::rootObject() {
-  if (!accessibilityEnabled())
-    return 0;
-
-  return getOrCreate(m_document);
-}
-
 AXObject* AXObjectCacheImpl::getOrCreate(AccessibilityRole role) {
   AXObject* obj = nullptr;
 
@@ -455,12 +447,11 @@
       obj = nullptr;
   }
 
-  if (obj)
-    getAXID(obj);
-  else
+  if (!obj)
     return 0;
 
-  m_objects.set(obj->axObjectID(), obj);
+  getOrCreateAXID(obj);
+
   obj->init();
   return obj;
 }
@@ -481,7 +472,7 @@
   if (!m_objects.take(axID))
     return;
 
-  ASSERT(m_objects.size() >= m_idsInUse.size());
+  DCHECK(m_objects.size() >= m_idsInUse.size());
 }
 
 void AXObjectCacheImpl::remove(LayoutObject* layoutObject) {
@@ -517,7 +508,7 @@
   m_inlineTextBoxObjectMapping.erase(inlineTextBox);
 }
 
-AXID AXObjectCacheImpl::platformGenerateAXID() const {
+AXID AXObjectCacheImpl::generateAXID() const {
   static AXID lastUsedID = 0;
 
   // Generate a new ID.
@@ -532,20 +523,21 @@
   return objID;
 }
 
-AXID AXObjectCacheImpl::getAXID(AXObject* obj) {
+AXID AXObjectCacheImpl::getOrCreateAXID(AXObject* obj) {
   // check for already-assigned ID
-  AXID objID = obj->axObjectID();
-  if (objID) {
-    ASSERT(m_idsInUse.contains(objID));
-    return objID;
+  const AXID existingAXID = obj->axObjectID();
+  if (existingAXID) {
+    DCHECK(m_idsInUse.contains(existingAXID));
+    return existingAXID;
   }
 
-  objID = platformGenerateAXID();
+  const AXID newAXID = generateAXID();
 
-  m_idsInUse.insert(objID);
-  obj->setAXObjectID(objID);
+  m_idsInUse.insert(newAXID);
+  obj->setAXObjectID(newAXID);
+  m_objects.set(newAXID, obj);
 
-  return objID;
+  return newAXID;
 }
 
 void AXObjectCacheImpl::removeAXID(AXObject* object) {
@@ -555,8 +547,8 @@
   AXID objID = object->axObjectID();
   if (!objID)
     return;
-  ASSERT(!HashTraits<AXID>::isDeletedValue(objID));
-  ASSERT(m_idsInUse.contains(objID));
+  DCHECK(!HashTraits<AXID>::isDeletedValue(objID));
+  DCHECK(m_idsInUse.contains(objID));
   object->setAXObjectID(0);
   m_idsInUse.erase(objID);
 
@@ -646,7 +638,7 @@
       AXLayoutObject* layoutObj = toAXLayoutObject(obj);
       LayoutObject* layoutObject = layoutObj->getLayoutObject();
       if (layoutObject && layoutObject->view())
-        ASSERT(!layoutObject->view()->layoutState());
+        DCHECK(!layoutObject->view()->layoutState());
     }
 #endif
 
@@ -867,7 +859,7 @@
   // still an owner.
   if (isAriaOwned(axElement)) {
     AXObject* ownedParent = getAriaOwnedParent(axElement);
-    ASSERT(ownedParent);
+    DCHECK(ownedParent);
     childrenChanged(ownedParent);
     return;
   }
diff --git a/third_party/WebKit/Source/modules/accessibility/AXObjectCacheImpl.h b/third_party/WebKit/Source/modules/accessibility/AXObjectCacheImpl.h
index 80357d3..7929982b 100644
--- a/third_party/WebKit/Source/modules/accessibility/AXObjectCacheImpl.h
+++ b/third_party/WebKit/Source/modules/accessibility/AXObjectCacheImpl.h
@@ -146,7 +146,7 @@
 
   void removeAXID(AXObject*);
 
-  AXID platformGenerateAXID() const;
+  AXID generateAXID() const;
 
   // Counts the number of times the document has been modified. Some attribute
   // values are cached as long as the modification count hasn't changed.
@@ -247,7 +247,7 @@
 
   AXObject* focusedImageMapUIElement(HTMLAreaElement*);
 
-  AXID getAXID(AXObject*);
+  AXID getOrCreateAXID(AXObject*);
 
   void textChanged(Node*);
   bool nodeIsTextControl(const Node*);
diff --git a/third_party/WebKit/Source/modules/geolocation/Geolocation.cpp b/third_party/WebKit/Source/modules/geolocation/Geolocation.cpp
index e03655f..769c95f9a 100644
--- a/third_party/WebKit/Source/modules/geolocation/Geolocation.cpp
+++ b/third_party/WebKit/Source/modules/geolocation/Geolocation.cpp
@@ -180,8 +180,8 @@
     return;
 
   reportGeolocationViolation(document());
-  InspectorInstrumentation::NativeBreakpoint nativeBreakpoint(
-      document(), "Geolocation.getCurrentPosition", true);
+  InspectorInstrumentation::breakIfNeeded(document(),
+                                          "Geolocation.getCurrentPosition");
 
   GeoNotifier* notifier =
       GeoNotifier::create(this, successCallback, errorCallback, options);
@@ -197,8 +197,8 @@
     return 0;
 
   reportGeolocationViolation(document());
-  InspectorInstrumentation::NativeBreakpoint nativeBreakpoint(
-      document(), "Geolocation.watchPosition", true);
+  InspectorInstrumentation::breakIfNeeded(document(),
+                                          "Geolocation.watchPosition");
 
   GeoNotifier* notifier =
       GeoNotifier::create(this, successCallback, errorCallback, options);
diff --git a/third_party/WebKit/Source/modules/notifications/Notification.cpp b/third_party/WebKit/Source/modules/notifications/Notification.cpp
index 930099c..16838d9 100644
--- a/third_party/WebKit/Source/modules/notifications/Notification.cpp
+++ b/third_party/WebKit/Source/modules/notifications/Notification.cpp
@@ -380,8 +380,8 @@
         "Only request notification permission in response to a user gesture.",
         0, nullptr);
   }
-  InspectorInstrumentation::NativeBreakpoint nativeBreakpoint(
-      context, "Notification.requestPermission", true);
+  InspectorInstrumentation::breakIfNeeded(context,
+                                          "Notification.requestPermission");
 
   return NotificationManager::from(context)->requestPermission(
       scriptState, deprecatedCallback);
diff --git a/third_party/WebKit/Source/web/WebAXObject.cpp b/third_party/WebKit/Source/web/WebAXObject.cpp
index 8d0abcc..648e6c62 100644
--- a/third_party/WebKit/Source/web/WebAXObject.cpp
+++ b/third_party/WebKit/Source/web/WebAXObject.cpp
@@ -154,7 +154,7 @@
   if (isDetached())
     return -1;
 
-  return m_private->axObjectCache().platformGenerateAXID();
+  return m_private->axObjectCache().generateAXID();
 }
 
 bool WebAXObject::updateLayoutAndCheckValidity() {
diff --git a/tools/chrome_proxy/webdriver/bypass.py b/tools/chrome_proxy/webdriver/bypass.py
index 7906a3e..3b7798a 100644
--- a/tools/chrome_proxy/webdriver/bypass.py
+++ b/tools/chrome_proxy/webdriver/bypass.py
@@ -46,14 +46,14 @@
       t.LoadURL('http://check.googlezip.net/test.html')
       responses = t.GetHTTPResponses()
       self.assertEqual(2, len(responses))
-      for response in t.GetHTTPResponses():
+      for response in responses:
         self.assertHasChromeProxyViaHeader(response)
 
       # Load HTTPS page and check that Data Saver is not used.
       t.LoadURL('https://check.googlezip.net/test.html')
       responses = t.GetHTTPResponses()
       self.assertEqual(2, len(responses))
-      for response in t.GetHTTPResponses():
+      for response in responses:
         self.assertNotHasChromeProxyViaHeader(response)
 
   # Verify that CORS requests receive a block-once from the data reduction
@@ -82,5 +82,26 @@
       self.assertNotEqual(0, same_origin_requests)
       self.assertNotEqual(0, cors_requests)
 
+  # Verify that when an origin times out using Data Saver, the request is
+  # fetched directly and data saver is bypassed only for one request.
+  def testOriginTimeoutBlockOnce(self):
+    with TestDriver() as test_driver:
+      test_driver.AddChromeArg('--enable-spdy-proxy-auth')
+
+      # Load URL that times out when the proxy server tries to access it.
+      test_driver.LoadURL('http://chromeproxy-test.appspot.com/blackhole')
+      responses = test_driver.GetHTTPResponses()
+      self.assertNotEqual(0, len(responses))
+      for response in responses:
+          self.assertNotHasChromeProxyViaHeader(response)
+
+      # Load HTTP page and check that Data Saver is used.
+      test_driver.LoadURL('http://check.googlezip.net/test.html')
+      responses = test_driver.GetHTTPResponses()
+      self.assertNotEqual(0, len(responses))
+      for response in responses:
+        self.assertHasChromeProxyViaHeader(response)
+
+
 if __name__ == '__main__':
   IntegrationTest.RunAllTests()
diff --git a/tools/clang/scripts/package.py b/tools/clang/scripts/package.py
index 17181098..780bf5f1 100755
--- a/tools/clang/scripts/package.py
+++ b/tools/clang/scripts/package.py
@@ -87,7 +87,7 @@
 
 def GsutilArchiveExists(archive_name, platform):
   gsutil_args = ['-q', 'stat',
-                 'gs://chromium-browser-clang/%s/%s.tgz' %
+                 'gs://chromium-browser-clang-staging/%s/%s.tgz' %
                  (platform, archive_name)]
   return RunGsutil(gsutil_args) == 0
 
@@ -97,7 +97,7 @@
   # so -n option to gsutil is used. It will warn, if the upload was aborted.
   gsutil_args = ['cp', '-n', '-a', 'public-read',
                   '%s.tgz' % archive_name,
-                  'gs://chromium-browser-clang/%s/%s.tgz' %
+                  'gs://chromium-browser-clang-staging/%s/%s.tgz' %
                  (platform, archive_name)]
   if args.upload:
     print 'Uploading %s to Google Cloud Storage...' % archive_name
@@ -141,9 +141,10 @@
       GsutilArchiveExists(golddir, platform)):
     print ('Desired toolchain revision %s is already available '
            'in Google Cloud Storage:') % expected_stamp
-    print 'gs://chromium-browser-clang/%s/%s.tgz' % (platform, pdir)
+    print 'gs://chromium-browser-clang-staging/%s/%s.tgz' % (platform, pdir)
     if sys.platform.startswith('linux'):
-      print 'gs://chromium-browser-clang/%s/%s.tgz' % (platform, golddir)
+      print 'gs://chromium-browser-clang-staging/%s/%s.tgz' % (platform,
+                                                               golddir)
     return 0
 
   with open('buildlog.txt', 'w') as log:
diff --git a/tools/clang/scripts/update.py b/tools/clang/scripts/update.py
index 5fa6812..9a667cb 100755
--- a/tools/clang/scripts/update.py
+++ b/tools/clang/scripts/update.py
@@ -336,7 +336,7 @@
     return
 
   gnuwin_dir = os.path.join(LLVM_BUILD_TOOLS_DIR, 'gnuwin')
-  GNUWIN_VERSION = '5'
+  GNUWIN_VERSION = '6'
   GNUWIN_STAMP = os.path.join(gnuwin_dir, 'stamp')
   if ReadStampFile(GNUWIN_STAMP) == GNUWIN_VERSION:
     print 'GNU Win tools already up to date.'
diff --git a/tools/mb/mb_config.pyl b/tools/mb/mb_config.pyl
index 6e8ca3d4..27c87d26 100644
--- a/tools/mb/mb_config.pyl
+++ b/tools/mb/mb_config.pyl
@@ -270,7 +270,7 @@
       'Linux Builder Trusty (dbg)': 'debug_bot',
       'Linux Builder Trusty (dbg)(32)': 'debug_bot_x86',
       'Linux Builder Trusty': 'release_bot',
-      'Linux Deterministic': 'release_bot',
+      'Deterministic Linux': 'release_bot',
     },
 
     'chromium.lkgr': {
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index e706221f..5ad2834 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -31503,6 +31503,13 @@
   </summary>
 </histogram>
 
+<histogram name="Net.CountOfQuicServerInfos" units="configs">
+  <owner>zhongyi@chromium.org</owner>
+  <summary>
+    This counts the number of server configs persisted in prefs file.
+  </summary>
+</histogram>
+
 <histogram name="Net.CountOfSpdyServers">
   <owner>bnc@chromium.org</owner>
   <owner>rch@chromium.org</owner>
diff --git a/tools/perf/page_sets/data/system_health_desktop.json b/tools/perf/page_sets/data/system_health_desktop.json
index 1597659..35f5945 100644
--- a/tools/perf/page_sets/data/system_health_desktop.json
+++ b/tools/perf/page_sets/data/system_health_desktop.json
@@ -12,7 +12,8 @@
         "browse:media:youtube": {
             "DEFAULT": "system_health_desktop_026.wpr",
             "linux": "system_health_desktop_026.wpr",
-            "mac": "system_health_desktop_040.wpr"
+            "mac": "system_health_desktop_040.wpr",
+            "win": "system_health_desktop_047.wpr"
         },
         "browse:news:cnn": {
             "DEFAULT": "system_health_desktop_013.wpr"
@@ -74,7 +75,8 @@
         "load:media:youtube": {
             "DEFAULT": "system_health_desktop_003.wpr",
             "linux": "system_health_desktop_003.wpr",
-            "mac": "system_health_desktop_040.wpr"
+            "mac": "system_health_desktop_040.wpr",
+            "win": "system_health_desktop_047.wpr"
         },
         "load:news:bbc": {
             "DEFAULT": "system_health_desktop_002.wpr"
diff --git a/tools/perf/page_sets/data/system_health_desktop_047.wpr.sha1 b/tools/perf/page_sets/data/system_health_desktop_047.wpr.sha1
new file mode 100644
index 0000000..3ebf9a5e
--- /dev/null
+++ b/tools/perf/page_sets/data/system_health_desktop_047.wpr.sha1
@@ -0,0 +1 @@
+d8ddd37088c656bb37a0329230cb81d2514aa634
\ No newline at end of file
diff --git a/ui/aura/BUILD.gn b/ui/aura/BUILD.gn
index 19b76f8..8e1f152 100644
--- a/ui/aura/BUILD.gn
+++ b/ui/aura/BUILD.gn
@@ -252,8 +252,6 @@
     "test/test_window_delegate.h",
     "test/test_window_parenting_client.cc",
     "test/test_window_parenting_client.h",
-    "test/test_window_targeter.cc",
-    "test/test_window_targeter.h",
     "test/test_windows.cc",
     "test/test_windows.h",
     "test/ui_controls_factory_aura.h",
diff --git a/ui/aura/mus/window_tree_client.cc b/ui/aura/mus/window_tree_client.cc
index 93426157..0f7351c 100644
--- a/ui/aura/mus/window_tree_client.cc
+++ b/ui/aura/mus/window_tree_client.cc
@@ -160,14 +160,6 @@
   event->set_root_location(root_location);
 }
 
-// Set the |target| to be the target window of this |event| and send it to
-// the EventProcessor.
-void DispatchEventToTarget(ui::Event* event, WindowMus* target) {
-  ui::Event::DispatcherApi dispatch_helper(event);
-  dispatch_helper.set_target(target->GetWindow());
-  GetWindowTreeHostMus(target)->SendEventToProcessor(event);
-}
-
 }  // namespace
 
 WindowTreeClient::WindowTreeClient(
@@ -1158,22 +1150,34 @@
     return;
   }
 
+  WindowTreeHostMus* host = GetWindowTreeHostMus(window);
+  DCHECK(host);
+
+  // The location of the event is relative to |window|. As the event is handed
+  // to WindowTreeHost we need it to be relative to WindowTreeHost.
+  if (event->IsLocatedEvent()) {
+    gfx::Point host_location = event->AsLocatedEvent()->location();
+    aura::Window::ConvertPointToTarget(window->GetWindow(), host->window(),
+                                       &host_location);
+    event->AsLocatedEvent()->set_location(host_location);
+  }
+
   EventAckHandler ack_handler(CreateEventResultCallback(event_id));
   // TODO(moshayedi): crbug.com/617222. No need to convert to ui::MouseEvent or
   // ui::TouchEvent once we have proper support for pointer events.
   if (event->IsMousePointerEvent()) {
     if (event->type() == ui::ET_POINTER_WHEEL_CHANGED) {
       ui::MouseWheelEvent mapped_event(*event->AsPointerEvent());
-      DispatchEventToTarget(&mapped_event, window);
+      host->SendEventToProcessor(&mapped_event);
     } else {
       ui::MouseEvent mapped_event(*event->AsPointerEvent());
-      DispatchEventToTarget(&mapped_event, window);
+      host->SendEventToProcessor(&mapped_event);
     }
   } else if (event->IsTouchPointerEvent()) {
     ui::TouchEvent mapped_event(*event->AsPointerEvent());
-    DispatchEventToTarget(&mapped_event, window);
+    host->SendEventToProcessor(&mapped_event);
   } else {
-    DispatchEventToTarget(event.get(), window);
+    host->SendEventToProcessor(event.get());
   }
   ack_handler.set_handled(event->handled());
 }
diff --git a/ui/aura/mus/window_tree_client_unittest.cc b/ui/aura/mus/window_tree_client_unittest.cc
index 8289b88..76dcf10 100644
--- a/ui/aura/mus/window_tree_client_unittest.cc
+++ b/ui/aura/mus/window_tree_client_unittest.cc
@@ -31,9 +31,7 @@
 #include "ui/aura/test/mus/test_window_tree.h"
 #include "ui/aura/test/mus/window_tree_client_private.h"
 #include "ui/aura/test/test_window_delegate.h"
-#include "ui/aura/test/test_window_targeter.h"
 #include "ui/aura/window.h"
-#include "ui/aura/window_targeter.h"
 #include "ui/aura/window_tracker.h"
 #include "ui/aura/window_tree_host_observer.h"
 #include "ui/base/class_property.h"
@@ -495,6 +493,8 @@
 
 class InputEventBasicTestWindowDelegate : public test::TestWindowDelegate {
  public:
+  static uint32_t constexpr kEventId = 1;
+
   explicit InputEventBasicTestWindowDelegate(TestWindowTree* test_window_tree)
       : test_window_tree_(test_window_tree) {}
   ~InputEventBasicTestWindowDelegate() override {}
@@ -502,30 +502,21 @@
   bool got_move() const { return got_move_; }
   bool was_acked() const { return was_acked_; }
   const gfx::Point& last_event_location() const { return last_event_location_; }
-  void set_event_id(uint32_t event_id) { event_id_ = event_id; }
 
   // TestWindowDelegate::
   void OnMouseEvent(ui::MouseEvent* event) override {
-    was_acked_ = test_window_tree_->WasEventAcked(event_id_);
+    was_acked_ = test_window_tree_->WasEventAcked(kEventId);
     if (event->type() == ui::ET_MOUSE_MOVED)
       got_move_ = true;
     last_event_location_ = event->location();
     event->SetHandled();
   }
 
-  void reset() {
-    was_acked_ = false;
-    got_move_ = false;
-    last_event_location_ = gfx::Point();
-    event_id_ = 0;
-  }
-
  private:
   TestWindowTree* test_window_tree_;
   bool was_acked_ = false;
   bool got_move_ = false;
   gfx::Point last_event_location_;
-  uint32_t event_id_ = 0;
 
   DISALLOW_COPY_AND_ASSIGN(InputEventBasicTestWindowDelegate);
 };
@@ -550,153 +541,22 @@
   EXPECT_FALSE(window_delegate.got_move());
   EXPECT_FALSE(window_delegate.was_acked());
   const gfx::Point event_location_in_child(2, 3);
-  const uint32_t event_id = 1;
-  window_delegate.set_event_id(event_id);
   std::unique_ptr<ui::Event> ui_event(
       new ui::MouseEvent(ui::ET_MOUSE_MOVED, event_location_in_child,
                          gfx::Point(), ui::EventTimeForNow(), ui::EF_NONE, 0));
   window_tree_client()->OnWindowInputEvent(
-      event_id, server_id(&child), window_tree_host.display_id(),
-      ui::Event::Clone(*ui_event.get()), 0);
-  EXPECT_TRUE(window_tree()->WasEventAcked(event_id));
+      InputEventBasicTestWindowDelegate::kEventId, server_id(&child),
+      window_tree_host.display_id(), ui::Event::Clone(*ui_event.get()), 0);
+  EXPECT_TRUE(window_tree()->WasEventAcked(
+      InputEventBasicTestWindowDelegate::kEventId));
   EXPECT_EQ(ui::mojom::EventResult::HANDLED,
-            window_tree()->GetEventResult(event_id));
+            window_tree()->GetEventResult(
+                InputEventBasicTestWindowDelegate::kEventId));
   EXPECT_TRUE(window_delegate.got_move());
   EXPECT_FALSE(window_delegate.was_acked());
   EXPECT_EQ(event_location_in_child, window_delegate.last_event_location());
 }
 
-TEST_F(WindowTreeClientClientTest, InputEventFindTargetAndConversion) {
-  WindowTreeHostMus window_tree_host(window_tree_client_impl());
-  Window* top_level = window_tree_host.window();
-  const gfx::Rect bounds(0, 0, 100, 100);
-  window_tree_host.SetBoundsInPixels(bounds);
-  window_tree_host.InitHost();
-  window_tree_host.Show();
-  EXPECT_EQ(bounds, top_level->bounds());
-  EXPECT_EQ(bounds, window_tree_host.GetBoundsInPixels());
-  InputEventBasicTestWindowDelegate window_delegate1(window_tree());
-  Window child1(&window_delegate1);
-  child1.Init(ui::LAYER_NOT_DRAWN);
-  child1.SetEventTargeter(base::MakeUnique<WindowTargeter>());
-  top_level->AddChild(&child1);
-  child1.SetBounds(gfx::Rect(10, 10, 100, 100));
-  child1.Show();
-  InputEventBasicTestWindowDelegate window_delegate2(window_tree());
-  Window child2(&window_delegate2);
-  child2.Init(ui::LAYER_NOT_DRAWN);
-  child1.AddChild(&child2);
-  child2.SetBounds(gfx::Rect(20, 30, 100, 100));
-  child2.Show();
-
-  EXPECT_FALSE(window_delegate1.got_move());
-  EXPECT_FALSE(window_delegate2.got_move());
-
-  // child1 has a targeter set and event_location is (50, 60), child2
-  // should get the event even though mus-ws wants to send to child1.
-  const gfx::Point event_location(50, 60);
-  uint32_t event_id = 1;
-  window_delegate1.set_event_id(event_id);
-  window_delegate2.set_event_id(event_id);
-  std::unique_ptr<ui::Event> ui_event(
-      new ui::MouseEvent(ui::ET_MOUSE_MOVED, event_location, gfx::Point(),
-                         ui::EventTimeForNow(), ui::EF_NONE, 0));
-  window_tree_client()->OnWindowInputEvent(
-      event_id, server_id(&child1), window_tree_host.display_id(),
-      ui::Event::Clone(*ui_event.get()), 0);
-  EXPECT_TRUE(window_tree()->WasEventAcked(event_id));
-  EXPECT_EQ(ui::mojom::EventResult::HANDLED,
-            window_tree()->GetEventResult(event_id));
-  EXPECT_FALSE(window_delegate1.got_move());
-  EXPECT_TRUE(window_delegate2.got_move());
-  EXPECT_EQ(gfx::Point(30, 30), window_delegate2.last_event_location());
-  window_delegate1.reset();
-  window_delegate2.reset();
-
-  // Remove the targeter for child1 and specify the event to go to child1. This
-  // time child1 should receive the event not child2.
-  child1.SetEventTargeter(nullptr);
-  event_id = 2;
-  window_delegate1.set_event_id(event_id);
-  window_delegate2.set_event_id(event_id);
-  std::unique_ptr<ui::Event> ui_event1(
-      new ui::MouseEvent(ui::ET_MOUSE_MOVED, event_location, gfx::Point(),
-                         ui::EventTimeForNow(), ui::EF_NONE, 0));
-  window_tree_client()->OnWindowInputEvent(
-      event_id, server_id(&child1), window_tree_host.display_id(),
-      ui::Event::Clone(*ui_event1.get()), 0);
-  EXPECT_TRUE(window_tree()->WasEventAcked(event_id));
-  EXPECT_EQ(ui::mojom::EventResult::HANDLED,
-            window_tree()->GetEventResult(event_id));
-  EXPECT_TRUE(window_delegate1.got_move());
-  EXPECT_FALSE(window_delegate2.got_move());
-  EXPECT_EQ(gfx::Point(50, 60), window_delegate1.last_event_location());
-}
-
-TEST_F(WindowTreeClientClientTest, InputEventCustomWindowTargeter) {
-  WindowTreeHostMus window_tree_host(window_tree_client_impl());
-  Window* top_level = window_tree_host.window();
-  const gfx::Rect bounds(0, 0, 100, 100);
-  window_tree_host.SetBoundsInPixels(bounds);
-  window_tree_host.InitHost();
-  window_tree_host.Show();
-  EXPECT_EQ(bounds, top_level->bounds());
-  EXPECT_EQ(bounds, window_tree_host.GetBoundsInPixels());
-  InputEventBasicTestWindowDelegate window_delegate1(window_tree());
-  Window child1(&window_delegate1);
-  child1.Init(ui::LAYER_NOT_DRAWN);
-  child1.SetEventTargeter(base::MakeUnique<test::TestWindowTargeter>());
-  top_level->AddChild(&child1);
-  child1.SetBounds(gfx::Rect(10, 10, 100, 100));
-  child1.Show();
-  InputEventBasicTestWindowDelegate window_delegate2(window_tree());
-  Window child2(&window_delegate2);
-  child2.Init(ui::LAYER_NOT_DRAWN);
-  child1.AddChild(&child2);
-  child2.SetBounds(gfx::Rect(20, 30, 100, 100));
-  child2.Show();
-
-  EXPECT_FALSE(window_delegate1.got_move());
-  EXPECT_FALSE(window_delegate2.got_move());
-
-  // child1 has a custom targeter set which would always return itself as the
-  // target window therefore event should go to child1 unlike
-  // WindowTreeClientClientTest.InputEventFindTargetAndConversion.
-  const gfx::Point event_location(50, 60);
-  uint32_t event_id = 1;
-  window_delegate1.set_event_id(event_id);
-  window_delegate2.set_event_id(event_id);
-  std::unique_ptr<ui::Event> ui_event(
-      new ui::MouseEvent(ui::ET_MOUSE_MOVED, event_location, gfx::Point(),
-                         ui::EventTimeForNow(), ui::EF_NONE, 0));
-  window_tree_client()->OnWindowInputEvent(
-      event_id, server_id(&child1), window_tree_host.display_id(),
-      ui::Event::Clone(*ui_event.get()), 0);
-  EXPECT_TRUE(window_tree()->WasEventAcked(event_id));
-  EXPECT_EQ(ui::mojom::EventResult::HANDLED,
-            window_tree()->GetEventResult(event_id));
-  EXPECT_TRUE(window_delegate1.got_move());
-  EXPECT_FALSE(window_delegate2.got_move());
-  EXPECT_EQ(gfx::Point(50, 60), window_delegate1.last_event_location());
-  window_delegate1.reset();
-  window_delegate2.reset();
-
-  // child1 should get the event even though mus-ws specifies child2 and it's
-  // actually in child2's space. Event location will be transformed.
-  event_id = 2;
-  window_delegate1.set_event_id(event_id);
-  window_delegate2.set_event_id(event_id);
-  window_tree_client()->OnWindowInputEvent(
-      event_id, server_id(&child2), window_tree_host.display_id(),
-      ui::Event::Clone(*ui_event.get()), 0);
-  EXPECT_TRUE(window_tree()->WasEventAcked(event_id));
-  EXPECT_EQ(ui::mojom::EventResult::HANDLED,
-            window_tree()->GetEventResult(event_id));
-  EXPECT_TRUE(window_delegate1.got_move());
-  EXPECT_FALSE(window_delegate2.got_move());
-  EXPECT_EQ(gfx::Point(70, 90), window_delegate1.last_event_location());
-}
-
 class WindowTreeClientPointerObserverTest : public WindowTreeClientClientTest {
  public:
   WindowTreeClientPointerObserverTest() {}
@@ -1728,7 +1588,7 @@
   EXPECT_EQ(gfx::Rect(2, 4, 6, 8), top_level->GetHost()->GetBoundsInPixels());
 }
 
-TEST_F(WindowTreeClientClientTestHighDPI, PointerEventsInDip) {
+TEST_F(WindowTreeClientClientTestHighDPI, PointerEventsInDips) {
   display::Screen* screen = display::Screen::GetScreen();
   const display::Display primary_display = screen->GetPrimaryDisplay();
   ASSERT_EQ(2.0f, primary_display.device_scale_factor());
@@ -1762,77 +1622,4 @@
             last_event->root_location());
 }
 
-TEST_F(WindowTreeClientClientTestHighDPI, InputEventsInDip) {
-  WindowTreeHostMus window_tree_host(window_tree_client_impl());
-  display::Screen* screen = display::Screen::GetScreen();
-  display::Display display;
-  ASSERT_TRUE(
-      screen->GetDisplayWithDisplayId(window_tree_host.display_id(), &display));
-  ASSERT_EQ(2.0f, display.device_scale_factor());
-
-  Window* top_level = window_tree_host.window();
-  const gfx::Rect bounds_in_pixels(0, 0, 100, 100);
-  window_tree_host.SetBoundsInPixels(bounds_in_pixels);
-  window_tree_host.InitHost();
-  window_tree_host.Show();
-  EXPECT_EQ(gfx::ConvertRectToDIP(2.0f, bounds_in_pixels), top_level->bounds());
-  EXPECT_EQ(bounds_in_pixels, window_tree_host.GetBoundsInPixels());
-
-  InputEventBasicTestWindowDelegate window_delegate1(window_tree());
-  Window child1(&window_delegate1);
-  child1.Init(ui::LAYER_NOT_DRAWN);
-  child1.SetEventTargeter(base::MakeUnique<test::TestWindowTargeter>());
-  top_level->AddChild(&child1);
-  child1.SetBounds(gfx::Rect(10, 10, 100, 100));
-  child1.Show();
-  InputEventBasicTestWindowDelegate window_delegate2(window_tree());
-  Window child2(&window_delegate2);
-  child2.Init(ui::LAYER_NOT_DRAWN);
-  child1.AddChild(&child2);
-  child2.SetBounds(gfx::Rect(20, 30, 100, 100));
-  child2.Show();
-
-  EXPECT_FALSE(window_delegate1.got_move());
-  EXPECT_FALSE(window_delegate2.got_move());
-
-  // child1 has a custom targeter set which would always return itself as the
-  // target window therefore event should go to child1 and should be in dip.
-  const gfx::Point event_location_in_pixels(50, 60);
-  uint32_t event_id = 1;
-  window_delegate1.set_event_id(event_id);
-  window_delegate2.set_event_id(event_id);
-  std::unique_ptr<ui::Event> ui_event(
-      new ui::MouseEvent(ui::ET_MOUSE_MOVED, event_location_in_pixels,
-                         gfx::Point(), ui::EventTimeForNow(), ui::EF_NONE, 0));
-  window_tree_client()->OnWindowInputEvent(
-      event_id, server_id(&child1), window_tree_host.display_id(),
-      ui::Event::Clone(*ui_event.get()), 0);
-  EXPECT_TRUE(window_tree()->WasEventAcked(event_id));
-  EXPECT_EQ(ui::mojom::EventResult::HANDLED,
-            window_tree()->GetEventResult(event_id));
-  EXPECT_TRUE(window_delegate1.got_move());
-  EXPECT_FALSE(window_delegate2.got_move());
-  const gfx::Point event_location_in_dip(25, 30);
-  EXPECT_EQ(event_location_in_dip, window_delegate1.last_event_location());
-  window_delegate1.reset();
-  window_delegate2.reset();
-
-  // Event location will be transformed and should be in dip.
-  event_id = 2;
-  window_delegate1.set_event_id(event_id);
-  window_delegate2.set_event_id(event_id);
-  window_tree_client()->OnWindowInputEvent(
-      event_id, server_id(&child2), window_tree_host.display_id(),
-      ui::Event::Clone(*ui_event.get()), 0);
-  EXPECT_TRUE(window_tree()->WasEventAcked(event_id));
-  EXPECT_EQ(ui::mojom::EventResult::HANDLED,
-            window_tree()->GetEventResult(event_id));
-  EXPECT_TRUE(window_delegate1.got_move());
-  EXPECT_FALSE(window_delegate2.got_move());
-  gfx::Point transformed_event_location_in_dip(event_location_in_dip.x() + 20,
-                                               event_location_in_dip.y() + 30);
-  EXPECT_EQ(transformed_event_location_in_dip,
-            window_delegate1.last_event_location());
-}
-
 }  // namespace aura
diff --git a/ui/aura/test/aura_test_helper.cc b/ui/aura/test/aura_test_helper.cc
index ccbd85342f..9a71aa92 100644
--- a/ui/aura/test/aura_test_helper.cc
+++ b/ui/aura/test/aura_test_helper.cc
@@ -24,7 +24,6 @@
 #include "ui/aura/window.h"
 #include "ui/aura/window_event_dispatcher.h"
 #include "ui/aura/window_port_local.h"
-#include "ui/aura/window_targeter.h"
 #include "ui/base/ime/input_method_factory.h"
 #include "ui/base/ime/input_method_initializer.h"
 #include "ui/base/platform_window_defaults.h"
@@ -130,8 +129,6 @@
   if (!screen)
     display::Screen::SetScreenInstance(test_screen_.get());
   host_.reset(test_screen_->CreateHostForPrimaryDisplay());
-  host_->window()->SetEventTargeter(
-      std::unique_ptr<ui::EventTargeter>(new WindowTargeter()));
 
   client::SetFocusClient(root_window(), focus_client_.get());
   client::SetCaptureClient(root_window(), capture_client());
diff --git a/ui/aura/test/test_window_targeter.cc b/ui/aura/test/test_window_targeter.cc
deleted file mode 100644
index 1eff0b70..0000000
--- a/ui/aura/test/test_window_targeter.cc
+++ /dev/null
@@ -1,26 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "ui/aura/test/test_window_targeter.h"
-
-namespace aura {
-namespace test {
-
-TestWindowTargeter::TestWindowTargeter() {}
-
-TestWindowTargeter::~TestWindowTargeter() {}
-
-ui::EventTarget* TestWindowTargeter::FindTargetForEvent(ui::EventTarget* root,
-                                                        ui::Event* event) {
-  return root;
-}
-
-ui::EventTarget* TestWindowTargeter::FindNextBestTarget(
-    ui::EventTarget* previous_target,
-    ui::Event* event) {
-  return previous_target;
-}
-
-}  // namespace test
-}  // namespace aura
diff --git a/ui/aura/test/test_window_targeter.h b/ui/aura/test/test_window_targeter.h
deleted file mode 100644
index 664fd163..0000000
--- a/ui/aura/test/test_window_targeter.h
+++ /dev/null
@@ -1,37 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef UI_AURA_TEST_TEST_WINDOW_TARGETER_H_
-#define UI_AURA_TEST_TEST_WINDOW_TARGETER_H_
-
-#include "base/macros.h"
-#include "ui/aura/window_targeter.h"
-#include "ui/events/event_targeter.h"
-
-namespace aura {
-namespace test {
-
-// A test WindowTargeter implementation that would always return the
-// EventTarget it received from FindTargetForEvent or FindNextBestTarget
-// as the EventTarget that should receive the event.
-class TestWindowTargeter : public WindowTargeter {
- public:
-  TestWindowTargeter();
-  ~TestWindowTargeter() override;
-
- protected:
-  // WindowTargeter:
-  ui::EventTarget* FindTargetForEvent(ui::EventTarget* root,
-                                      ui::Event* event) override;
-  ui::EventTarget* FindNextBestTarget(ui::EventTarget* previous_target,
-                                      ui::Event* event) override;
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(TestWindowTargeter);
-};
-
-}  // namespace test
-}  // namespace aura
-
-#endif  // UI_AURA_TEST_TEST_WINDOW_TARGETER_H_
diff --git a/ui/aura/window_event_dispatcher.cc b/ui/aura/window_event_dispatcher.cc
index 72a5d0e..6cd3c43e2da 100644
--- a/ui/aura/window_event_dispatcher.cc
+++ b/ui/aura/window_event_dispatcher.cc
@@ -27,7 +27,6 @@
 #include "ui/base/hit_test.h"
 #include "ui/compositor/dip_util.h"
 #include "ui/events/event.h"
-#include "ui/events/event_targeter.h"
 #include "ui/events/event_utils.h"
 #include "ui/events/gestures/gesture_recognizer.h"
 #include "ui/events/gestures/gesture_types.h"
@@ -77,7 +76,6 @@
       dispatching_held_event_(nullptr),
       observer_manager_(this),
       env_controller_(new EnvInputStateController),
-      event_targeter_(new WindowTargeter),
       repost_event_factory_(this),
       held_event_factory_(this) {
   ui::GestureRecognizer::Get()->AddGestureEventHelper(this);
@@ -92,10 +90,6 @@
   ui::GestureRecognizer::Get()->RemoveGestureEventHelper(this);
 }
 
-ui::EventTargeter* WindowEventDispatcher::GetDefaultEventTargeter() {
-  return event_targeter_.get();
-}
-
 void WindowEventDispatcher::RepostEvent(const ui::LocatedEvent* event) {
   DCHECK(event->type() == ui::ET_MOUSE_PRESSED ||
          event->type() == ui::ET_GESTURE_TAP_DOWN ||
@@ -422,36 +416,8 @@
 
 ////////////////////////////////////////////////////////////////////////////////
 // WindowEventDispatcher, ui::EventProcessor implementation:
-ui::EventTarget* WindowEventDispatcher::GetRootForEvent(ui::Event* event) {
-  if (Env::GetInstance()->mode() == Env::Mode::LOCAL)
-    return window();
-
-  if (!event->target())
-    return window();
-
-  ui::EventTarget* target = event->target();
-  ui::EventTarget* ancestor_with_targeter = target;
-  for (ui::EventTarget* ancestor = target->GetParentTarget(); ancestor;
-       ancestor = ancestor->GetParentTarget()) {
-    if (ancestor->GetEventTargeter())
-      ancestor_with_targeter = ancestor;
-    if (ancestor == window())
-      break;
-  }
-
-  if (ancestor_with_targeter != target && event->IsLocatedEvent()) {
-    gfx::Point location = event->AsLocatedEvent()->location();
-    gfx::Point root_location = event->AsLocatedEvent()->root_location();
-    Window::ConvertPointToTarget(static_cast<Window*>(target),
-                                 static_cast<Window*>(ancestor_with_targeter),
-                                 &location);
-    Window::ConvertPointToTarget(static_cast<Window*>(target),
-                                 static_cast<Window*>(ancestor_with_targeter),
-                                 &root_location);
-    event->AsLocatedEvent()->set_location(location);
-    event->AsLocatedEvent()->set_root_location(root_location);
-  }
-  return ancestor_with_targeter;
+ui::EventTarget* WindowEventDispatcher::GetRootTarget() {
+  return window();
 }
 
 void WindowEventDispatcher::OnEventProcessingStarted(ui::Event* event) {
diff --git a/ui/aura/window_event_dispatcher.h b/ui/aura/window_event_dispatcher.h
index eb2a3bb..32b2b90 100644
--- a/ui/aura/window_event_dispatcher.h
+++ b/ui/aura/window_event_dispatcher.h
@@ -60,9 +60,6 @@
   Window* mouse_pressed_handler() { return mouse_pressed_handler_; }
   Window* mouse_moved_handler() { return mouse_moved_handler_; }
 
-  // Overridden from ui::EventProcessor:
-  ui::EventTargeter* GetDefaultEventTargeter() override;
-
   // Repost event for re-processing. Used when exiting context menus.
   // We support the ET_MOUSE_PRESSED, ET_TOUCH_PRESSED and ET_GESTURE_TAP_DOWN
   // event types (although the latter is currently a no-op).
@@ -176,7 +173,7 @@
   void ReleaseNativeCapture() override;
 
   // Overridden from ui::EventProcessor:
-  ui::EventTarget* GetRootForEvent(ui::Event* event) override;
+  ui::EventTarget* GetRootTarget() override;
   void OnEventProcessingStarted(ui::Event* event) override;
   void OnEventProcessingFinished(ui::Event* event) override;
 
@@ -265,9 +262,6 @@
 
   std::unique_ptr<MusMouseLocationUpdater> mus_mouse_location_updater_;
 
-  // The default EventTargeter for WindowEventDispatcher generated events.
-  std::unique_ptr<ui::EventTargeter> event_targeter_;
-
   // Used to schedule reposting an event.
   base::WeakPtrFactory<WindowEventDispatcher> repost_event_factory_;
 
diff --git a/ui/aura/window_event_dispatcher_unittest.cc b/ui/aura/window_event_dispatcher_unittest.cc
index 3197a454..ca48fb2d 100644
--- a/ui/aura/window_event_dispatcher_unittest.cc
+++ b/ui/aura/window_event_dispatcher_unittest.cc
@@ -2742,50 +2742,6 @@
   EXPECT_EQ(gfx::Point(0, 0), Env::GetInstance()->last_mouse_location());
 }
 
-TEST_F(WindowEventDispatcherMusTest, UseDefaultTargeterToFindTarget) {
-  LastEventLocationDelegate last_event_location_delegate1;
-  std::unique_ptr<Window> child1(
-      CreateTestWindowWithDelegate(&last_event_location_delegate1, 123,
-                                   gfx::Rect(10, 10, 100, 100), root_window()));
-  LastEventLocationDelegate last_event_location_delegate2;
-  std::unique_ptr<Window> child2(
-      CreateTestWindowWithDelegate(&last_event_location_delegate2, 124,
-                                   gfx::Rect(20, 30, 100, 100), child1.get()));
-
-  const gfx::Point mouse_location(30, 40);
-  ui::MouseEvent mouse(ui::ET_MOUSE_PRESSED, mouse_location, mouse_location,
-                       ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON,
-                       ui::EF_LEFT_MOUSE_BUTTON);
-  DispatchEventUsingWindowDispatcher(&mouse);
-  EXPECT_EQ(0, last_event_location_delegate1.mouse_event_count());
-  EXPECT_EQ(1, last_event_location_delegate2.mouse_event_count());
-  EXPECT_EQ(gfx::Point(), last_event_location_delegate1.last_mouse_location());
-  EXPECT_EQ(mouse_location,
-            last_event_location_delegate2.last_mouse_location());
-}
-
-TEST_F(WindowEventDispatcherMusTest, UseDefaultTargeterToFindTarget2) {
-  LastEventLocationDelegate last_event_location_delegate1;
-  std::unique_ptr<Window> child1(
-      CreateTestWindowWithDelegate(&last_event_location_delegate1, 123,
-                                   gfx::Rect(10, 10, 100, 100), root_window()));
-  LastEventLocationDelegate last_event_location_delegate2;
-  std::unique_ptr<Window> child2(
-      CreateTestWindowWithDelegate(&last_event_location_delegate2, 124,
-                                   gfx::Rect(20, 30, 100, 100), child1.get()));
-
-  const gfx::Point mouse_location(15, 25);
-  ui::MouseEvent mouse(ui::ET_MOUSE_PRESSED, mouse_location, mouse_location,
-                       ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON,
-                       ui::EF_LEFT_MOUSE_BUTTON);
-  DispatchEventUsingWindowDispatcher(&mouse);
-  EXPECT_EQ(1, last_event_location_delegate1.mouse_event_count());
-  EXPECT_EQ(0, last_event_location_delegate2.mouse_event_count());
-  EXPECT_EQ(mouse_location,
-            last_event_location_delegate1.last_mouse_location());
-  EXPECT_EQ(gfx::Point(), last_event_location_delegate2.last_mouse_location());
-}
-
 class NestedLocationDelegate : public test::TestWindowDelegate {
  public:
   NestedLocationDelegate() {}
diff --git a/ui/aura/window_tree_host.cc b/ui/aura/window_tree_host.cc
index e624728..039ec6d 100644
--- a/ui/aura/window_tree_host.cc
+++ b/ui/aura/window_tree_host.cc
@@ -260,6 +260,8 @@
     window()->Init(ui::LAYER_NOT_DRAWN);
     window()->set_host(this);
     window()->SetName("RootWindow");
+    window()->SetEventTargeter(
+        std::unique_ptr<ui::EventTargeter>(new WindowTargeter()));
     dispatcher_.reset(new WindowEventDispatcher(this));
   }
 }
diff --git a/ui/events/event_processor.cc b/ui/events/event_processor.cc
index 39678dc2..61d2e72 100644
--- a/ui/events/event_processor.cc
+++ b/ui/events/event_processor.cc
@@ -10,6 +10,11 @@
 namespace ui {
 
 EventDispatchDetails EventProcessor::OnEventFromSource(Event* event) {
+  EventTarget* root = GetRootTarget();
+  CHECK(root);
+  EventTargeter* targeter = root->GetEventTargeter();
+  CHECK(targeter);
+
   // If |event| is in the process of being dispatched or has already been
   // dispatched, then dispatch a copy of the event instead.
   bool dispatch_original_event = event->phase() == EP_PREDISPATCH;
@@ -21,22 +26,9 @@
   }
 
   OnEventProcessingStarted(event_to_dispatch);
-  EventTarget* target = nullptr;
-  EventTargeter* targeter = nullptr;
-  if (!event_to_dispatch->handled()) {
-    target = event->target();
-    EventTarget* root = GetRootForEvent(event_to_dispatch);
-    DCHECK(root);
-    targeter = root->GetEventTargeter();
-    if (!targeter) {
-      targeter = GetDefaultEventTargeter();
-      if (!target)
-        target = targeter->FindTargetForEvent(root, event_to_dispatch);
-    } else {
-      target = targeter->FindTargetForEvent(root, event_to_dispatch);
-    }
-    DCHECK(targeter);
-  }
+  EventTarget* target = NULL;
+  if (!event_to_dispatch->handled())
+    target = targeter->FindTargetForEvent(root, event_to_dispatch);
 
   EventDispatchDetails details;
   while (target) {
diff --git a/ui/events/event_processor.h b/ui/events/event_processor.h
index 82d30ce..8e63051 100644
--- a/ui/events/event_processor.h
+++ b/ui/events/event_processor.h
@@ -10,21 +10,14 @@
 
 namespace ui {
 
-class EventTargeter;
-
 // EventProcessor receives an event from an EventSource and dispatches it to a
 // tree of EventTargets.
 class EVENTS_EXPORT EventProcessor : public EventDispatcherDelegate {
  public:
   ~EventProcessor() override {}
 
-  // Returns the EventTarget with the right EventTargeter that we should use for
-  // dispatching this |event|.
-  virtual EventTarget* GetRootForEvent(Event* event) = 0;
-
-  // If the root target returned by GetRootForEvent() does not have a
-  // targeter set, then the default targeter is used to find the target.
-  virtual EventTargeter* GetDefaultEventTargeter() = 0;
+  // Returns the root of the tree this event processor owns.
+  virtual EventTarget* GetRootTarget() = 0;
 
   // Dispatches an event received from the EventSource to the tree of
   // EventTargets (whose root is returned by GetRootTarget()).  The co-ordinate
diff --git a/ui/events/event_processor_unittest.cc b/ui/events/event_processor_unittest.cc
index f3a617d..2428a24f 100644
--- a/ui/events/event_processor_unittest.cc
+++ b/ui/events/event_processor_unittest.cc
@@ -38,7 +38,7 @@
   }
 
   TestEventTarget* root() {
-    return static_cast<TestEventTarget*>(processor_.GetRoot());
+    return static_cast<TestEventTarget*>(processor_.GetRootTarget());
   }
 
   TestEventProcessor* processor() {
diff --git a/ui/events/test/test_event_processor.cc b/ui/events/test/test_event_processor.cc
index dd83c4c..f5fbf893 100644
--- a/ui/events/test/test_event_processor.cc
+++ b/ui/events/test/test_event_processor.cc
@@ -19,10 +19,6 @@
 
 TestEventProcessor::~TestEventProcessor() {}
 
-EventTarget* TestEventProcessor::GetRoot() {
-  return root_.get();
-}
-
 void TestEventProcessor::SetRoot(std::unique_ptr<EventTarget> root) {
   root_ = std::move(root);
 }
@@ -37,14 +33,10 @@
   return true;
 }
 
-EventTarget* TestEventProcessor::GetRootForEvent(Event* event) {
+EventTarget* TestEventProcessor::GetRootTarget() {
   return root_.get();
 }
 
-EventTargeter* TestEventProcessor::GetDefaultEventTargeter() {
-  return root_->GetEventTargeter();
-}
-
 EventDispatchDetails TestEventProcessor::OnEventFromSource(Event* event) {
   return EventProcessor::OnEventFromSource(event);
 }
diff --git a/ui/events/test/test_event_processor.h b/ui/events/test/test_event_processor.h
index 1e484a45..9b71282 100644
--- a/ui/events/test/test_event_processor.h
+++ b/ui/events/test/test_event_processor.h
@@ -30,14 +30,12 @@
     should_processing_occur_ = occur;
   }
 
-  EventTarget* GetRoot();
   void SetRoot(std::unique_ptr<EventTarget> root);
   void Reset();
 
   // EventProcessor:
   bool CanDispatchToTarget(EventTarget* target) override;
-  EventTarget* GetRootForEvent(Event* event) override;
-  EventTargeter* GetDefaultEventTargeter() override;
+  EventTarget* GetRootTarget() override;
   EventDispatchDetails OnEventFromSource(Event* event) override;
   void OnEventProcessingStarted(Event* event) override;
   void OnEventProcessingFinished(Event* event) override;
diff --git a/ui/views/bubble/bubble_window_targeter_unittest.cc b/ui/views/bubble/bubble_window_targeter_unittest.cc
index 975d68e82..3533ad2 100644
--- a/ui/views/bubble/bubble_window_targeter_unittest.cc
+++ b/ui/views/bubble/bubble_window_targeter_unittest.cc
@@ -7,7 +7,6 @@
 #include "base/macros.h"
 #include "ui/aura/window.h"
 #include "ui/aura/window_event_dispatcher.h"
-#include "ui/aura/window_tree_host.h"
 #include "ui/events/event_utils.h"
 #include "ui/views/bubble/bubble_border.h"
 #include "ui/views/bubble/bubble_dialog_delegate.h"
@@ -88,9 +87,8 @@
 };
 
 TEST_F(BubbleWindowTargeterTest, HitTest) {
-  aura::Window* root = bubble_widget()->GetNativeWindow()->GetRootWindow();
-  ui::EventTargeter* targeter =
-      root->GetHost()->dispatcher()->GetDefaultEventTargeter();
+  ui::EventTarget* root = bubble_widget()->GetNativeWindow()->GetRootWindow();
+  ui::EventTargeter* targeter = root->GetEventTargeter();
   aura::Window* bubble_window = bubble_widget()->GetNativeWindow();
   gfx::Rect bubble_bounds = bubble_window->GetBoundsInRootWindow();
 
diff --git a/ui/views/test/event_generator_delegate_mac.mm b/ui/views/test/event_generator_delegate_mac.mm
index 925401e..6fe8fb3 100644
--- a/ui/views/test/event_generator_delegate_mac.mm
+++ b/ui/views/test/event_generator_delegate_mac.mm
@@ -265,10 +265,7 @@
   ui::EventProcessor* GetEventProcessor() override { return this; }
 
   // Overridden from ui::EventProcessor:
-  ui::EventTarget* GetRootForEvent(ui::Event* event) override { return this; }
-  ui::EventTargeter* GetDefaultEventTargeter() override {
-    return this->GetEventTargeter();
-  }
+  ui::EventTarget* GetRootTarget() override { return this; }
 
   // Overridden from ui::EventDispatcherDelegate (via ui::EventProcessor):
   bool CanDispatchToTarget(EventTarget* target) override { return true; }
diff --git a/ui/views/touchui/touch_selection_controller_impl_unittest.cc b/ui/views/touchui/touch_selection_controller_impl_unittest.cc
index 47430626..83de784 100644
--- a/ui/views/touchui/touch_selection_controller_impl_unittest.cc
+++ b/ui/views/touchui/touch_selection_controller_impl_unittest.cc
@@ -10,8 +10,6 @@
 #include "ui/aura/client/screen_position_client.h"
 #include "ui/aura/test/test_cursor_client.h"
 #include "ui/aura/window.h"
-#include "ui/aura/window_event_dispatcher.h"
-#include "ui/aura/window_tree_host.h"
 #include "ui/base/resource/resource_bundle.h"
 #include "ui/base/touch/touch_editing_controller.h"
 #include "ui/base/ui_base_switches.h"
@@ -796,9 +794,8 @@
   if (IsMus())
     return;
 
-  aura::Window* root = GetContext();
-  ui::EventTargeter* targeter =
-      root->GetHost()->dispatcher()->GetDefaultEventTargeter();
+  ui::EventTarget* root = GetContext();
+  ui::EventTargeter* targeter = root->GetEventTargeter();
 
   // Create the first window containing a Views::Textfield.
   CreateTextfield();
diff --git a/ui/views/widget/root_view.cc b/ui/views/widget/root_view.cc
index a9926a68..d245548 100644
--- a/ui/views/widget/root_view.cc
+++ b/ui/views/widget/root_view.cc
@@ -252,14 +252,10 @@
 ////////////////////////////////////////////////////////////////////////////////
 // RootView, ui::EventProcessor overrides:
 
-ui::EventTarget* RootView::GetRootForEvent(ui::Event* event) {
+ui::EventTarget* RootView::GetRootTarget() {
   return this;
 }
 
-ui::EventTargeter* RootView::GetDefaultEventTargeter() {
-  return this->GetEventTargeter();
-}
-
 void RootView::OnEventProcessingStarted(ui::Event* event) {
   if (!event->IsGestureEvent())
     return;
diff --git a/ui/views/widget/root_view.h b/ui/views/widget/root_view.h
index d8212a24..18b0021b 100644
--- a/ui/views/widget/root_view.h
+++ b/ui/views/widget/root_view.h
@@ -97,8 +97,7 @@
   View* GetFocusTraversableParentView() override;
 
   // Overridden from ui::EventProcessor:
-  ui::EventTarget* GetRootForEvent(ui::Event* event) override;
-  ui::EventTargeter* GetDefaultEventTargeter() override;
+  ui::EventTarget* GetRootTarget() override;
   void OnEventProcessingStarted(ui::Event* event) override;
   void OnEventProcessingFinished(ui::Event* event) override;