diff --git a/DEPS b/DEPS
index 8dfe513..a81bc2e 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': 'fb58f14643e0935d853e015f77d76c3d4cee47ac',
+  'skia_revision': 'f1b61afbe97199896bfbcadb68758bfcf0dc803a',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling V8
   # 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': '89b70b666f2f27974e80e0cbb896feca8a7695a2',
+  'catapult_revision': '2ae07fc28bb3b248baa71647d5cfcefd67e57a84',
   # 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/java/src/org/chromium/android_webview/AwContents.java b/android_webview/java/src/org/chromium/android_webview/AwContents.java
index 561a019..f4e1cfe 100644
--- a/android_webview/java/src/org/chromium/android_webview/AwContents.java
+++ b/android_webview/java/src/org/chromium/android_webview/AwContents.java
@@ -3158,7 +3158,7 @@
             }
 
             mScrollOffsetManager.setProcessingTouchEvent(true);
-            boolean rv = mContentViewCore.onTouchEvent(event);
+            boolean rv = mWebContents.getEventForwarder().onTouchEvent(event);
             mScrollOffsetManager.setProcessingTouchEvent(false);
 
             if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
diff --git a/android_webview/java/src/org/chromium/android_webview/PopupTouchHandleDrawable.java b/android_webview/java/src/org/chromium/android_webview/PopupTouchHandleDrawable.java
index 1e809d1..72734d60 100644
--- a/android_webview/java/src/org/chromium/android_webview/PopupTouchHandleDrawable.java
+++ b/android_webview/java/src/org/chromium/android_webview/PopupTouchHandleDrawable.java
@@ -249,7 +249,9 @@
         final float offsetY = event.getRawY() - event.getY() - mTempScreenCoords[1];
         final MotionEvent offsetEvent = MotionEvent.obtainNoHistory(event);
         offsetEvent.offsetLocation(offsetX, offsetY);
-        final boolean handled = mContentViewCore.onTouchHandleEvent(offsetEvent);
+        final boolean handled =
+                mContentViewCore.getWebContents().getEventForwarder().onTouchHandleEvent(
+                        offsetEvent);
         offsetEvent.recycle();
         return handled;
     }
diff --git a/ash/BUILD.gn b/ash/BUILD.gn
index 69396a9..221d41c8 100644
--- a/ash/BUILD.gn
+++ b/ash/BUILD.gn
@@ -453,6 +453,7 @@
     "common/wallpaper/wallpaper_view.h",
     "common/wallpaper/wallpaper_widget_controller.cc",
     "common/wallpaper/wallpaper_widget_controller.h",
+    "common/window_user_data.h",
     "common/wm/always_on_top_controller.cc",
     "common/wm/always_on_top_controller.h",
     "common/wm/container_finder.cc",
@@ -584,7 +585,6 @@
     "common/wm_transient_window_observer.h",
     "common/wm_window.cc",
     "common/wm_window.h",
-    "common/wm_window_user_data.h",
     "debug.cc",
     "debug.h",
     "default_wallpaper_delegate.cc",
@@ -1199,12 +1199,12 @@
     "common/system/update/tray_update_unittest.cc",
     "common/system/user/tray_user_unittest.cc",
     "common/wallpaper/wallpaper_controller_unittest.cc",
+    "common/window_user_data_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",
diff --git a/ash/DEPS b/ash/DEPS
index 6fb9784c..e62e79c 100644
--- a/ash/DEPS
+++ b/ash/DEPS
@@ -4,6 +4,7 @@
   "+components/quirks",
   "+components/session_manager",
   "+components/signin/core/account_id",
+  "+components/ui_devtools",
   "+components/user_manager",
   "+components/wallpaper",
   "+gpu/config",
diff --git a/ash/accelerators/accelerator_controller_unittest.cc b/ash/accelerators/accelerator_controller_unittest.cc
index 784cc02c..f50aa050 100644
--- a/ash/accelerators/accelerator_controller_unittest.cc
+++ b/ash/accelerators/accelerator_controller_unittest.cc
@@ -1022,7 +1022,8 @@
   app_list::test::TestAppListPresenter test_app_list_presenter;
   WmShell::Get()->app_list()->SetAppListPresenter(
       test_app_list_presenter.CreateInterfacePtrAndBind());
-  AccessibilityDelegate* delegate = WmShell::Get()->accessibility_delegate();
+  AccessibilityDelegate* delegate =
+      Shell::GetInstance()->accessibility_delegate();
 
   // The press event should not toggle the AppList, the release should instead.
   EXPECT_FALSE(
@@ -1436,7 +1437,8 @@
 }
 
 TEST_F(AcceleratorControllerTest, DisallowedWithNoWindow) {
-  AccessibilityDelegate* delegate = WmShell::Get()->accessibility_delegate();
+  AccessibilityDelegate* delegate =
+      Shell::GetInstance()->accessibility_delegate();
 
   for (size_t i = 0; i < kActionsNeedingWindowLength; ++i) {
     delegate->TriggerAccessibilityAlert(A11Y_ALERT_NONE);
diff --git a/ash/accelerators/spoken_feedback_toggler.cc b/ash/accelerators/spoken_feedback_toggler.cc
index e62e75c1..942208a 100644
--- a/ash/accelerators/spoken_feedback_toggler.cc
+++ b/ash/accelerators/spoken_feedback_toggler.cc
@@ -9,7 +9,7 @@
 #include "ash/accelerators/key_hold_detector.h"
 #include "ash/common/accessibility_delegate.h"
 #include "ash/common/accessibility_types.h"
-#include "ash/common/wm_shell.h"
+#include "ash/shell.h"
 #include "ui/events/event.h"
 
 namespace ash {
@@ -53,7 +53,7 @@
 void SpokenFeedbackToggler::OnKeyHold(const ui::KeyEvent* event) {
   if (!toggled_) {
     toggled_ = true;
-    WmShell::Get()->accessibility_delegate()->ToggleSpokenFeedback(
+    Shell::GetInstance()->accessibility_delegate()->ToggleSpokenFeedback(
         A11Y_NOTIFICATION_SHOW);
   }
 }
diff --git a/ash/accelerators/spoken_feedback_toggler_unittest.cc b/ash/accelerators/spoken_feedback_toggler_unittest.cc
index 8680dcb..6835fa3 100644
--- a/ash/accelerators/spoken_feedback_toggler_unittest.cc
+++ b/ash/accelerators/spoken_feedback_toggler_unittest.cc
@@ -4,7 +4,7 @@
 
 #include "ash/accelerators/spoken_feedback_toggler.h"
 #include "ash/common/accessibility_delegate.h"
-#include "ash/common/wm_shell.h"
+#include "ash/shell.h"
 #include "ash/test/ash_test_base.h"
 #include "ash/wm/window_util.h"
 #include "ui/aura/test/test_window_delegate.h"
@@ -17,7 +17,8 @@
 
 TEST_F(SpokenFeedbackTogglerTest, Basic) {
   SpokenFeedbackToggler::ScopedEnablerForTest scoped;
-  AccessibilityDelegate* delegate = WmShell::Get()->accessibility_delegate();
+  AccessibilityDelegate* delegate =
+      Shell::GetInstance()->accessibility_delegate();
   ui::test::EventGenerator& generator = GetEventGenerator();
   EXPECT_FALSE(delegate->IsSpokenFeedbackEnabled());
 
diff --git a/ash/ash_touch_exploration_manager_chromeos.cc b/ash/ash_touch_exploration_manager_chromeos.cc
index 1cd1ccd0..e77cd13 100644
--- a/ash/ash_touch_exploration_manager_chromeos.cc
+++ b/ash/ash_touch_exploration_manager_chromeos.cc
@@ -58,8 +58,8 @@
 }
 
 void AshTouchExplorationManager::SilenceSpokenFeedback() {
-  if (WmShell::Get()->accessibility_delegate()->IsSpokenFeedbackEnabled())
-    WmShell::Get()->accessibility_delegate()->SilenceSpokenFeedback();
+  if (Shell::GetInstance()->accessibility_delegate()->IsSpokenFeedbackEnabled())
+    Shell::GetInstance()->accessibility_delegate()->SilenceSpokenFeedback();
 }
 
 void AshTouchExplorationManager::PlayVolumeAdjustEarcon() {
@@ -67,29 +67,30 @@
     return;
   if (!audio_handler_->IsOutputMuted() &&
       audio_handler_->GetOutputVolumePercent() != 100) {
-    WmShell::Get()->accessibility_delegate()->PlayEarcon(
+    Shell::GetInstance()->accessibility_delegate()->PlayEarcon(
         chromeos::SOUND_VOLUME_ADJUST);
   }
 }
 
 void AshTouchExplorationManager::PlayPassthroughEarcon() {
-  WmShell::Get()->accessibility_delegate()->PlayEarcon(
+  Shell::GetInstance()->accessibility_delegate()->PlayEarcon(
       chromeos::SOUND_PASSTHROUGH);
 }
 
 void AshTouchExplorationManager::PlayExitScreenEarcon() {
-  WmShell::Get()->accessibility_delegate()->PlayEarcon(
+  Shell::GetInstance()->accessibility_delegate()->PlayEarcon(
       chromeos::SOUND_EXIT_SCREEN);
 }
 
 void AshTouchExplorationManager::PlayEnterScreenEarcon() {
-  WmShell::Get()->accessibility_delegate()->PlayEarcon(
+  Shell::GetInstance()->accessibility_delegate()->PlayEarcon(
       chromeos::SOUND_ENTER_SCREEN);
 }
 
 void AshTouchExplorationManager::HandleAccessibilityGesture(
     ui::AXGesture gesture) {
-  WmShell::Get()->accessibility_delegate()->HandleAccessibilityGesture(gesture);
+  Shell::GetInstance()->accessibility_delegate()->HandleAccessibilityGesture(
+      gesture);
 }
 
 void AshTouchExplorationManager::OnDisplayMetricsChanged(
@@ -102,13 +103,15 @@
 
 void AshTouchExplorationManager::PlaySpokenFeedbackToggleCountdown(
     int tick_count) {
-  AccessibilityDelegate* delegate = WmShell::Get()->accessibility_delegate();
+  AccessibilityDelegate* delegate =
+      Shell::GetInstance()->accessibility_delegate();
   if (delegate->ShouldToggleSpokenFeedbackViaTouch())
     delegate->PlaySpokenFeedbackToggleCountdown(tick_count);
 }
 
 void AshTouchExplorationManager::ToggleSpokenFeedback() {
-  AccessibilityDelegate* delegate = WmShell::Get()->accessibility_delegate();
+  AccessibilityDelegate* delegate =
+      Shell::GetInstance()->accessibility_delegate();
   if (delegate->ShouldToggleSpokenFeedbackViaTouch())
     delegate->ToggleSpokenFeedback(ash::A11Y_NOTIFICATION_SHOW);
 }
@@ -138,7 +141,7 @@
       wm::GetActiveWindow()->GetName() == kExoShellSurfaceWindowName;
 
   const bool spoken_feedback_enabled =
-      WmShell::Get()->accessibility_delegate()->IsSpokenFeedbackEnabled();
+      Shell::GetInstance()->accessibility_delegate()->IsSpokenFeedbackEnabled();
 
   if (!touch_accessibility_enabler_) {
     // Always enable gesture to toggle spoken feedback.
@@ -159,7 +162,7 @@
                                        .work_area();
       touch_exploration_controller_->SetExcludeBounds(work_area);
       SilenceSpokenFeedback();
-      WmShell::Get()->accessibility_delegate()->ClearFocusHighlight();
+      Shell::GetInstance()->accessibility_delegate()->ClearFocusHighlight();
     } else {
       touch_exploration_controller_->SetExcludeBounds(gfx::Rect());
     }
diff --git a/ash/common/DEPS b/ash/common/DEPS
index dbf6b2b..038605e3 100644
--- a/ash/common/DEPS
+++ b/ash/common/DEPS
@@ -1,6 +1,5 @@
 include_rules = [
   "+components/prefs",
-  "+components/ui_devtools",
   "+mojo/public/cpp",
   "+services/preferences/public",
   "+ui",
diff --git a/ash/common/accelerators/accelerator_controller.cc b/ash/common/accelerators/accelerator_controller.cc
index 46abdd9..944245e 100644
--- a/ash/common/accelerators/accelerator_controller.cc
+++ b/ash/common/accelerators/accelerator_controller.cc
@@ -263,7 +263,9 @@
     // When spoken feedback is enabled, we should neither toggle the list nor
     // consume the key since Search+Shift is one of the shortcuts the a11y
     // feature uses. crbug.com/132296
-    if (WmShell::Get()->accessibility_delegate()->IsSpokenFeedbackEnabled())
+    if (Shell::GetInstance()
+            ->accessibility_delegate()
+            ->IsSpokenFeedbackEnabled())
       return false;
   }
   return true;
@@ -402,8 +404,8 @@
 }
 
 bool CanHandleShowStylusTools() {
-  return WmShell::Get()->palette_delegate() &&
-         WmShell::Get()->palette_delegate()->ShouldShowPalette();
+  return Shell::GetInstance()->palette_delegate() &&
+         Shell::GetInstance()->palette_delegate()->ShouldShowPalette();
 }
 
 void HandleSuspend() {
@@ -491,13 +493,13 @@
   message_center::MessageCenter::Get()->AddNotification(
       std::move(notification));
 
-  WmShell::Get()->accessibility_delegate()->ToggleHighContrast();
+  Shell::GetInstance()->accessibility_delegate()->ToggleHighContrast();
 }
 
 void HandleToggleSpokenFeedback() {
   base::RecordAction(UserMetricsAction("Accel_Toggle_Spoken_Feedback"));
 
-  WmShell::Get()->accessibility_delegate()->ToggleSpokenFeedback(
+  Shell::GetInstance()->accessibility_delegate()->ToggleSpokenFeedback(
       A11Y_NOTIFICATION_SHOW);
 }
 
@@ -1124,7 +1126,7 @@
   }
   if (wm_shell->mru_window_tracker()->BuildMruWindowList().empty() &&
       actions_needing_window_.find(action) != actions_needing_window_.end()) {
-    wm_shell->accessibility_delegate()->TriggerAccessibilityAlert(
+    Shell::GetInstance()->accessibility_delegate()->TriggerAccessibilityAlert(
         A11Y_ALERT_WINDOW_NEEDED);
     return RESTRICTION_PREVENT_PROCESSING_AND_PROPAGATION;
   }
diff --git a/ash/common/accelerators/debug_commands.cc b/ash/common/accelerators/debug_commands.cc
index c84d84f..58633fba 100644
--- a/ash/common/accelerators/debug_commands.cc
+++ b/ash/common/accelerators/debug_commands.cc
@@ -15,6 +15,7 @@
 #include "ash/common/wm_shell.h"
 #include "ash/common/wm_window.h"
 #include "ash/root_window_controller.h"
+#include "ash/shell.h"
 #include "ash/wm/window_properties.h"
 #include "base/command_line.h"
 #include "base/metrics/user_metrics.h"
@@ -103,7 +104,7 @@
 void HandleToggleWallpaperMode() {
   static int index = 0;
   WallpaperController* wallpaper_controller =
-      WmShell::Get()->wallpaper_controller();
+      Shell::GetInstance()->wallpaper_controller();
   switch (++index % 4) {
     case 0:
       ash::WmShell::Get()->wallpaper_delegate()->InitializeWallpaper();
@@ -187,7 +188,7 @@
       HandlePrintWindowHierarchy();
       break;
     case DEBUG_SHOW_TOAST:
-      WmShell::Get()->toast_manager()->Show(
+      Shell::GetInstance()->toast_manager()->Show(
           ToastData("id", base::ASCIIToUTF16("Toast"), 5000 /* duration_ms */,
                     base::ASCIIToUTF16("Dismiss")));
       break;
diff --git a/ash/common/devtools/ash_devtools_dom_agent.cc b/ash/common/devtools/ash_devtools_dom_agent.cc
index 79aa4339..f72c249 100644
--- a/ash/common/devtools/ash_devtools_dom_agent.cc
+++ b/ash/common/devtools/ash_devtools_dom_agent.cc
@@ -7,6 +7,7 @@
 #include "ash/common/wm_window.h"
 #include "ash/public/cpp/shell_window_ids.h"
 #include "ash/root_window_controller.h"
+#include "ash/shell.h"
 #include "components/ui_devtools/devtools_server.h"
 #include "third_party/skia/include/core/SkColor.h"
 #include "ui/display/display.h"
@@ -104,9 +105,7 @@
 
 }  // namespace
 
-AshDevToolsDOMAgent::AshDevToolsDOMAgent(ash::WmShell* shell) : shell_(shell) {
-  DCHECK(shell_);
-}
+AshDevToolsDOMAgent::AshDevToolsDOMAgent() {}
 
 AshDevToolsDOMAgent::~AshDevToolsDOMAgent() {
   RemoveObservers();
@@ -247,8 +246,8 @@
 std::unique_ptr<ui::devtools::protocol::DOM::Node>
 AshDevToolsDOMAgent::BuildInitialTree() {
   std::unique_ptr<Array<DOM::Node>> children = Array<DOM::Node>::create();
-  for (ash::WmWindow* window : shell_->GetAllRootWindows())
-    children->addItem(BuildTreeForWindow(window));
+  for (aura::Window* window : Shell::GetAllRootWindows())
+    children->addItem(BuildTreeForWindow(WmWindow::Get(window)));
   return BuildNode("root", nullptr, std::move(children));
 }
 
@@ -475,7 +474,7 @@
   params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
   params.opacity = views::Widget::InitParams::WindowOpacity::TRANSLUCENT_WINDOW;
   params.name = "HighlightingWidget";
-  shell_->GetPrimaryRootWindowController()
+  Shell::GetPrimaryRootWindowController()
       ->ConfigureWidgetInitParamsForContainer(widget_for_highlighting_.get(),
                                               kShellWindowId_OverlayContainer,
                                               &params);
diff --git a/ash/common/devtools/ash_devtools_dom_agent.h b/ash/common/devtools/ash_devtools_dom_agent.h
index c66a04f2..9799043 100644
--- a/ash/common/devtools/ash_devtools_dom_agent.h
+++ b/ash/common/devtools/ash_devtools_dom_agent.h
@@ -5,8 +5,8 @@
 #ifndef ASH_COMMON_DEVTOOLS_ASH_DEVTOOLS_DOM_AGENT_H_
 #define ASH_COMMON_DEVTOOLS_ASH_DEVTOOLS_DOM_AGENT_H_
 
-#include "ash/common/wm_shell.h"
-#include "base/compiler_specific.h"
+#include "ash/ash_export.h"
+#include "base/macros.h"
 #include "base/observer_list.h"
 #include "components/ui_devtools/DOM.h"
 #include "components/ui_devtools/devtools_base_agent.h"
@@ -18,6 +18,9 @@
 #include "ui/views/widget/widget_removals_observer.h"
 
 namespace ash {
+
+class WmWindow;
+
 namespace devtools {
 
 class ASH_EXPORT AshDevToolsDOMAgentObserver {
@@ -35,7 +38,7 @@
       public views::WidgetRemovalsObserver,
       public views::ViewObserver {
  public:
-  AshDevToolsDOMAgent(ash::WmShell* shell);
+  AshDevToolsDOMAgent();
   ~AshDevToolsDOMAgent() override;
 
   // DOM::Backend
@@ -127,7 +130,6 @@
   bool IsHighlightingWindow(WmWindow* window);
 
   std::unique_ptr<views::Widget> widget_for_highlighting_;
-  ash::WmShell* shell_;
 
   using WindowToNodeIdMap = std::unordered_map<WmWindow*, int>;
   WindowToNodeIdMap window_to_node_id_map_;
diff --git a/ash/common/devtools/ash_devtools_unittest.cc b/ash/common/devtools/ash_devtools_unittest.cc
index 7c96bff..865195997 100644
--- a/ash/common/devtools/ash_devtools_unittest.cc
+++ b/ash/common/devtools/ash_devtools_unittest.cc
@@ -199,8 +199,7 @@
     fake_frontend_channel_ = base::MakeUnique<FakeFrontendChannel>();
     uber_dispatcher_ =
         base::MakeUnique<UberDispatcher>(fake_frontend_channel_.get());
-    dom_agent_ =
-        base::MakeUnique<devtools::AshDevToolsDOMAgent>(WmShell::Get());
+    dom_agent_ = base::MakeUnique<devtools::AshDevToolsDOMAgent>();
     dom_agent_->Init(uber_dispatcher_.get());
     css_agent_ =
         base::MakeUnique<devtools::AshDevToolsCSSAgent>(dom_agent_.get());
diff --git a/ash/common/keyboard/keyboard_ui.cc b/ash/common/keyboard/keyboard_ui.cc
index 37fcc44..b5f8a559 100644
--- a/ash/common/keyboard/keyboard_ui.cc
+++ b/ash/common/keyboard/keyboard_ui.cc
@@ -10,6 +10,7 @@
 #include "ash/common/system/tray/system_tray_notifier.h"
 #include "ash/common/system/tray_accessibility.h"
 #include "ash/common/wm_shell.h"
+#include "ash/shell.h"
 #include "base/memory/ptr_util.h"
 #include "ui/keyboard/keyboard_controller.h"
 
@@ -39,7 +40,9 @@
     // to the appropriate keyboard functions.
   }
   bool IsEnabled() override {
-    return WmShell::Get()->accessibility_delegate()->IsVirtualKeyboardEnabled();
+    return Shell::GetInstance()
+        ->accessibility_delegate()
+        ->IsVirtualKeyboardEnabled();
   }
 
   // AccessibilityObserver:
diff --git a/ash/common/mojo_interface_factory.cc b/ash/common/mojo_interface_factory.cc
index 501c006..f074291 100644
--- a/ash/common/mojo_interface_factory.cc
+++ b/ash/common/mojo_interface_factory.cc
@@ -20,6 +20,7 @@
 #include "ash/common/wallpaper/wallpaper_controller.h"
 #include "ash/common/wm/maximize_mode/maximize_mode_controller.h"
 #include "ash/common/wm_shell.h"
+#include "ash/shell.h"
 #include "base/bind.h"
 #include "services/service_manager/public/cpp/interface_registry.h"
 #include "ui/app_list/presenter/app_list.h"
@@ -85,7 +86,7 @@
 
 void BindWallpaperRequestOnMainThread(
     mojom::WallpaperControllerRequest request) {
-  WmShell::Get()->wallpaper_controller()->BindRequest(std::move(request));
+  Shell::GetInstance()->wallpaper_controller()->BindRequest(std::move(request));
 }
 
 }  // namespace
diff --git a/ash/common/shelf/shelf_widget.cc b/ash/common/shelf/shelf_widget.cc
index 248b4d5..3b3a7978 100644
--- a/ash/common/shelf/shelf_widget.cc
+++ b/ash/common/shelf/shelf_widget.cc
@@ -20,6 +20,7 @@
 #include "ash/common/wm_shell.h"
 #include "ash/common/wm_window.h"
 #include "ash/root_window_controller.h"
+#include "ash/shell.h"
 #include "ash/wm/window_properties.h"
 #include "base/memory/ptr_util.h"
 #include "ui/compositor/layer.h"
@@ -134,7 +135,7 @@
       shelf_view_(nullptr),
       background_animator_(SHELF_BACKGROUND_DEFAULT,
                            wm_shelf_,
-                           WmShell::Get()->wallpaper_controller()),
+                           Shell::GetInstance()->wallpaper_controller()),
       activating_as_fallback_(false) {
   DCHECK(wm_shelf_);
   background_animator_.AddObserver(this);
diff --git a/ash/common/system/chromeos/ime_menu/ime_menu_tray.cc b/ash/common/system/chromeos/ime_menu/ime_menu_tray.cc
index 90db052..a2c80135 100644
--- a/ash/common/system/chromeos/ime_menu/ime_menu_tray.cc
+++ b/ash/common/system/chromeos/ime_menu/ime_menu_tray.cc
@@ -25,6 +25,7 @@
 #include "ash/public/cpp/shell_window_ids.h"
 #include "ash/resources/grit/ash_resources.h"
 #include "ash/root_window_controller.h"
+#include "ash/shell.h"
 #include "ash/strings/grit/ash_strings.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/strings/utf_string_conversions.h"
@@ -470,7 +471,7 @@
   }
 
   AccessibilityDelegate* accessibility_delegate =
-      WmShell::Get()->accessibility_delegate();
+      Shell::GetInstance()->accessibility_delegate();
   // Fails to show the keyboard.
   if (accessibility_delegate->IsVirtualKeyboardEnabled())
     return;
@@ -503,8 +504,9 @@
 }
 
 bool ImeMenuTray::ShouldShowKeyboardToggle() const {
-  return keyboard_suppressed_ &&
-         !WmShell::Get()->accessibility_delegate()->IsVirtualKeyboardEnabled();
+  return keyboard_suppressed_ && !Shell::GetInstance()
+                                      ->accessibility_delegate()
+                                      ->IsVirtualKeyboardEnabled();
 }
 
 void ImeMenuTray::SetShelfAlignment(ShelfAlignment alignment) {
@@ -626,7 +628,8 @@
   if (!force_show_keyboard_)
     return;
 
-  WmShell::Get()->accessibility_delegate()->SetVirtualKeyboardEnabled(false);
+  Shell::GetInstance()->accessibility_delegate()->SetVirtualKeyboardEnabled(
+      false);
   force_show_keyboard_ = false;
 }
 
diff --git a/ash/common/system/chromeos/ime_menu/ime_menu_tray_unittest.cc b/ash/common/system/chromeos/ime_menu/ime_menu_tray_unittest.cc
index a7ebfc31..c5a59239 100644
--- a/ash/common/system/chromeos/ime_menu/ime_menu_tray_unittest.cc
+++ b/ash/common/system/chromeos/ime_menu/ime_menu_tray_unittest.cc
@@ -12,6 +12,7 @@
 #include "ash/common/system/tray/system_tray_notifier.h"
 #include "ash/common/test/test_system_tray_delegate.h"
 #include "ash/common/wm_shell.h"
+#include "ash/shell.h"
 #include "ash/test/ash_test_base.h"
 #include "ash/test/status_area_widget_test_helper.h"
 #include "base/strings/utf_string_conversions.h"
@@ -259,7 +260,7 @@
   EXPECT_TRUE(IsBubbleShown());
 
   AccessibilityDelegate* accessibility_delegate =
-      WmShell::Get()->accessibility_delegate();
+      Shell::GetInstance()->accessibility_delegate();
 
   accessibility_delegate->SetVirtualKeyboardEnabled(true);
   EXPECT_TRUE(accessibility_delegate->IsVirtualKeyboardEnabled());
@@ -278,7 +279,7 @@
 
 TEST_F(ImeMenuTrayTest, ForceToShowEmojiKeyset) {
   AccessibilityDelegate* accessibility_delegate =
-      WmShell::Get()->accessibility_delegate();
+      Shell::GetInstance()->accessibility_delegate();
   accessibility_delegate->SetVirtualKeyboardEnabled(false);
   ASSERT_FALSE(accessibility_delegate->IsVirtualKeyboardEnabled());
 
diff --git a/ash/common/system/chromeos/palette/palette_tray.cc b/ash/common/system/chromeos/palette/palette_tray.cc
index 113b02f..4fc2922 100644
--- a/ash/common/system/chromeos/palette/palette_tray.cc
+++ b/ash/common/system/chromeos/palette/palette_tray.cc
@@ -288,7 +288,7 @@
 }
 
 void PaletteTray::OnStylusStateChanged(ui::StylusState stylus_state) {
-  PaletteDelegate* palette_delegate = WmShell::Get()->palette_delegate();
+  PaletteDelegate* palette_delegate = Shell::GetInstance()->palette_delegate();
 
   // Don't do anything if the palette should not be shown or if the user has
   // disabled it all-together.
@@ -400,7 +400,7 @@
 }
 
 void PaletteTray::Initialize() {
-  PaletteDelegate* delegate = WmShell::Get()->palette_delegate();
+  PaletteDelegate* delegate = Shell::GetInstance()->palette_delegate();
   // |delegate| can be null in tests.
   if (!delegate)
     return;
diff --git a/ash/common/system/chromeos/palette/tools/capture_region_mode.cc b/ash/common/system/chromeos/palette/tools/capture_region_mode.cc
index bdcbb64..dd0c016 100644
--- a/ash/common/system/chromeos/palette/tools/capture_region_mode.cc
+++ b/ash/common/system/chromeos/palette/tools/capture_region_mode.cc
@@ -9,8 +9,8 @@
 #include "ash/common/system/chromeos/palette/palette_ids.h"
 #include "ash/common/system/toast/toast_data.h"
 #include "ash/common/system/toast/toast_manager.h"
-#include "ash/common/wm_shell.h"
 #include "ash/resources/vector_icons/vector_icons.h"
+#include "ash/shell.h"
 #include "ash/strings/grit/ash_strings.h"
 #include "ui/base/l10n/l10n_util.h"
 
@@ -46,9 +46,9 @@
   ToastData toast(kToastId, l10n_util::GetStringUTF16(
                                 IDS_ASH_STYLUS_TOOLS_CAPTURE_REGION_TOAST),
                   kToastDurationMs, base::Optional<base::string16>());
-  ash::WmShell::Get()->toast_manager()->Show(toast);
+  Shell::GetInstance()->toast_manager()->Show(toast);
 
-  WmShell::Get()->palette_delegate()->TakePartialScreenshot(base::Bind(
+  Shell::GetInstance()->palette_delegate()->TakePartialScreenshot(base::Bind(
       &CaptureRegionMode::OnScreenshotDone, weak_factory_.GetWeakPtr()));
   delegate()->HidePalette();
 }
@@ -58,7 +58,7 @@
 
   // If the user manually cancelled the action we need to make sure to cancel
   // the screenshot session as well.
-  WmShell::Get()->palette_delegate()->CancelPartialScreenshot();
+  Shell::GetInstance()->palette_delegate()->CancelPartialScreenshot();
 }
 
 views::View* CaptureRegionMode::CreateView() {
diff --git a/ash/common/system/chromeos/palette/tools/capture_screen_action.cc b/ash/common/system/chromeos/palette/tools/capture_screen_action.cc
index c23e19c..29cfd10 100644
--- a/ash/common/system/chromeos/palette/tools/capture_screen_action.cc
+++ b/ash/common/system/chromeos/palette/tools/capture_screen_action.cc
@@ -6,8 +6,8 @@
 
 #include "ash/common/palette_delegate.h"
 #include "ash/common/system/chromeos/palette/palette_ids.h"
-#include "ash/common/wm_shell.h"
 #include "ash/resources/vector_icons/vector_icons.h"
+#include "ash/shell.h"
 #include "ash/strings/grit/ash_strings.h"
 #include "ui/base/l10n/l10n_util.h"
 
@@ -31,7 +31,7 @@
 
   delegate()->DisableTool(GetToolId());
   delegate()->HidePaletteImmediately();
-  WmShell::Get()->palette_delegate()->TakeScreenshot();
+  Shell::GetInstance()->palette_delegate()->TakeScreenshot();
 }
 
 views::View* CaptureScreenAction::CreateView() {
diff --git a/ash/common/system/chromeos/palette/tools/create_note_action.cc b/ash/common/system/chromeos/palette/tools/create_note_action.cc
index 7cb25335..bbdc251 100644
--- a/ash/common/system/chromeos/palette/tools/create_note_action.cc
+++ b/ash/common/system/chromeos/palette/tools/create_note_action.cc
@@ -6,8 +6,8 @@
 
 #include "ash/common/palette_delegate.h"
 #include "ash/common/system/chromeos/palette/palette_ids.h"
-#include "ash/common/wm_shell.h"
 #include "ash/resources/vector_icons/vector_icons.h"
+#include "ash/shell.h"
 #include "ash/strings/grit/ash_strings.h"
 #include "ui/base/l10n/l10n_util.h"
 
@@ -29,14 +29,14 @@
 void CreateNoteAction::OnEnable() {
   CommonPaletteTool::OnEnable();
 
-  WmShell::Get()->palette_delegate()->CreateNote();
+  Shell::GetInstance()->palette_delegate()->CreateNote();
 
   delegate()->DisableTool(GetToolId());
   delegate()->HidePalette();
 }
 
 views::View* CreateNoteAction::CreateView() {
-  if (!WmShell::Get()->palette_delegate()->HasNoteApp())
+  if (!Shell::GetInstance()->palette_delegate()->HasNoteApp())
     return nullptr;
 
   return CreateDefaultView(
diff --git a/ash/common/system/chromeos/palette/tools/create_note_unittest.cc b/ash/common/system/chromeos/palette/tools/create_note_unittest.cc
index 3ff21b29..d8af84c 100644
--- a/ash/common/system/chromeos/palette/tools/create_note_unittest.cc
+++ b/ash/common/system/chromeos/palette/tools/create_note_unittest.cc
@@ -7,8 +7,9 @@
 #include "ash/common/system/chromeos/palette/palette_tool.h"
 #include "ash/common/system/chromeos/palette/tools/create_note_action.h"
 #include "ash/common/test/test_palette_delegate.h"
-#include "ash/common/wm_shell.h"
+#include "ash/shell.h"
 #include "ash/test/ash_test_base.h"
+#include "ash/test/shell_test_api.h"
 #include "base/macros.h"
 #include "base/memory/ptr_util.h"
 #include "ui/views/view.h"
@@ -26,7 +27,7 @@
   void SetUp() override {
     test::AshTestBase::SetUp();
 
-    WmShell::Get()->SetPaletteDelegateForTesting(
+    test::ShellTestApi().SetPaletteDelegate(
         base::MakeUnique<TestPaletteDelegate>());
 
     palette_tool_delegate_ = base::MakeUnique<MockPaletteToolDelegate>();
@@ -35,7 +36,7 @@
 
   TestPaletteDelegate* test_palette_delegate() {
     return static_cast<TestPaletteDelegate*>(
-        WmShell::Get()->palette_delegate());
+        Shell::GetInstance()->palette_delegate());
   }
 
  protected:
diff --git a/ash/common/system/chromeos/palette/tools/screenshot_unittest.cc b/ash/common/system/chromeos/palette/tools/screenshot_unittest.cc
index faf7eae..f66426d1 100644
--- a/ash/common/system/chromeos/palette/tools/screenshot_unittest.cc
+++ b/ash/common/system/chromeos/palette/tools/screenshot_unittest.cc
@@ -8,8 +8,9 @@
 #include "ash/common/system/chromeos/palette/tools/capture_region_mode.h"
 #include "ash/common/system/chromeos/palette/tools/capture_screen_action.h"
 #include "ash/common/test/test_palette_delegate.h"
-#include "ash/common/wm_shell.h"
+#include "ash/shell.h"
 #include "ash/test/ash_test_base.h"
+#include "ash/test/shell_test_api.h"
 #include "base/macros.h"
 #include "base/memory/ptr_util.h"
 
@@ -26,7 +27,7 @@
   void SetUp() override {
     test::AshTestBase::SetUp();
 
-    WmShell::Get()->SetPaletteDelegateForTesting(
+    test::ShellTestApi().SetPaletteDelegate(
         base::MakeUnique<TestPaletteDelegate>());
 
     palette_tool_delegate_ = base::MakeUnique<MockPaletteToolDelegate>();
@@ -34,7 +35,7 @@
 
   TestPaletteDelegate* test_palette_delegate() {
     return static_cast<TestPaletteDelegate*>(
-        WmShell::Get()->palette_delegate());
+        Shell::GetInstance()->palette_delegate());
   }
 
  protected:
diff --git a/ash/common/system/chromeos/tray_caps_lock.cc b/ash/common/system/chromeos/tray_caps_lock.cc
index c4c115e..164085fb 100644
--- a/ash/common/system/chromeos/tray_caps_lock.cc
+++ b/ash/common/system/chromeos/tray_caps_lock.cc
@@ -14,6 +14,7 @@
 #include "ash/common/wm_shell.h"
 #include "ash/resources/grit/ash_resources.h"
 #include "ash/resources/vector_icons/vector_icons.h"
+#include "ash/shell.h"
 #include "ash/strings/grit/ash_strings.h"
 #include "base/sys_info.h"
 #include "ui/accessibility/ax_node_data.h"
@@ -158,7 +159,7 @@
   caps_lock_enabled_ = enabled;
 
   // Send an a11y alert.
-  WmShell::Get()->accessibility_delegate()->TriggerAccessibilityAlert(
+  Shell::GetInstance()->accessibility_delegate()->TriggerAccessibilityAlert(
       enabled ? A11Y_ALERT_CAPS_ON : A11Y_ALERT_CAPS_OFF);
 
   if (tray_view())
diff --git a/ash/common/system/ime/tray_ime_chromeos.cc b/ash/common/system/ime/tray_ime_chromeos.cc
index 8b2dca5..484ad9c 100644
--- a/ash/common/system/ime/tray_ime_chromeos.cc
+++ b/ash/common/system/ime/tray_ime_chromeos.cc
@@ -25,6 +25,7 @@
 #include "ash/common/wm_shell.h"
 #include "ash/resources/grit/ash_resources.h"
 #include "ash/resources/vector_icons/vector_icons.h"
+#include "ash/shell.h"
 #include "ash/strings/grit/ash_strings.h"
 #include "base/logging.h"
 #include "base/strings/utf_string_conversions.h"
@@ -274,8 +275,9 @@
 }
 
 bool TrayIME::ShouldShowKeyboardToggle() {
-  return keyboard_suppressed_ &&
-         !WmShell::Get()->accessibility_delegate()->IsVirtualKeyboardEnabled();
+  return keyboard_suppressed_ && !Shell::GetInstance()
+                                      ->accessibility_delegate()
+                                      ->IsVirtualKeyboardEnabled();
 }
 
 base::string16 TrayIME::GetDefaultViewLabel(bool show_ime_label) {
diff --git a/ash/common/system/ime/tray_ime_chromeos_unittest.cc b/ash/common/system/ime/tray_ime_chromeos_unittest.cc
index fb6e8bc0..7db95ae1 100644
--- a/ash/common/system/ime/tray_ime_chromeos_unittest.cc
+++ b/ash/common/system/ime/tray_ime_chromeos_unittest.cc
@@ -9,6 +9,7 @@
 #include "ash/common/system/chromeos/ime_menu/ime_list_view.h"
 #include "ash/common/system/tray/system_tray_notifier.h"
 #include "ash/common/wm_shell.h"
+#include "ash/shell.h"
 #include "ash/test/ash_test_base.h"
 #include "base/strings/utf_string_conversions.h"
 #include "ui/events/devices/device_data_manager.h"
@@ -59,7 +60,8 @@
 };
 
 void TrayIMETest::SetAccessibilityKeyboardEnabled(bool enabled) {
-  WmShell::Get()->accessibility_delegate()->SetVirtualKeyboardEnabled(enabled);
+  Shell::GetInstance()->accessibility_delegate()->SetVirtualKeyboardEnabled(
+      enabled);
   keyboard::SetAccessibilityKeyboardEnabled(enabled);
   AccessibilityNotificationVisibility notification =
       enabled ? A11Y_NOTIFICATION_SHOW : A11Y_NOTIFICATION_NONE;
diff --git a/ash/common/system/tray/system_tray.cc b/ash/common/system/tray/system_tray.cc
index 306c94c..8c2d237b 100644
--- a/ash/common/system/tray/system_tray.cc
+++ b/ash/common/system/tray/system_tray.cc
@@ -136,7 +136,9 @@
 
     // If ChromeVox is enabled, focus the default item if no item is focused and
     // there isn't a delayed close.
-    if (WmShell::Get()->accessibility_delegate()->IsSpokenFeedbackEnabled() &&
+    if (Shell::GetInstance()
+            ->accessibility_delegate()
+            ->IsSpokenFeedbackEnabled() &&
         !is_persistent) {
       bubble_->FocusDefaultIfNeeded();
     }
@@ -485,7 +487,9 @@
   if (system_bubble_.get() && creation_type == BUBBLE_USE_EXISTING) {
     system_bubble_->bubble()->UpdateView(items, bubble_type);
     // If ChromeVox is enabled, focus the default item if no item is focused.
-    if (WmShell::Get()->accessibility_delegate()->IsSpokenFeedbackEnabled())
+    if (Shell::GetInstance()
+            ->accessibility_delegate()
+            ->IsSpokenFeedbackEnabled())
       system_bubble_->bubble()->FocusDefaultIfNeeded();
   } else {
     // Cleanup the existing bubble before showing a new one. Otherwise, it's
diff --git a/ash/common/system/tray/system_tray_unittest.cc b/ash/common/system/tray/system_tray_unittest.cc
index 4b3123c..ba66ce65 100644
--- a/ash/common/system/tray/system_tray_unittest.cc
+++ b/ash/common/system/tray/system_tray_unittest.cc
@@ -20,6 +20,7 @@
 #include "ash/common/wm_window.h"
 #include "ash/public/cpp/shell_window_ids.h"
 #include "ash/root_window_controller.h"
+#include "ash/shell.h"
 #include "ash/test/ash_test_base.h"
 #include "ash/test/status_area_widget_test_helper.h"
 #include "ash/test/test_system_tray_item.h"
@@ -484,7 +485,8 @@
 
 TEST_F(SystemTrayTest, WithSystemModal) {
   // Check if the accessibility item is created even with system modal dialog.
-  WmShell::Get()->accessibility_delegate()->SetVirtualKeyboardEnabled(true);
+  Shell::GetInstance()->accessibility_delegate()->SetVirtualKeyboardEnabled(
+      true);
   std::unique_ptr<views::Widget> widget(CreateTestWidget(
       new ModalWidgetDelegate, kShellWindowId_SystemModalContainer,
       gfx::Rect(0, 0, 100, 100)));
diff --git a/ash/common/system/tray_accessibility.cc b/ash/common/system/tray_accessibility.cc
index ec77b71a..16b9d2e3 100644
--- a/ash/common/system/tray_accessibility.cc
+++ b/ash/common/system/tray_accessibility.cc
@@ -21,6 +21,7 @@
 #include "ash/common/wm_shell.h"
 #include "ash/resources/grit/ash_resources.h"
 #include "ash/resources/vector_icons/vector_icons.h"
+#include "ash/shell.h"
 #include "ash/strings/grit/ash_strings.h"
 #include "base/strings/utf_string_conversions.h"
 #include "ui/base/l10n/l10n_util.h"
@@ -49,7 +50,8 @@
 };
 
 uint32_t GetAccessibilityState() {
-  AccessibilityDelegate* delegate = WmShell::Get()->accessibility_delegate();
+  AccessibilityDelegate* delegate =
+      Shell::GetInstance()->accessibility_delegate();
   uint32_t state = A11Y_NONE;
   if (delegate->IsSpokenFeedbackEnabled())
     state |= A11Y_SPOKEN_FEEDBACK;
@@ -162,7 +164,8 @@
   CreateScrollableList();
   ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance();
 
-  AccessibilityDelegate* delegate = WmShell::Get()->accessibility_delegate();
+  AccessibilityDelegate* delegate =
+      Shell::GetInstance()->accessibility_delegate();
   spoken_feedback_enabled_ = delegate->IsSpokenFeedbackEnabled();
   spoken_feedback_view_ =
       AddScrollListItem(bundle.GetLocalizedString(
@@ -239,7 +242,8 @@
 }
 
 void AccessibilityDetailedView::HandleViewClicked(views::View* view) {
-  AccessibilityDelegate* delegate = WmShell::Get()->accessibility_delegate();
+  AccessibilityDelegate* delegate =
+      Shell::GetInstance()->accessibility_delegate();
   UserMetricsAction user_action;
   if (view == spoken_feedback_view_) {
     user_action = delegate->IsSpokenFeedbackEnabled()
@@ -361,7 +365,8 @@
   // - "Enable accessibility menu" on chrome://settings is checked;
   // - or any of accessibility features is enabled
   // Otherwise, not shows it.
-  AccessibilityDelegate* delegate = WmShell::Get()->accessibility_delegate();
+  AccessibilityDelegate* delegate =
+      Shell::GetInstance()->accessibility_delegate();
   if (login_ != LoginStatus::NOT_LOGGED_IN &&
       !delegate->ShouldShowAccessibilityMenu() &&
       // On login screen, keeps the initial visibility of the menu.
diff --git a/ash/common/wallpaper/wallpaper_controller_unittest.cc b/ash/common/wallpaper/wallpaper_controller_unittest.cc
index d7aac9f..007cb89 100644
--- a/ash/common/wallpaper/wallpaper_controller_unittest.cc
+++ b/ash/common/wallpaper/wallpaper_controller_unittest.cc
@@ -13,6 +13,7 @@
 #include "ash/common/wm_window.h"
 #include "ash/public/cpp/shell_window_ids.h"
 #include "ash/root_window_controller.h"
+#include "ash/shell.h"
 #include "ash/test/ash_test_base.h"
 #include "ash/test/test_wallpaper_delegate.h"
 #include "base/message_loop/message_loop.h"
@@ -115,7 +116,7 @@
         WmShell::Get()->GetPrimaryRootWindow()->GetRootWindowController();
     root_window_controller->SetWallpaperWidgetController(nullptr);
     root_window_controller->SetAnimatingWallpaperWidgetController(nullptr);
-    controller_ = WmShell::Get()->wallpaper_controller();
+    controller_ = Shell::GetInstance()->wallpaper_controller();
     wallpaper_delegate_ = static_cast<test::TestWallpaperDelegate*>(
         WmShell::Get()->wallpaper_delegate());
     controller_->set_wallpaper_reload_delay_for_test(0);
@@ -200,7 +201,8 @@
 };
 
 TEST_F(WallpaperControllerTest, BasicReparenting) {
-  WallpaperController* controller = WmShell::Get()->wallpaper_controller();
+  WallpaperController* controller =
+      Shell::GetInstance()->wallpaper_controller();
   controller->CreateEmptyWallpaper();
 
   // Wallpaper view/window exists in the wallpaper container and nothing is in
@@ -232,7 +234,8 @@
       ui::ScopedAnimationDurationScaleMode::NON_ZERO_DURATION);
 
   // Create the wallpaper and its view.
-  WallpaperController* controller = WmShell::Get()->wallpaper_controller();
+  WallpaperController* controller =
+      Shell::GetInstance()->wallpaper_controller();
   controller->CreateEmptyWallpaper();
 
   // The new wallpaper is ready to animate.
@@ -257,7 +260,8 @@
       ui::ScopedAnimationDurationScaleMode::NON_ZERO_DURATION);
 
   // Reset wallpaper state, see ControllerOwnership above.
-  WallpaperController* controller = WmShell::Get()->wallpaper_controller();
+  WallpaperController* controller =
+      Shell::GetInstance()->wallpaper_controller();
   controller->CreateEmptyWallpaper();
 
   // Run wallpaper show animation to completion.
@@ -304,7 +308,8 @@
       ui::ScopedAnimationDurationScaleMode::NON_ZERO_DURATION);
 
   // Reset wallpaper state, see ControllerOwnership above.
-  WallpaperController* controller = WmShell::Get()->wallpaper_controller();
+  WallpaperController* controller =
+      Shell::GetInstance()->wallpaper_controller();
   controller->CreateEmptyWallpaper();
 
   // Run wallpaper show animation to completion.
@@ -356,7 +361,7 @@
   // that the resized image is the expected size.
   controller_->SetWallpaperImage(image, WALLPAPER_LAYOUT_STRETCH);
   EXPECT_TRUE(image.BackedBySameObjectAs(controller_->GetWallpaper()));
-  RunAllBlockingPoolTasksUntilIdle(WmShell::Get()->blocking_pool().get());
+  RunAllBlockingPoolTasksUntilIdle(Shell::GetInstance()->blocking_pool().get());
   gfx::ImageSkia resized_image = controller_->GetWallpaper();
   EXPECT_FALSE(image.BackedBySameObjectAs(resized_image));
   EXPECT_EQ(gfx::Size(320, 200).ToString(), resized_image.size().ToString());
@@ -365,7 +370,7 @@
   // previously-resized image instead of doing another resize
   // (http://crbug.com/321402).
   controller_->SetWallpaperImage(image, WALLPAPER_LAYOUT_STRETCH);
-  RunAllBlockingPoolTasksUntilIdle(WmShell::Get()->blocking_pool().get());
+  RunAllBlockingPoolTasksUntilIdle(Shell::GetInstance()->blocking_pool().get());
   EXPECT_TRUE(resized_image.BackedBySameObjectAs(controller_->GetWallpaper()));
 }
 
diff --git a/ash/common/wallpaper/wallpaper_view.cc b/ash/common/wallpaper/wallpaper_view.cc
index 2aed6b6..2233d57 100644
--- a/ash/common/wallpaper/wallpaper_view.cc
+++ b/ash/common/wallpaper/wallpaper_view.cc
@@ -12,6 +12,7 @@
 #include "ash/common/wm_shell.h"
 #include "ash/common/wm_window.h"
 #include "ash/root_window_controller.h"
+#include "ash/shell.h"
 #include "ui/display/display.h"
 #include "ui/display/manager/managed_display_info.h"
 #include "ui/display/screen.h"
@@ -125,7 +126,8 @@
   // to fill the wallpaper. Ideally the image should be larger than the largest
   // display supported, if not we will scale and center it if the layout is
   // wallpaper::WALLPAPER_LAYOUT_CENTER_CROPPED.
-  WallpaperController* controller = WmShell::Get()->wallpaper_controller();
+  WallpaperController* controller =
+      Shell::GetInstance()->wallpaper_controller();
   gfx::ImageSkia wallpaper = controller->GetWallpaper();
   wallpaper::WallpaperLayout layout = controller->GetWallpaperLayout();
 
@@ -191,7 +193,8 @@
 }
 
 views::Widget* CreateWallpaper(WmWindow* root_window, int container_id) {
-  WallpaperController* controller = WmShell::Get()->wallpaper_controller();
+  WallpaperController* controller =
+      Shell::GetInstance()->wallpaper_controller();
   WallpaperDelegate* wallpaper_delegate = WmShell::Get()->wallpaper_delegate();
 
   views::Widget* wallpaper_widget = new views::Widget;
diff --git a/ash/common/window_user_data.h b/ash/common/window_user_data.h
new file mode 100644
index 0000000..7ee3131
--- /dev/null
+++ b/ash/common/window_user_data.h
@@ -0,0 +1,80 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef ASH_COMMON_WINDOW_USER_DATA_H_
+#define ASH_COMMON_WINDOW_USER_DATA_H_
+
+#include <map>
+#include <memory>
+#include <utility>
+
+#include "base/macros.h"
+#include "ui/aura/window.h"
+#include "ui/aura/window_observer.h"
+
+namespace ash {
+
+// WindowUserData provides a way to associate an object with a Window and have
+// that object destroyed when the window is destroyed, or when WindowUserData
+// is destroyed (from aura::WindowObserver::OnWindowDestroying()).
+//
+// NOTE: WindowUserData does not make use of the Set/GetProperty API offered
+// on aura::Window. This is done to avoid collisions in the case of multiple
+// WindowUserDatas operating on the same Window.
+template <typename UserData>
+class WindowUserData : public aura::WindowObserver {
+ public:
+  WindowUserData() {}
+
+  ~WindowUserData() override { clear(); }
+
+  void clear() {
+    for (auto& pair : window_to_data_)
+      pair.first->RemoveObserver(this);
+    window_to_data_.clear();
+  }
+
+  // Sets the data associated with window. This destroys any existing data.
+  // |data| may be null.
+  void Set(aura::Window* window, std::unique_ptr<UserData> data) {
+    if (!data) {
+      if (window_to_data_.erase(window))
+        window->RemoveObserver(this);
+      return;
+    }
+    if (window_to_data_.count(window) == 0u)
+      window->AddObserver(this);
+    window_to_data_[window] = std::move(data);
+  }
+
+  // Returns the data associated with the window, or null if none set. The
+  // returned object is owned by WindowUserData.
+  UserData* Get(aura::Window* window) {
+    auto it = window_to_data_.find(window);
+    return it == window_to_data_.end() ? nullptr : it->second.get();
+  }
+
+  // Returns the set of windows with data associated with them.
+  std::set<aura::Window*> GetWindows() {
+    std::set<aura::Window*> windows;
+    for (auto& pair : window_to_data_)
+      windows.insert(pair.first);
+    return windows;
+  }
+
+ private:
+  // aura::WindowObserver:
+  void OnWindowDestroying(aura::Window* window) override {
+    window->RemoveObserver(this);
+    window_to_data_.erase(window);
+  }
+
+  std::map<aura::Window*, std::unique_ptr<UserData>> window_to_data_;
+
+  DISALLOW_COPY_AND_ASSIGN(WindowUserData);
+};
+
+}  // namespace ash
+
+#endif  // ASH_COMMON_WINDOW_USER_DATA_H_
diff --git a/ash/common/window_user_data_unittest.cc b/ash/common/window_user_data_unittest.cc
new file mode 100644
index 0000000..527b796f
--- /dev/null
+++ b/ash/common/window_user_data_unittest.cc
@@ -0,0 +1,80 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ash/common/window_user_data.h"
+
+#include <memory>
+
+#include "ash/common/test/ash_test.h"
+#include "ash/common/window_user_data.h"
+#include "base/memory/ptr_util.h"
+#include "ui/aura/window.h"
+#include "ui/compositor/layer_type.h"
+
+namespace ash {
+namespace {
+
+// Class that sets a bool* to true from the destructor. Used to track
+// destruction.
+class Data {
+ public:
+  explicit Data(bool* delete_setter) : delete_setter_(delete_setter) {}
+  ~Data() { *delete_setter_ = true; }
+
+ private:
+  bool* delete_setter_;
+
+  DISALLOW_COPY_AND_ASSIGN(Data);
+};
+
+}  // namespace
+
+using WindowUserDataTest = AshTest;
+
+// Verifies clear() deletes the data associated with a window.
+TEST_F(WindowUserDataTest, ClearDestroys) {
+  WindowUserData<Data> user_data;
+  aura::Window window(nullptr, ui::wm::WINDOW_TYPE_UNKNOWN);
+  window.Init(ui::LAYER_NOT_DRAWN);
+  bool data_deleted = false;
+  user_data.Set(&window, base::MakeUnique<Data>(&data_deleted));
+  EXPECT_FALSE(data_deleted);
+  user_data.clear();
+  EXPECT_TRUE(data_deleted);
+}
+
+// Verifies Set() called with an existing window replaces the existing data.
+TEST_F(WindowUserDataTest, ReplaceDestroys) {
+  WindowUserData<Data> user_data;
+  std::unique_ptr<aura::Window> window(
+      base::MakeUnique<aura::Window>(nullptr, ui::wm::WINDOW_TYPE_UNKNOWN));
+  window->Init(ui::LAYER_NOT_DRAWN);
+  bool data1_deleted = false;
+  user_data.Set(window.get(), base::MakeUnique<Data>(&data1_deleted));
+  EXPECT_FALSE(data1_deleted);
+  bool data2_deleted = false;
+  user_data.Set(window.get(), base::MakeUnique<Data>(&data2_deleted));
+  EXPECT_TRUE(data1_deleted);
+  EXPECT_FALSE(data2_deleted);
+  ASSERT_EQ(1u, user_data.GetWindows().size());
+  EXPECT_EQ(window.get(), *user_data.GetWindows().begin());
+  window.reset();
+  EXPECT_TRUE(data2_deleted);
+  EXPECT_TRUE(user_data.GetWindows().empty());
+}
+
+// Verifies Set() with null deletes existing data.
+TEST_F(WindowUserDataTest, NullClears) {
+  WindowUserData<Data> user_data;
+  aura::Window window(nullptr, ui::wm::WINDOW_TYPE_UNKNOWN);
+  window.Init(ui::LAYER_NOT_DRAWN);
+  bool data1_deleted = false;
+  user_data.Set(&window, base::MakeUnique<Data>(&data1_deleted));
+  EXPECT_FALSE(data1_deleted);
+  user_data.Set(&window, nullptr);
+  EXPECT_TRUE(data1_deleted);
+  EXPECT_TRUE(user_data.GetWindows().empty());
+}
+
+}  // namespace ash
diff --git a/ash/common/wm/container_finder.cc b/ash/common/wm/container_finder.cc
index bd89b23..95207ec3 100644
--- a/ash/common/wm/container_finder.cc
+++ b/ash/common/wm/container_finder.cc
@@ -14,6 +14,7 @@
 #include "ash/public/cpp/window_properties.h"
 #include "ash/root_window_controller.h"
 #include "ash/shell.h"
+#include "ui/aura/window.h"
 #include "ui/gfx/geometry/rect.h"
 
 namespace ash {
@@ -110,12 +111,12 @@
   return nullptr;
 }
 
-std::vector<WmWindow*> GetContainersFromAllRootWindows(
+aura::Window::Windows GetContainersFromAllRootWindows(
     int container_id,
-    WmWindow* priority_root) {
-  std::vector<WmWindow*> containers;
-  for (WmWindow* root : WmShell::Get()->GetAllRootWindows()) {
-    WmWindow* container = root->GetChildByShellWindowId(container_id);
+    aura::Window* priority_root) {
+  aura::Window::Windows containers;
+  for (aura::Window* root : Shell::GetAllRootWindows()) {
+    aura::Window* container = root->GetChildById(container_id);
     if (!container)
       continue;
 
diff --git a/ash/common/wm/container_finder.h b/ash/common/wm/container_finder.h
index a54e57a..25570b5 100644
--- a/ash/common/wm/container_finder.h
+++ b/ash/common/wm/container_finder.h
@@ -9,6 +9,10 @@
 
 #include "ash/ash_export.h"
 
+namespace aura {
+class Window;
+}
+
 namespace gfx {
 class Rect;
 }
@@ -32,9 +36,9 @@
 // Returns the list of containers that match |container_id| in all root windows.
 // If |priority_root| is non-null, the container in |priority_root| is placed at
 // the front of the list.
-ASH_EXPORT std::vector<WmWindow*> GetContainersFromAllRootWindows(
+ASH_EXPORT std::vector<aura::Window*> GetContainersFromAllRootWindows(
     int container_id,
-    WmWindow* priority_root = nullptr);
+    aura::Window* priority_root = nullptr);
 
 }  // namespace wm
 }  // namespace ash
diff --git a/ash/common/wm/dock/docked_window_layout_manager.cc b/ash/common/wm/dock/docked_window_layout_manager.cc
index ef6478b..c8920b40 100644
--- a/ash/common/wm/dock/docked_window_layout_manager.cc
+++ b/ash/common/wm/dock/docked_window_layout_manager.cc
@@ -59,7 +59,7 @@
         alignment_(DOCKED_ALIGNMENT_NONE),
         background_animator_(SHELF_BACKGROUND_DEFAULT,
                              nullptr,
-                             WmShell::Get()->wallpaper_controller()),
+                             Shell::GetInstance()->wallpaper_controller()),
         opaque_background_(ui::LAYER_SOLID_COLOR),
         visible_background_type_(manager_->shelf()->GetBackgroundType()),
         visible_background_change_type_(AnimationChangeType::IMMEDIATE) {
diff --git a/ash/common/wm/overview/window_selector.cc b/ash/common/wm/overview/window_selector.cc
index 4a983a83..caca63f 100644
--- a/ash/common/wm/overview/window_selector.cc
+++ b/ash/common/wm/overview/window_selector.cc
@@ -310,7 +310,7 @@
   display::Screen::GetScreen()->AddObserver(this);
   shell->RecordUserMetricsAction(UMA_WINDOW_OVERVIEW);
   // Send an a11y alert.
-  WmShell::Get()->accessibility_delegate()->TriggerAccessibilityAlert(
+  Shell::GetInstance()->accessibility_delegate()->TriggerAccessibilityAlert(
       A11Y_ALERT_WINDOW_OVERVIEW_MODE_ENTERED);
 
   UpdateShelfVisibility();
diff --git a/ash/common/wm/screen_dimmer.cc b/ash/common/wm/screen_dimmer.cc
index 8304d118..5986b5d 100644
--- a/ash/common/wm/screen_dimmer.cc
+++ b/ash/common/wm/screen_dimmer.cc
@@ -4,14 +4,14 @@
 
 #include "ash/common/wm/screen_dimmer.h"
 
+#include "ash/common/window_user_data.h"
 #include "ash/common/wm/container_finder.h"
 #include "ash/common/wm/window_dimmer.h"
-#include "ash/common/wm_shell.h"
 #include "ash/common/wm_window.h"
-#include "ash/common/wm_window_user_data.h"
 #include "ash/public/cpp/shell_window_ids.h"
 #include "ash/shell.h"
 #include "base/memory/ptr_util.h"
+#include "ui/aura/window.h"
 
 namespace ash {
 namespace {
@@ -28,7 +28,7 @@
     : container_(container),
       is_dimming_(false),
       at_bottom_(false),
-      window_dimmers_(base::MakeUnique<WmWindowUserData<WindowDimmer>>()) {
+      window_dimmers_(base::MakeUnique<WindowUserData<WindowDimmer>>()) {
   Shell::GetInstance()->AddShellObserver(this);
 }
 
@@ -46,9 +46,9 @@
   Update(should_dim);
 }
 
-std::vector<WmWindow*> ScreenDimmer::GetAllContainers() {
+aura::Window::Windows ScreenDimmer::GetAllContainers() {
   return container_ == Container::ROOT
-             ? WmShell::Get()->GetAllRootWindows()
+             ? Shell::GetAllRootWindows()
              : wm::GetContainersFromAllRootWindows(
                    ash::kShellWindowId_LockScreenContainersContainer);
 }
@@ -58,7 +58,7 @@
 }
 
 void ScreenDimmer::Update(bool should_dim) {
-  for (WmWindow* container : GetAllContainers()) {
+  for (aura::Window* container : GetAllContainers()) {
     WindowDimmer* window_dimmer = window_dimmers_->Get(container);
     if (should_dim) {
       if (!window_dimmer) {
diff --git a/ash/common/wm/screen_dimmer.h b/ash/common/wm/screen_dimmer.h
index 1667b05..0a826552 100644
--- a/ash/common/wm/screen_dimmer.h
+++ b/ash/common/wm/screen_dimmer.h
@@ -12,15 +12,20 @@
 #include "ash/common/shell_observer.h"
 #include "base/macros.h"
 
-namespace ash {
-namespace test {
-class ScreenDimmerTest;
+namespace aura {
+class Window;
 }
 
+namespace ash {
+
 class WindowDimmer;
 
 template <typename UserData>
-class WmWindowUserData;
+class WindowUserData;
+
+namespace test {
+class ScreenDimmerTest;
+}
 
 // ScreenDimmer displays a partially-opaque layer above everything
 // else in the given container window to darken the display.  It shouldn't be
@@ -53,8 +58,9 @@
  private:
   friend class test::ScreenDimmerTest;
 
-  // Returns the WmWindows (one per display) that correspond to |container_|.
-  std::vector<WmWindow*> GetAllContainers();
+  // Returns the aura::Windows (one per display) that correspond to
+  // |container_|.
+  std::vector<aura::Window*> GetAllContainers();
 
   // ShellObserver:
   void OnRootWindowAdded(WmWindow* root_window) override;
@@ -70,7 +76,7 @@
   bool at_bottom_;
 
   // Owns the WindowDimmers.
-  std::unique_ptr<WmWindowUserData<WindowDimmer>> window_dimmers_;
+  std::unique_ptr<WindowUserData<WindowDimmer>> window_dimmers_;
 
   DISALLOW_COPY_AND_ASSIGN(ScreenDimmer);
 };
diff --git a/ash/common/wm/system_modal_container_layout_manager.cc b/ash/common/wm/system_modal_container_layout_manager.cc
index d6d8d040..2f21230 100644
--- a/ash/common/wm/system_modal_container_layout_manager.cc
+++ b/ash/common/wm/system_modal_container_layout_manager.cc
@@ -160,7 +160,7 @@
 
 void SystemModalContainerLayoutManager::CreateModalBackground() {
   if (!window_dimmer_) {
-    window_dimmer_ = base::MakeUnique<WindowDimmer>(container_);
+    window_dimmer_ = base::MakeUnique<WindowDimmer>(container_->aura_window());
     window_dimmer_->window()->SetName(
         "SystemModalContainerLayoutManager.ModalBackground");
     // There isn't always a keyboard controller.
@@ -189,7 +189,7 @@
       static_cast<SystemModalContainerLayoutManager*>(
           window->GetParent()->GetLayoutManager());
   return layout_manager->window_dimmer_ &&
-         layout_manager->window_dimmer_->window() == window;
+         WmWindow::Get(layout_manager->window_dimmer_->window()) == window;
 }
 
 ////////////////////////////////////////////////////////////////////////////////
diff --git a/ash/common/wm/window_dimmer.cc b/ash/common/wm/window_dimmer.cc
index d5db5815..3e74613 100644
--- a/ash/common/wm/window_dimmer.cc
+++ b/ash/common/wm/window_dimmer.cc
@@ -6,11 +6,10 @@
 
 #include <memory>
 
-#include "ash/common/wm_shell.h"
-#include "ash/common/wm_window.h"
 #include "base/time/time.h"
 #include "ui/aura/window.h"
 #include "ui/compositor/layer.h"
+#include "ui/wm/core/visibility_controller.h"
 #include "ui/wm/core/window_animations.h"
 
 namespace ash {
@@ -22,64 +21,63 @@
 
 }  // namespace
 
-WindowDimmer::WindowDimmer(WmWindow* parent)
+WindowDimmer::WindowDimmer(aura::Window* parent)
     : parent_(parent),
-      window_(WmWindow::Get(
-          new aura::Window(nullptr, ui::wm::WINDOW_TYPE_NORMAL))) {
-  window_->aura_window()->Init(ui::LAYER_SOLID_COLOR);
-  window_->SetVisibilityChangesAnimated();
-  window_->SetVisibilityAnimationType(
-      ::wm::WINDOW_VISIBILITY_ANIMATION_TYPE_FADE);
-  window_->SetVisibilityAnimationDuration(
+      window_(new aura::Window(nullptr, ui::wm::WINDOW_TYPE_NORMAL)) {
+  window_->Init(ui::LAYER_SOLID_COLOR);
+  ::wm::SetWindowVisibilityChangesAnimated(window_);
+  ::wm::SetWindowVisibilityAnimationType(
+      window_, ::wm::WINDOW_VISIBILITY_ANIMATION_TYPE_FADE);
+  ::wm::SetWindowVisibilityAnimationDuration(
+      window_,
       base::TimeDelta::FromMilliseconds(kDefaultDimAnimationDurationMs));
-  window_->aura_window()->AddObserver(this);
+  window_->AddObserver(this);
 
   SetDimOpacity(kDefaultDimOpacity);
 
   parent->AddChild(window_);
-  parent->aura_window()->AddObserver(this);
+  parent->AddObserver(this);
   parent->StackChildAtTop(window_);
 
-  window_->SetBounds(gfx::Rect(parent_->GetBounds().size()));
+  window_->SetBounds(gfx::Rect(parent_->bounds().size()));
 }
 
 WindowDimmer::~WindowDimmer() {
   if (parent_)
-    parent_->aura_window()->RemoveObserver(this);
+    parent_->RemoveObserver(this);
   if (window_) {
-    window_->aura_window()->RemoveObserver(this);
-    window_->Destroy();
+    window_->RemoveObserver(this);
+    // See class description for details on ownership.
+    delete window_;
   }
 }
 
 void WindowDimmer::SetDimOpacity(float target_opacity) {
   DCHECK(window_);
-  window_->GetLayer()->SetColor(
-      SkColorSetA(SK_ColorBLACK, 255 * target_opacity));
+  window_->layer()->SetColor(SkColorSetA(SK_ColorBLACK, 255 * target_opacity));
 }
 
 void WindowDimmer::OnWindowBoundsChanged(aura::Window* window,
                                          const gfx::Rect& old_bounds,
                                          const gfx::Rect& new_bounds) {
-  if (WmWindow::Get(window) == parent_)
+  if (window == parent_)
     window_->SetBounds(gfx::Rect(new_bounds.size()));
 }
 
 void WindowDimmer::OnWindowDestroying(aura::Window* window) {
-  if (WmWindow::Get(window) == parent_) {
-    parent_->aura_window()->RemoveObserver(this);
+  if (window == parent_) {
+    parent_->RemoveObserver(this);
     parent_ = nullptr;
   } else {
-    DCHECK_EQ(window_, WmWindow::Get(window));
-    window_->aura_window()->RemoveObserver(this);
+    DCHECK_EQ(window_, window);
+    window_->RemoveObserver(this);
     window_ = nullptr;
   }
 }
 
 void WindowDimmer::OnWindowHierarchyChanging(
     const HierarchyChangeParams& params) {
-  if (WmWindow::Get(params.receiver) == window_ &&
-      params.target == params.receiver) {
+  if (params.receiver == window_ && params.target == params.receiver) {
     // This may happen on a display change or some unexpected condition. Hide
     // the window to ensure it isn't obscuring the wrong thing.
     window_->Hide();
diff --git a/ash/common/wm/window_dimmer.h b/ash/common/wm/window_dimmer.h
index 48ded97..9f121e1 100644
--- a/ash/common/wm/window_dimmer.h
+++ b/ash/common/wm/window_dimmer.h
@@ -11,28 +11,28 @@
 
 namespace ash {
 
-class WmWindow;
-
 // WindowDimmer creates a window whose opacity is animated by way of
 // SetDimOpacity() and whose size matches that of its parent. WindowDimmer is
 // intended to be used in cases where a certain set of windows need to appear
 // partially obscured. This is achieved by creating WindowDimmer, setting the
 // opacity, and then stacking window() above the windows that are to appear
-// obscured. The window created by WindowDimmer is owned by the parent, but also
-// deleted if WindowDimmer is deleted. It is expected that WindowDimmer is
-// deleted when the parent window is deleted (such as happens with
-// WmWindowUserData).
+// obscured.
+//
+// WindowDimmer owns the window it creates, but supports having that window
+// deleted out from under it (this generally happens if the parent of the
+// window is deleted). If WindowDimmer is deleted and the window it created is
+// still valid, then WindowDimmer deletes the window.
 class ASH_EXPORT WindowDimmer : public aura::WindowObserver {
  public:
   // Creates a new WindowDimmer. The window() created by WindowDimmer is added
   // to |parent| and stacked above all other child windows.
-  explicit WindowDimmer(WmWindow* parent);
+  explicit WindowDimmer(aura::Window* parent);
   ~WindowDimmer() override;
 
   void SetDimOpacity(float target_opacity);
 
-  WmWindow* parent() { return parent_; }
-  WmWindow* window() { return window_; }
+  aura::Window* parent() { return parent_; }
+  aura::Window* window() { return window_; }
 
   // NOTE: WindowDimmer is an observer for both |parent_| and |window_|.
   // aura::WindowObserver:
@@ -43,8 +43,9 @@
   void OnWindowHierarchyChanging(const HierarchyChangeParams& params) override;
 
  private:
-  WmWindow* parent_;
-  WmWindow* window_;
+  aura::Window* parent_;
+  // See class description for details on ownership.
+  aura::Window* window_;
 
   DISALLOW_COPY_AND_ASSIGN(WindowDimmer);
 };
diff --git a/ash/common/wm_shell.cc b/ash/common/wm_shell.cc
index 11ada3ba..84215f33 100644
--- a/ash/common/wm_shell.cc
+++ b/ash/common/wm_shell.cc
@@ -7,16 +7,11 @@
 #include <utility>
 
 #include "ash/common/accelerators/accelerator_controller.h"
-#include "ash/common/accelerators/ash_focus_manager_factory.h"
-#include "ash/common/accessibility_delegate.h"
 #include "ash/common/cast_config_controller.h"
-#include "ash/common/devtools/ash_devtools_css_agent.h"
-#include "ash/common/devtools/ash_devtools_dom_agent.h"
 #include "ash/common/focus_cycler.h"
 #include "ash/common/keyboard/keyboard_ui.h"
 #include "ash/common/media_controller.h"
 #include "ash/common/new_window_controller.h"
-#include "ash/common/palette_delegate.h"
 #include "ash/common/session/session_controller.h"
 #include "ash/common/session/session_state_delegate.h"
 #include "ash/common/shelf/app_list_shelf_item_delegate.h"
@@ -33,11 +28,9 @@
 #include "ash/common/system/chromeos/session/logout_confirmation_controller.h"
 #include "ash/common/system/keyboard_brightness_control_delegate.h"
 #include "ash/common/system/locale/locale_notification_controller.h"
-#include "ash/common/system/toast/toast_manager.h"
 #include "ash/common/system/tray/system_tray_controller.h"
 #include "ash/common/system/tray/system_tray_delegate.h"
 #include "ash/common/system/tray/system_tray_notifier.h"
-#include "ash/common/wallpaper/wallpaper_controller.h"
 #include "ash/common/wallpaper/wallpaper_delegate.h"
 #include "ash/common/wm/immersive_context_ash.h"
 #include "ash/common/wm/maximize_mode/maximize_mode_controller.h"
@@ -53,13 +46,11 @@
 #include "base/bind.h"
 #include "base/logging.h"
 #include "base/memory/ptr_util.h"
-#include "base/threading/sequenced_worker_pool.h"
 #include "services/preferences/public/cpp/pref_client_store.h"
 #include "services/preferences/public/interfaces/preferences.mojom.h"
 #include "services/service_manager/public/cpp/connector.h"
 #include "ui/app_list/presenter/app_list.h"
 #include "ui/display/display.h"
-#include "ui/views/focus/focus_manager_factory.h"
 
 namespace ash {
 
@@ -80,43 +71,7 @@
   return instance_;
 }
 
-void WmShell::Initialize(const scoped_refptr<base::SequencedWorkerPool>& pool) {
-  blocking_pool_ = pool;
-
-  // Some delegates access WmShell during their construction. Create them here
-  // instead of the WmShell constructor.
-  accessibility_delegate_.reset(delegate_->CreateAccessibilityDelegate());
-  palette_delegate_ = delegate_->CreatePaletteDelegate();
-  toast_manager_.reset(new ToastManager);
-
-  // Create the app list item in the shelf data model.
-  AppListShelfItemDelegate::CreateAppListItemAndDelegate(shelf_model());
-
-  // Install the custom factory early on so that views::FocusManagers for Tray,
-  // Shelf, and WallPaper could be created by the factory.
-  views::FocusManagerFactory::Install(new AshFocusManagerFactory);
-
-  wallpaper_controller_.reset(new WallpaperController(blocking_pool_));
-
-  // Start devtools server
-  devtools_server_ = ui::devtools::UiDevToolsServer::Create(nullptr);
-  if (devtools_server_) {
-    auto dom_backend = base::MakeUnique<devtools::AshDevToolsDOMAgent>(this);
-    auto css_backend =
-        base::MakeUnique<devtools::AshDevToolsCSSAgent>(dom_backend.get());
-    auto devtools_client = base::MakeUnique<ui::devtools::UiDevToolsClient>(
-        "Ash", devtools_server_.get());
-    devtools_client->AddAgent(std::move(dom_backend));
-    devtools_client->AddAgent(std::move(css_backend));
-    devtools_server_->AttachClient(std::move(devtools_client));
-  }
-}
-
 void WmShell::Shutdown() {
-  // These members access WmShell in their destructors.
-  wallpaper_controller_.reset();
-  accessibility_delegate_.reset();
-
   // ShelfWindowWatcher has window observers and a pointer to the shelf model.
   shelf_window_watcher_.reset();
   // ShelfItemDelegate subclasses it owns have complex cleanup to run (e.g. ARC
@@ -125,9 +80,6 @@
   // Must be destroyed before FocusClient.
   shelf_delegate_.reset();
 
-  // Balances the Install() in Initialize().
-  views::FocusManagerFactory::Install(nullptr);
-
   // Removes itself as an observer of |pref_store_|.
   shelf_controller_.reset();
 }
@@ -197,11 +149,6 @@
   shelf_delegate_ = std::move(test_delegate);
 }
 
-void WmShell::SetPaletteDelegateForTesting(
-    std::unique_ptr<PaletteDelegate> palette_delegate) {
-  palette_delegate_ = std::move(palette_delegate);
-}
-
 WmShell::WmShell(std::unique_ptr<ShellDelegate> shell_delegate)
     : delegate_(std::move(shell_delegate)),
       app_list_(base::MakeUnique<app_list::AppList>()),
@@ -359,10 +306,6 @@
   mru_window_tracker_.reset();
 }
 
-void WmShell::DeleteToastManager() {
-  toast_manager_.reset();
-}
-
 void WmShell::SetAcceleratorController(
     std::unique_ptr<AcceleratorController> accelerator_controller) {
   accelerator_controller_ = std::move(accelerator_controller);
diff --git a/ash/common/wm_shell.h b/ash/common/wm_shell.h
index 42ac186..2de95ba 100644
--- a/ash/common/wm_shell.h
+++ b/ash/common/wm_shell.h
@@ -16,7 +16,6 @@
 #include "ash/common/session/session_state_observer.h"
 #include "ash/common/wm/lock_state_observer.h"
 #include "base/observer_list.h"
-#include "components/ui_devtools/devtools_server.h"
 #include "ui/base/ui_base_types.h"
 #include "ui/compositor/layer_type.h"
 #include "ui/wm/public/activation_change_observer.h"
@@ -26,10 +25,6 @@
 class AppList;
 }
 
-namespace base {
-class SequencedWorkerPool;
-}
-
 namespace display {
 class Display;
 class ManagedDisplayInfo;
@@ -51,7 +46,6 @@
 
 namespace ash {
 class AcceleratorController;
-class AccessibilityDelegate;
 class BrightnessControlDelegate;
 class CastConfigController;
 class FocusCycler;
@@ -66,7 +60,6 @@
 class MediaController;
 class MruWindowTracker;
 class NewWindowController;
-class PaletteDelegate;
 class RootWindowController;
 class ScopedDisableInternalMouseAndKeyboard;
 class SessionController;
@@ -81,9 +74,7 @@
 class SystemTrayDelegate;
 class SystemTrayController;
 class SystemTrayNotifier;
-class ToastManager;
 class VpnList;
-class WallpaperController;
 class WallpaperDelegate;
 class WindowCycleController;
 class WindowCycleEventFilter;
@@ -112,7 +103,6 @@
   static WmShell* Get();
   static bool HasInstance() { return instance_ != nullptr; }
 
-  virtual void Initialize(const scoped_refptr<base::SequencedWorkerPool>& pool);
   virtual void Shutdown();
 
   ShellDelegate* delegate() { return delegate_.get(); }
@@ -121,10 +111,6 @@
     return accelerator_controller_.get();
   }
 
-  AccessibilityDelegate* accessibility_delegate() {
-    return accessibility_delegate_.get();
-  }
-
   app_list::AppList* app_list() { return app_list_.get(); }
 
   BrightnessControlDelegate* brightness_control_delegate() {
@@ -161,8 +147,6 @@
     return new_window_controller_.get();
   }
 
-  PaletteDelegate* palette_delegate() { return palette_delegate_.get(); }
-
   preferences::PrefClientStore* pref_store() { return pref_store_.get(); }
 
   SessionController* session_controller() { return session_controller_.get(); }
@@ -189,14 +173,8 @@
     return system_tray_delegate_.get();
   }
 
-  ToastManager* toast_manager() { return toast_manager_.get(); }
-
   VpnList* vpn_list() { return vpn_list_.get(); }
 
-  WallpaperController* wallpaper_controller() {
-    return wallpaper_controller_.get();
-  }
-
   WallpaperDelegate* wallpaper_delegate() { return wallpaper_delegate_.get(); }
 
   WindowCycleController* window_cycle_controller() {
@@ -207,10 +185,6 @@
     return window_selector_controller_.get();
   }
 
-  const scoped_refptr<base::SequencedWorkerPool>& blocking_pool() {
-    return blocking_pool_;
-  }
-
   // Returns true when ash is running as a service_manager::Service.
   virtual bool IsRunningInMash() const = 0;
 
@@ -373,8 +347,6 @@
   void RemoveLockStateObserver(LockStateObserver* observer);
 
   void SetShelfDelegateForTesting(std::unique_ptr<ShelfDelegate> test_delegate);
-  void SetPaletteDelegateForTesting(
-      std::unique_ptr<PaletteDelegate> palette_delegate);
 
   // True if any touch points are down.
   virtual bool IsTouchDown() = 0;
@@ -415,8 +387,6 @@
   void CreateMruWindowTracker();
   void DeleteMruWindowTracker();
 
-  void DeleteToastManager();
-
   void SetAcceleratorController(
       std::unique_ptr<AcceleratorController> accelerator_controller);
 
@@ -435,7 +405,6 @@
   scoped_refptr<preferences::PrefClientStore> pref_store_;
 
   std::unique_ptr<AcceleratorController> accelerator_controller_;
-  std::unique_ptr<AccessibilityDelegate> accessibility_delegate_;
   std::unique_ptr<app_list::AppList> app_list_;
   std::unique_ptr<BrightnessControlDelegate> brightness_control_delegate_;
   std::unique_ptr<CastConfigController> cast_config_;
@@ -450,7 +419,6 @@
   std::unique_ptr<MediaController> media_controller_;
   std::unique_ptr<MruWindowTracker> mru_window_tracker_;
   std::unique_ptr<NewWindowController> new_window_controller_;
-  std::unique_ptr<PaletteDelegate> palette_delegate_;
   std::unique_ptr<SessionController> session_controller_;
   std::unique_ptr<ShelfController> shelf_controller_;
   std::unique_ptr<ShelfDelegate> shelf_delegate_;
@@ -459,19 +427,14 @@
   std::unique_ptr<SystemTrayController> system_tray_controller_;
   std::unique_ptr<SystemTrayNotifier> system_tray_notifier_;
   std::unique_ptr<SystemTrayDelegate> system_tray_delegate_;
-  std::unique_ptr<ToastManager> toast_manager_;
   std::unique_ptr<VpnList> vpn_list_;
-  std::unique_ptr<WallpaperController> wallpaper_controller_;
   std::unique_ptr<WallpaperDelegate> wallpaper_delegate_;
   std::unique_ptr<WindowCycleController> window_cycle_controller_;
   std::unique_ptr<WindowSelectorController> window_selector_controller_;
-  std::unique_ptr<ui::devtools::UiDevToolsServer> devtools_server_;
 
   base::ObserverList<LockStateObserver> lock_state_observers_;
 
   bool simulate_modal_window_open_for_testing_ = false;
-
-  scoped_refptr<base::SequencedWorkerPool> blocking_pool_;
 };
 
 }  // namespace ash
diff --git a/ash/common/wm_window_user_data.h b/ash/common/wm_window_user_data.h
deleted file mode 100644
index 2a6abd23..0000000
--- a/ash/common/wm_window_user_data.h
+++ /dev/null
@@ -1,78 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef ASH_COMMON_WM_WINDOW_USER_DATA_H_
-#define ASH_COMMON_WM_WINDOW_USER_DATA_H_
-
-#include <map>
-#include <memory>
-#include <utility>
-
-#include "ash/common/wm_window.h"
-#include "base/macros.h"
-#include "ui/aura/window.h"
-#include "ui/aura/window_observer.h"
-
-namespace ash {
-
-// WmWindowUserData provides a way to associate arbitrary objects with a
-// WmWindow. WmWindowUserData owns the data, deleting it either when
-// WmWindowUserData is deleted, or when the window the data is associated with
-// is destroyed (from aura::WindowObserver::OnWindowDestroying()).
-template <typename UserData>
-class WmWindowUserData : public aura::WindowObserver {
- public:
-  WmWindowUserData() {}
-
-  ~WmWindowUserData() override { clear(); }
-
-  void clear() {
-    for (auto& pair : window_to_data_)
-      pair.first->aura_window()->RemoveObserver(this);
-    window_to_data_.clear();
-  }
-
-  // Sets the data associated with window. This destroys any existing data.
-  // |data| may be null.
-  void Set(WmWindow* window, std::unique_ptr<UserData> data) {
-    if (!data) {
-      if (window_to_data_.erase(window))
-        window->aura_window()->RemoveObserver(this);
-      return;
-    }
-    if (window_to_data_.count(window) == 0u)
-      window->aura_window()->AddObserver(this);
-    window_to_data_[window] = std::move(data);
-  }
-
-  // Returns the data associated with the window, or null if none set. The
-  // returned object is owned by WmWindowUserData.
-  UserData* Get(WmWindow* window) {
-    auto it = window_to_data_.find(window);
-    return it == window_to_data_.end() ? nullptr : it->second.get();
-  }
-
-  // Returns the set of windows with data associated with them.
-  std::set<WmWindow*> GetWindows() {
-    std::set<WmWindow*> windows;
-    for (auto& pair : window_to_data_)
-      windows.insert(pair.first);
-    return windows;
-  }
-
- private:
-  // aura::WindowObserver:
-  void OnWindowDestroying(aura::Window* window) override {
-    window->RemoveObserver(this);
-    window_to_data_.erase(WmWindow::Get(window));
-  }
-
-  std::map<WmWindow*, std::unique_ptr<UserData>> window_to_data_;
-
-  DISALLOW_COPY_AND_ASSIGN(WmWindowUserData);
-};
-
-}  // namespace ash
-
-#endif  // ASH_COMMON_WM_WINDOW_USER_DATA_H_
diff --git a/ash/common/wm_window_user_data_unittest.cc b/ash/common/wm_window_user_data_unittest.cc
deleted file mode 100644
index 64fb42c..0000000
--- a/ash/common/wm_window_user_data_unittest.cc
+++ /dev/null
@@ -1,88 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "ash/common/wm_window_user_data.h"
-
-#include <memory>
-
-#include "ash/common/test/ash_test.h"
-#include "ash/common/wm_shell.h"
-#include "ash/common/wm_window.h"
-#include "ash/common/wm_window_user_data.h"
-#include "base/memory/ptr_util.h"
-#include "ui/aura/window.h"
-#include "ui/compositor/layer_type.h"
-
-namespace ash {
-namespace {
-
-// Class that sets a bool* to true from the destructor. Used to track
-// destruction.
-class Data {
- public:
-  explicit Data(bool* delete_setter) : delete_setter_(delete_setter) {}
-  ~Data() { *delete_setter_ = true; }
-
- private:
-  bool* delete_setter_;
-
-  DISALLOW_COPY_AND_ASSIGN(Data);
-};
-
-}  // namespace
-
-using WmWindowUserDataTest = AshTest;
-
-// Verifies clear() deletes the data associated with a window.
-TEST_F(WmWindowUserDataTest, ClearDestroys) {
-  WmWindowUserData<Data> user_data;
-  std::unique_ptr<aura::Window> window(
-      base::MakeUnique<aura::Window>(nullptr, ui::wm::WINDOW_TYPE_UNKNOWN));
-  window->Init(ui::LAYER_NOT_DRAWN);
-  bool data_deleted = false;
-  user_data.Set(WmWindow::Get(window.get()),
-                base::MakeUnique<Data>(&data_deleted));
-  EXPECT_FALSE(data_deleted);
-  user_data.clear();
-  EXPECT_TRUE(data_deleted);
-}
-
-// Verifies Set() called with an existing window replaces the existing data.
-TEST_F(WmWindowUserDataTest, ReplaceDestroys) {
-  WmWindowUserData<Data> user_data;
-  std::unique_ptr<aura::Window> window(
-      base::MakeUnique<aura::Window>(nullptr, ui::wm::WINDOW_TYPE_UNKNOWN));
-  window->Init(ui::LAYER_NOT_DRAWN);
-  bool data1_deleted = false;
-  user_data.Set(WmWindow::Get(window.get()),
-                base::MakeUnique<Data>(&data1_deleted));
-  EXPECT_FALSE(data1_deleted);
-  bool data2_deleted = false;
-  user_data.Set(WmWindow::Get(window.get()),
-                base::MakeUnique<Data>(&data2_deleted));
-  EXPECT_TRUE(data1_deleted);
-  EXPECT_FALSE(data2_deleted);
-  ASSERT_EQ(1u, user_data.GetWindows().size());
-  EXPECT_EQ(WmWindow::Get(window.get()), *user_data.GetWindows().begin());
-  window.reset();
-  EXPECT_TRUE(data2_deleted);
-  EXPECT_TRUE(user_data.GetWindows().empty());
-}
-
-// Verifies Set() with null deletes existing data.
-TEST_F(WmWindowUserDataTest, NullClears) {
-  WmWindowUserData<Data> user_data;
-  std::unique_ptr<aura::Window> window(
-      base::MakeUnique<aura::Window>(nullptr, ui::wm::WINDOW_TYPE_UNKNOWN));
-  window->Init(ui::LAYER_NOT_DRAWN);
-  bool data1_deleted = false;
-  user_data.Set(WmWindow::Get(window.get()),
-                base::MakeUnique<Data>(&data1_deleted));
-  EXPECT_FALSE(data1_deleted);
-  user_data.Set(WmWindow::Get(window.get()), nullptr);
-  EXPECT_TRUE(data1_deleted);
-  EXPECT_TRUE(user_data.GetWindows().empty());
-}
-
-}  // namespace ash
diff --git a/ash/default_wallpaper_delegate.cc b/ash/default_wallpaper_delegate.cc
index 1e78a74..48c28b48 100644
--- a/ash/default_wallpaper_delegate.cc
+++ b/ash/default_wallpaper_delegate.cc
@@ -5,7 +5,7 @@
 #include "ash/default_wallpaper_delegate.h"
 
 #include "ash/common/wallpaper/wallpaper_controller.h"
-#include "ash/common/wm_shell.h"
+#include "ash/shell.h"
 #include "ui/wm/core/window_animations.h"
 
 namespace ash {
@@ -28,7 +28,7 @@
 void DefaultWallpaperDelegate::UpdateWallpaper(bool clear_cache) {}
 
 void DefaultWallpaperDelegate::InitializeWallpaper() {
-  WmShell::Get()->wallpaper_controller()->CreateEmptyWallpaper();
+  Shell::GetInstance()->wallpaper_controller()->CreateEmptyWallpaper();
 }
 
 bool DefaultWallpaperDelegate::CanOpenSetWallpaperPage() {
diff --git a/ash/magnifier/magnification_controller.cc b/ash/magnifier/magnification_controller.cc
index 78c2cabd..a26f7d2a 100644
--- a/ash/magnifier/magnification_controller.cc
+++ b/ash/magnifier/magnification_controller.cc
@@ -10,7 +10,6 @@
 #include "ash/common/accelerators/accelerator_controller.h"
 #include "ash/common/accessibility_delegate.h"
 #include "ash/common/system/tray/system_tray_delegate.h"
-#include "ash/common/wm_shell.h"
 #include "ash/display/root_window_transformers.h"
 #include "ash/host/ash_window_tree_host.h"
 #include "ash/host/root_window_transformer.h"
@@ -566,7 +565,8 @@
     return;
 
   ValidateScale(&scale);
-  WmShell::Get()->accessibility_delegate()->SaveScreenMagnifierScale(scale);
+  Shell::GetInstance()->accessibility_delegate()->SaveScreenMagnifierScale(
+      scale);
   RedrawKeepingMousePosition(scale, animate);
 }
 
@@ -597,9 +597,9 @@
     if (!is_enabled_ && input_method)
       input_method->AddObserver(this);
 
-    WmShell* wm_shell = WmShell::Get();
+    Shell* shell = Shell::GetInstance();
     float scale =
-        wm_shell->accessibility_delegate()->GetSavedScreenMagnifierScale();
+        shell->accessibility_delegate()->GetSavedScreenMagnifierScale();
     if (scale <= 0.0f)
       scale = kInitialMagnifiedScale;
     ValidateScale(&scale);
@@ -610,7 +610,7 @@
 
     is_enabled_ = enabled;
     RedrawKeepingMousePosition(scale, true);
-    wm_shell->accessibility_delegate()->SaveScreenMagnifierScale(scale);
+    shell->accessibility_delegate()->SaveScreenMagnifierScale(scale);
   } else {
     // Do nothing, if already disabled.
     if (!is_enabled_)
diff --git a/ash/mus/context_menu_mus.cc b/ash/mus/context_menu_mus.cc
index c3fe04f..802713086 100644
--- a/ash/mus/context_menu_mus.cc
+++ b/ash/mus/context_menu_mus.cc
@@ -9,6 +9,7 @@
 #include "ash/common/wallpaper/wallpaper_delegate.h"
 #include "ash/common/wm_shell.h"
 #include "ash/public/cpp/shelf_types.h"
+#include "ash/shell.h"
 #include "ash/strings/grit/ash_strings.h"
 
 namespace ash {
@@ -46,7 +47,7 @@
                                        ? SHELF_AUTO_HIDE_BEHAVIOR_NEVER
                                        : SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS);
   } else if (command_id == MENU_CHANGE_WALLPAPER) {
-    WmShell::Get()->wallpaper_controller()->OpenSetWallpaperPage();
+    Shell::GetInstance()->wallpaper_controller()->OpenSetWallpaperPage();
   }
 }
 
diff --git a/ash/shell.cc b/ash/shell.cc
index 7926e0c..2679f06 100644
--- a/ash/shell.cc
+++ b/ash/shell.cc
@@ -16,11 +16,16 @@
 #include "ash/aura/wm_shell_aura.h"
 #include "ash/autoclick/autoclick_controller.h"
 #include "ash/common/accelerators/accelerator_controller.h"
+#include "ash/common/accelerators/ash_focus_manager_factory.h"
+#include "ash/common/accessibility_delegate.h"
 #include "ash/common/ash_constants.h"
+#include "ash/common/devtools/ash_devtools_css_agent.h"
+#include "ash/common/devtools/ash_devtools_dom_agent.h"
 #include "ash/common/frame/custom_frame_view_ash.h"
 #include "ash/common/gpu_support.h"
 #include "ash/common/keyboard/keyboard_ui.h"
 #include "ash/common/login_status.h"
+#include "ash/common/palette_delegate.h"
 #include "ash/common/session/session_state_delegate.h"
 #include "ash/common/shelf/app_list_shelf_item_delegate.h"
 #include "ash/common/shelf/shelf_delegate.h"
@@ -33,7 +38,9 @@
 #include "ash/common/system/chromeos/network/sms_observer.h"
 #include "ash/common/system/chromeos/power/power_status.h"
 #include "ash/common/system/status_area_widget.h"
+#include "ash/common/system/toast/toast_manager.h"
 #include "ash/common/system/tray/system_tray_delegate.h"
+#include "ash/common/wallpaper/wallpaper_controller.h"
 #include "ash/common/wallpaper/wallpaper_delegate.h"
 #include "ash/common/wm/container_finder.h"
 #include "ash/common/wm/maximize_mode/maximize_mode_controller.h"
@@ -103,6 +110,7 @@
 #include "chromeos/chromeos_switches.h"
 #include "chromeos/dbus/dbus_thread_manager.h"
 #include "chromeos/system/devicemode.h"
+#include "components/ui_devtools/devtools_server.h"
 #include "services/service_manager/public/cpp/connector.h"
 #include "services/ui/public/interfaces/constants.mojom.h"
 #include "ui/aura/client/aura_constants.h"
@@ -129,6 +137,7 @@
 #include "ui/keyboard/keyboard_util.h"
 #include "ui/views/corewm/tooltip_aura.h"
 #include "ui/views/corewm/tooltip_controller.h"
+#include "ui/views/focus/focus_manager_factory.h"
 #include "ui/views/widget/native_widget_aura.h"
 #include "ui/views/widget/widget.h"
 #include "ui/wm/core/accelerator_filter.h"
@@ -306,11 +315,10 @@
 #ifndef NDEBUG
   // Make sure that there is no system modal in Lock layer when unlocked.
   if (!locked) {
-    std::vector<WmWindow*> containers = wm::GetContainersFromAllRootWindows(
-        kShellWindowId_LockSystemModalContainer,
-        WmWindow::Get(GetPrimaryRootWindow()));
-    for (WmWindow* container : containers)
-      DCHECK(container->GetChildren().empty());
+    aura::Window::Windows containers = wm::GetContainersFromAllRootWindows(
+        kShellWindowId_LockSystemModalContainer, GetPrimaryRootWindow());
+    for (aura::Window* container : containers)
+      DCHECK(container->children().empty());
   }
 #endif
 }
@@ -550,8 +558,7 @@
   // layout.
   DeactivateKeyboard();
 
-  // Destroy toasts
-  wm_shell_->DeleteToastManager();
+  toast_manager_.reset();
 
   // Destroy SystemTrayDelegate before destroying the status area(s). Make sure
   // to deinitialize the shelf first, as it is initialized after the delegate.
@@ -620,6 +627,13 @@
   // AppListDelegateImpl depends upon AppList.
   app_list_delegate_impl_.reset();
 
+  // These members access Shell in their destructors.
+  wallpaper_controller_.reset();
+  accessibility_delegate_.reset();
+
+  // Balances the Install() in Initialize().
+  views::FocusManagerFactory::Install(nullptr);
+
   wm_shell_->Shutdown();
   // Depends on |focus_controller_|, so must be destroyed before.
   window_tree_host_manager_.reset();
@@ -656,7 +670,37 @@
 void Shell::Init(const ShellInitParams& init_params) {
   const bool is_mash = wm_shell_->IsRunningInMash();
 
-  wm_shell_->Initialize(init_params.blocking_pool);
+  blocking_pool_ = init_params.blocking_pool;
+
+  // Some delegates access WmShell during their construction. Create them here
+  // instead of the WmShell constructor.
+  accessibility_delegate_.reset(
+      wm_shell_->delegate()->CreateAccessibilityDelegate());
+  palette_delegate_ = wm_shell_->delegate()->CreatePaletteDelegate();
+  toast_manager_ = base::MakeUnique<ToastManager>();
+
+  // Create the app list item in the shelf data model.
+  AppListShelfItemDelegate::CreateAppListItemAndDelegate(
+      wm_shell_->shelf_model());
+
+  // Install the custom factory early on so that views::FocusManagers for Tray,
+  // Shelf, and WallPaper could be created by the factory.
+  views::FocusManagerFactory::Install(new AshFocusManagerFactory);
+
+  wallpaper_controller_ = base::MakeUnique<WallpaperController>(blocking_pool_);
+
+  // Start devtools server
+  devtools_server_ = ui::devtools::UiDevToolsServer::Create(nullptr);
+  if (devtools_server_) {
+    auto dom_backend = base::MakeUnique<devtools::AshDevToolsDOMAgent>();
+    auto css_backend =
+        base::MakeUnique<devtools::AshDevToolsCSSAgent>(dom_backend.get());
+    auto devtools_client = base::MakeUnique<ui::devtools::UiDevToolsClient>(
+        "Ash", devtools_server_.get());
+    devtools_client->AddAgent(std::move(dom_backend));
+    devtools_client->AddAgent(std::move(css_backend));
+    devtools_server_->AttachClient(std::move(devtools_client));
+  }
 
   if (is_mash)
     app_list_delegate_impl_ = base::MakeUnique<AppListDelegateImpl>();
diff --git a/ash/shell.h b/ash/shell.h
index 39209b1d..32ac6ee 100644
--- a/ash/shell.h
+++ b/ash/shell.h
@@ -35,6 +35,10 @@
 }
 }
 
+namespace base {
+class SequencedWorkerPool;
+}
+
 namespace chromeos {
 class AudioA11yController;
 }
@@ -52,6 +56,9 @@
 namespace ui {
 class UserActivityDetector;
 class UserActivityPowerManagerNotifier;
+namespace devtools {
+class UiDevToolsServer;
+}
 }
 
 namespace views {
@@ -74,8 +81,10 @@
 namespace ash {
 
 class AcceleratorControllerDelegateAura;
+class AccessibilityDelegate;
 class AppListDelegateImpl;
 class AshNativeCursorManager;
+class AshTouchTransformController;
 class AutoclickController;
 class BluetoothNotificationController;
 class DisplayColorManager;
@@ -95,6 +104,7 @@
 class MagnificationController;
 class MouseCursorEventFilter;
 class OverlayEventFilter;
+class PaletteDelegate;
 class PartialMagnificationController;
 class PowerButtonController;
 class PowerEventObserver;
@@ -117,11 +127,12 @@
 class SystemModalContainerEventFilter;
 class SystemTray;
 class ToplevelWindowEventHandler;
-class AshTouchTransformController;
 class ScreenLayoutObserver;
+class ToastManager;
 class VirtualKeyboardController;
 class VideoActivityNotifier;
 class VideoDetector;
+class WallpaperController;
 class WebNotificationTray;
 class WindowPositioner;
 class WindowTreeHostManager;
@@ -252,6 +263,12 @@
     return accelerator_controller_delegate_.get();
   }
 
+  AccessibilityDelegate* accessibility_delegate() {
+    return accessibility_delegate_.get();
+  }
+  const scoped_refptr<base::SequencedWorkerPool>& blocking_pool() {
+    return blocking_pool_;
+  }
   display::DisplayManager* display_manager() { return display_manager_.get(); }
   DisplayConfigurationController* display_configuration_controller() {
     return display_configuration_controller_.get();
@@ -274,7 +291,11 @@
   LockStateController* lock_state_controller() {
     return lock_state_controller_.get();
   }
+  PaletteDelegate* palette_delegate() { return palette_delegate_.get(); }
   VideoDetector* video_detector() { return video_detector_.get(); }
+  WallpaperController* wallpaper_controller() {
+    return wallpaper_controller_.get();
+  }
   WindowTreeHostManager* window_tree_host_manager() {
     return window_tree_host_manager_.get();
   }
@@ -317,6 +338,8 @@
     return autoclick_controller_.get();
   }
 
+  ToastManager* toast_manager() { return toast_manager_.get(); }
+
   aura::client::ActivationClient* activation_client();
 
   // Force the shelf to query for it's current visibility state.
@@ -515,11 +538,16 @@
   std::unique_ptr<SessionStateDelegate> session_state_delegate_;
   std::unique_ptr<WindowPositioner> window_positioner_;
 
+  std::unique_ptr<AccessibilityDelegate> accessibility_delegate_;
+  std::unique_ptr<PaletteDelegate> palette_delegate_;
   std::unique_ptr<DragDropController> drag_drop_controller_;
   std::unique_ptr<ResizeShadowController> resize_shadow_controller_;
+  std::unique_ptr<ToastManager> toast_manager_;
+  std::unique_ptr<WallpaperController> wallpaper_controller_;
   std::unique_ptr<::wm::ShadowController> shadow_controller_;
   std::unique_ptr<::wm::VisibilityController> visibility_controller_;
   std::unique_ptr<::wm::WindowModalityController> window_modality_controller_;
+  std::unique_ptr<ui::devtools::UiDevToolsServer> devtools_server_;
   std::unique_ptr<views::corewm::TooltipController> tooltip_controller_;
   LinkHandlerModelFactory* link_handler_model_factory_;
   std::unique_ptr<PowerButtonController> power_button_controller_;
@@ -626,6 +654,8 @@
 
   base::ObserverList<ShellObserver> shell_observers_;
 
+  scoped_refptr<base::SequencedWorkerPool> blocking_pool_;
+
   DISALLOW_COPY_AND_ASSIGN(Shell);
 };
 
diff --git a/ash/system/chromeos/power/tablet_power_button_controller.cc b/ash/system/chromeos/power/tablet_power_button_controller.cc
index 5199624..fcdf81b 100644
--- a/ash/system/chromeos/power/tablet_power_button_controller.cc
+++ b/ash/system/chromeos/power/tablet_power_button_controller.cc
@@ -222,7 +222,7 @@
   delegate->UpdateTouchscreenStatusFromPrefs();
 
   // Send an a11y alert.
-  WmShell::Get()->accessibility_delegate()->TriggerAccessibilityAlert(
+  Shell::GetInstance()->accessibility_delegate()->TriggerAccessibilityAlert(
       forced_off ? A11Y_ALERT_SCREEN_OFF : A11Y_ALERT_SCREEN_ON);
 }
 
diff --git a/ash/system/toast/toast_manager_unittest.cc b/ash/system/toast/toast_manager_unittest.cc
index 5fef6c2..4ae8eeac 100644
--- a/ash/system/toast/toast_manager_unittest.cc
+++ b/ash/system/toast/toast_manager_unittest.cc
@@ -34,7 +34,7 @@
   void SetUp() override {
     test::AshTestBase::SetUp();
 
-    manager_ = WmShell::Get()->toast_manager();
+    manager_ = Shell::GetInstance()->toast_manager();
 
     manager_->ResetSerialForTesting();
     EXPECT_EQ(0, GetToastSerial());
diff --git a/ash/test/shell_test_api.cc b/ash/test/shell_test_api.cc
index 8c8293c..4f543575 100644
--- a/ash/test/shell_test_api.cc
+++ b/ash/test/shell_test_api.cc
@@ -3,7 +3,7 @@
 // found in the LICENSE file.
 
 #include "ash/test/shell_test_api.h"
-
+#include "ash/common/palette_delegate.h"
 #include "ash/common/session/session_state_delegate.h"
 #include "ash/display/display_configuration_controller.h"
 #include "ash/root_window_controller.h"
@@ -12,6 +12,8 @@
 namespace ash {
 namespace test {
 
+ShellTestApi::ShellTestApi() : ShellTestApi(Shell::GetInstance()) {}
+
 ShellTestApi::ShellTestApi(Shell* shell) : shell_(shell) {}
 
 SystemGestureEventFilter* ShellTestApi::system_gesture_event_filter() {
@@ -38,6 +40,11 @@
   shell_->display_configuration_controller()->ResetAnimatorForTest();
 }
 
+void ShellTestApi::SetPaletteDelegate(
+    std::unique_ptr<PaletteDelegate> palette_delegate) {
+  shell_->palette_delegate_ = std::move(palette_delegate);
+}
+
 void ShellTestApi::SetSessionStateDelegate(
     SessionStateDelegate* session_state_delegate) {
   shell_->session_state_delegate_.reset(session_state_delegate);
diff --git a/ash/test/shell_test_api.h b/ash/test/shell_test_api.h
index 236258be..5a8ecd1 100644
--- a/ash/test/shell_test_api.h
+++ b/ash/test/shell_test_api.h
@@ -5,12 +5,15 @@
 #ifndef ASH_TEST_SHELL_TEST_API_H_
 #define ASH_TEST_SHELL_TEST_API_H_
 
+#include <memory>
+
 #include "base/macros.h"
 
 namespace ash {
 class AshNativeCursorManager;
 class DragDropController;
 class MaximizeModeWindowManager;
+class PaletteDelegate;
 class SessionStateDelegate;
 class ScreenPositionController;
 class Shell;
@@ -22,6 +25,7 @@
 // Accesses private data from a Shell for testing.
 class ShellTestApi {
  public:
+  ShellTestApi();
   explicit ShellTestApi(Shell* shell);
 
   SystemGestureEventFilter* system_gesture_event_filter();
@@ -32,7 +36,7 @@
   MaximizeModeWindowManager* maximize_mode_window_manager();
   void DisableDisplayAnimator();
 
-  // Set SessionStateDelegate.
+  void SetPaletteDelegate(std::unique_ptr<PaletteDelegate> palette_delegate);
   void SetSessionStateDelegate(SessionStateDelegate* session_state_delegate);
 
  private:
diff --git a/ash/test/test_wallpaper_delegate.cc b/ash/test/test_wallpaper_delegate.cc
index 16279be1..1296314 100644
--- a/ash/test/test_wallpaper_delegate.cc
+++ b/ash/test/test_wallpaper_delegate.cc
@@ -5,7 +5,7 @@
 #include "ash/test/test_wallpaper_delegate.h"
 
 #include "ash/common/wallpaper/wallpaper_controller.h"
-#include "ash/common/wm_shell.h"
+#include "ash/shell.h"
 
 namespace ash {
 namespace test {
@@ -17,7 +17,7 @@
 void TestWallpaperDelegate::UpdateWallpaper(bool clear_cache) {
   DefaultWallpaperDelegate::UpdateWallpaper(clear_cache);
   if (!custom_wallpaper_.isNull()) {
-    WmShell::Get()->wallpaper_controller()->SetWallpaperImage(
+    Shell::GetInstance()->wallpaper_controller()->SetWallpaperImage(
         custom_wallpaper_, wallpaper::WALLPAPER_LAYOUT_STRETCH);
   }
   update_wallpaper_count_++;
diff --git a/ash/wm/ash_focus_rules.cc b/ash/wm/ash_focus_rules.cc
index 549ee4a..0dcc82d 100644
--- a/ash/wm/ash_focus_rules.cc
+++ b/ash/wm/ash_focus_rules.cc
@@ -120,11 +120,10 @@
     aura::Window* ignore) const {
   aura::Window* window = nullptr;
   aura::Window* root = ignore ? ignore->GetRootWindow() : nullptr;
-  WmWindow::Windows containers = GetContainersFromAllRootWindows(
-      kActivatableShellWindowIds[index], WmWindow::Get(root));
-  for (WmWindow* container : containers) {
-    window = GetTopmostWindowToActivateInContainer(
-        WmWindow::GetAuraWindow(container), ignore);
+  aura::Window::Windows containers =
+      GetContainersFromAllRootWindows(kActivatableShellWindowIds[index], root);
+  for (aura::Window* container : containers) {
+    window = GetTopmostWindowToActivateInContainer(container, ignore);
     if (window)
       return window;
   }
diff --git a/ash/wm/lock_state_controller.cc b/ash/wm/lock_state_controller.cc
index 293d95a..c8def6f 100644
--- a/ash/wm/lock_state_controller.cc
+++ b/ash/wm/lock_state_controller.cc
@@ -292,7 +292,7 @@
   }
 
   base::TimeDelta sound_duration =
-      WmShell::Get()->accessibility_delegate()->PlayShutdownSound();
+      Shell::GetInstance()->accessibility_delegate()->PlayShutdownSound();
   sound_duration =
       std::min(sound_duration,
                base::TimeDelta::FromMilliseconds(kMaxShutdownSoundDurationMs));
diff --git a/ash/wm/overview/window_selector_unittest.cc b/ash/wm/overview/window_selector_unittest.cc
index b6c6bf8..ed7528c4 100644
--- a/ash/wm/overview/window_selector_unittest.cc
+++ b/ash/wm/overview/window_selector_unittest.cc
@@ -405,7 +405,8 @@
 // Tests that an a11y alert is sent on entering overview mode.
 TEST_F(WindowSelectorTest, A11yAlertOnOverviewMode) {
   gfx::Rect bounds(0, 0, 400, 400);
-  AccessibilityDelegate* delegate = WmShell::Get()->accessibility_delegate();
+  AccessibilityDelegate* delegate =
+      Shell::GetInstance()->accessibility_delegate();
   std::unique_ptr<aura::Window> window1(CreateWindow(bounds));
   EXPECT_NE(delegate->GetLastAccessibilityAlert(),
             A11Y_ALERT_WINDOW_OVERVIEW_MODE_ENTERED);
diff --git a/ash/wm/screen_dimmer_unittest.cc b/ash/wm/screen_dimmer_unittest.cc
index 709886f..df36a56 100644
--- a/ash/wm/screen_dimmer_unittest.cc
+++ b/ash/wm/screen_dimmer_unittest.cc
@@ -6,10 +6,9 @@
 
 #include <memory>
 
+#include "ash/common/window_user_data.h"
 #include "ash/common/wm/window_dimmer.h"
-#include "ash/common/wm_shell.h"
 #include "ash/common/wm_window.h"
-#include "ash/common/wm_window_user_data.h"
 #include "ash/root_window_controller.h"
 #include "ash/shell.h"
 #include "ash/test/ash_test_base.h"
@@ -37,9 +36,8 @@
 
   aura::Window* GetDimWindow() {
     WindowDimmer* window_dimmer =
-        dimmer_->window_dimmers_->Get(WmShell::Get()->GetPrimaryRootWindow());
-    return window_dimmer ? WmWindow::GetAuraWindow(window_dimmer->window())
-                         : nullptr;
+        dimmer_->window_dimmers_->Get(Shell::GetPrimaryRootWindow());
+    return window_dimmer ? window_dimmer->window() : nullptr;
   }
 
   ui::Layer* GetDimWindowLayer() {
@@ -132,7 +130,7 @@
 
 // This test verifies ScreenDimmer can be destroyed after the shell. The
 // interesting part of this test is in TearDown(), which creates a ScreenDimmer
-// that is deleted after WmShell.
+// that is deleted after Shell.
 TEST_F(ScreenDimmerShellDestructionTest, DontCrashIfScreenDimmerOutlivesShell) {
 }
 
diff --git a/ash/wm/screen_pinning_controller.cc b/ash/wm/screen_pinning_controller.cc
index 10f5552..4a708db 100644
--- a/ash/wm/screen_pinning_controller.cc
+++ b/ash/wm/screen_pinning_controller.cc
@@ -7,18 +7,20 @@
 #include <algorithm>
 #include <vector>
 
+#include "ash/common/window_user_data.h"
 #include "ash/common/wm/container_finder.h"
 #include "ash/common/wm/window_dimmer.h"
 #include "ash/common/wm/window_state.h"
 #include "ash/common/wm_window.h"
-#include "ash/common/wm_window_user_data.h"
 #include "ash/display/window_tree_host_manager.h"
 #include "ash/public/cpp/shell_window_ids.h"
 #include "ash/shell.h"
 #include "base/auto_reset.h"
 #include "base/logging.h"
 #include "base/memory/ptr_util.h"
+#include "ui/aura/window.h"
 #include "ui/aura/window_observer.h"
+#include "ui/wm/core/window_util.h"
 
 namespace ash {
 namespace {
@@ -30,11 +32,11 @@
   WmWindow* pinned_root = pinned_window->GetRootWindow();
 
   std::vector<WmWindow*> result;
-  for (WmWindow* system_modal : wm::GetContainersFromAllRootWindows(
+  for (aura::Window* system_modal : wm::GetContainersFromAllRootWindows(
            kShellWindowId_SystemModalContainer)) {
-    if (system_modal->GetRootWindow() == pinned_root)
+    if (WmWindow::Get(system_modal)->GetRootWindow() == pinned_root)
       continue;
-    result.push_back(system_modal);
+    result.push_back(WmWindow::Get(system_modal));
   }
   return result;
 }
@@ -53,6 +55,11 @@
   }
 }
 
+// Returns true if the aura::Window from a WindowDimmer is visible.
+bool IsWindowDimmerWindowVisible(WindowDimmer* window_dimmer) {
+  return window_dimmer->window()->layer()->GetTargetVisibility();
+}
+
 }  // namespace
 
 // Adapter to fire OnPinnedContainerWindowStackingChanged().
@@ -142,7 +149,7 @@
 
 ScreenPinningController::ScreenPinningController(
     WindowTreeHostManager* window_tree_host_manager)
-    : window_dimmers_(base::MakeUnique<WmWindowUserData<WindowDimmer>>()),
+    : window_dimmers_(base::MakeUnique<WindowUserData<WindowDimmer>>()),
       window_tree_host_manager_(window_tree_host_manager),
       pinned_container_window_observer_(
           base::MakeUnique<PinnedContainerWindowObserver>(this)),
@@ -252,7 +259,7 @@
 
 void ScreenPinningController::OnWindowAddedToSystemModalContainer(
     WmWindow* new_window) {
-  KeepDimWindowAtBottom(new_window->GetParent());
+  KeepDimWindowAtBottom(new_window->GetParent()->aura_window());
   WmWindow::GetAuraWindow(new_window)
       ->AddObserver(system_modal_container_child_window_observer_.get());
 }
@@ -265,17 +272,17 @@
 
 void ScreenPinningController::OnSystemModalContainerWindowStackingChanged(
     WmWindow* window) {
-  KeepDimWindowAtBottom(window->GetParent());
+  KeepDimWindowAtBottom(window->GetParent()->aura_window());
 }
 
 WmWindow* ScreenPinningController::CreateWindowDimmer(WmWindow* container) {
   std::unique_ptr<WindowDimmer> window_dimmer =
-      base::MakeUnique<WindowDimmer>(container);
+      base::MakeUnique<WindowDimmer>(container->aura_window());
   window_dimmer->SetDimOpacity(1);  // Fully opaque.
-  window_dimmer->window()->SetFullscreen(true);
+  ::wm::SetWindowFullscreen(window_dimmer->window(), true);
   window_dimmer->window()->Show();
-  WmWindow* window = window_dimmer->window();
-  window_dimmers_->Set(container, std::move(window_dimmer));
+  WmWindow* window = WmWindow::Get(window_dimmer->window());
+  window_dimmers_->Set(container->aura_window(), std::move(window_dimmer));
   return window;
 }
 
@@ -292,9 +299,9 @@
   // WindowDimmer. So create it.
 
   // First, delete unnecessary WindowDimmers.
-  for (WmWindow* container : window_dimmers_->GetWindows()) {
-    if (container != pinned_window_->GetParent() &&
-        !window_dimmers_->Get(container)->window()->GetTargetVisibility()) {
+  for (aura::Window* container : window_dimmers_->GetWindows()) {
+    if (container != pinned_window_->GetParent()->aura_window() &&
+        !IsWindowDimmerWindowVisible(window_dimmers_->Get(container))) {
       window_dimmers_->Set(container, nullptr);
     }
   }
@@ -303,7 +310,7 @@
   std::vector<WmWindow*> system_modal_containers =
       GetSystemModalWindowsExceptPinned(pinned_window_);
   for (WmWindow* system_modal : system_modal_containers) {
-    if (window_dimmers_->Get(system_modal)) {
+    if (window_dimmers_->Get(system_modal->aura_window())) {
       // |system_modal| already has a WindowDimmer.
       continue;
     }
@@ -326,12 +333,15 @@
   base::AutoReset<bool> auto_reset(&in_restacking_, true);
   WmWindow* container = pinned_window_->GetParent();
   container->StackChildAtTop(pinned_window_);
-  WindowDimmer* pinned_window_dimmer = window_dimmers_->Get(container);
-  if (pinned_window_dimmer && pinned_window_dimmer->window())
-    container->StackChildBelow(pinned_window_dimmer->window(), pinned_window_);
+  WindowDimmer* pinned_window_dimmer =
+      window_dimmers_->Get(container->aura_window());
+  if (pinned_window_dimmer && pinned_window_dimmer->window()) {
+    container->StackChildBelow(WmWindow::Get(pinned_window_dimmer->window()),
+                               pinned_window_);
+  }
 }
 
-void ScreenPinningController::KeepDimWindowAtBottom(WmWindow* container) {
+void ScreenPinningController::KeepDimWindowAtBottom(aura::Window* container) {
   if (in_restacking_)
     return;
 
diff --git a/ash/wm/screen_pinning_controller.h b/ash/wm/screen_pinning_controller.h
index 3245c60..90e56a3 100644
--- a/ash/wm/screen_pinning_controller.h
+++ b/ash/wm/screen_pinning_controller.h
@@ -11,6 +11,10 @@
 #include "ash/display/window_tree_host_manager.h"
 #include "base/macros.h"
 
+namespace aura {
+class Window;
+}
+
 namespace ash {
 
 class WindowDimmer;
@@ -18,7 +22,7 @@
 class WmWindow;
 
 template <typename UserData>
-class WmWindowUserData;
+class WindowUserData;
 
 // Handles pinned state.
 class ScreenPinningController : public WindowTreeHostManager::Observer {
@@ -68,7 +72,7 @@
   void KeepPinnedWindowOnTop();
 
   // Keeps the dim window at bottom of the container.
-  void KeepDimWindowAtBottom(WmWindow* container);
+  void KeepDimWindowAtBottom(aura::Window* container);
 
   // Creates a WindowDimmer for |container| and places it in |window_dimmers_|.
   // Returns the window from WindowDimmer.
@@ -83,7 +87,7 @@
   // Owns the WindowDimmers. There is one WindowDimmer for the parent of
   // |pinned_window_| and one for each display other than the display
   // |pinned_window_| is on.
-  std::unique_ptr<WmWindowUserData<WindowDimmer>> window_dimmers_;
+  std::unique_ptr<WindowUserData<WindowDimmer>> window_dimmers_;
 
   // Set true only when restacking done by this controller.
   bool in_restacking_ = false;
diff --git a/ash/wm/system_modal_container_layout_manager_unittest.cc b/ash/wm/system_modal_container_layout_manager_unittest.cc
index fc1c3ae..e5a5984 100644
--- a/ash/wm/system_modal_container_layout_manager_unittest.cc
+++ b/ash/wm/system_modal_container_layout_manager_unittest.cc
@@ -48,12 +48,12 @@
 }
 
 bool AllRootWindowsHaveModalBackgroundsForContainer(int container_id) {
-  WmWindow::Windows containers =
+  aura::Window::Windows containers =
       wm::GetContainersFromAllRootWindows(container_id);
   bool has_modal_screen = !containers.empty();
-  for (WmWindow* container : containers) {
+  for (aura::Window* container : containers) {
     has_modal_screen &= static_cast<SystemModalContainerLayoutManager*>(
-                            container->GetLayoutManager())
+                            WmWindow::Get(container)->GetLayoutManager())
                             ->has_window_dimmer();
   }
   return has_modal_screen;
diff --git a/base/android/java/src/org/chromium/base/ApiCompatibilityUtils.java b/base/android/java/src/org/chromium/base/ApiCompatibilityUtils.java
index 825bce7..f773859 100644
--- a/base/android/java/src/org/chromium/base/ApiCompatibilityUtils.java
+++ b/base/android/java/src/org/chromium/base/ApiCompatibilityUtils.java
@@ -639,4 +639,13 @@
             window.setFeatureInt(featureNumber, featureValue);
         }
     }
+
+    /**
+     *  Null-safe equivalent of {@code a.equals(b)}.
+     *
+     *  @see Objects#equals(Object, Object)
+     */
+    public static boolean objectEquals(Object a, Object b) {
+        return (a == null) ? (b == null) : a.equals(b);
+    }
 }
diff --git a/base/memory/singleton.h b/base/memory/singleton.h
index cfdff78..5c58d5fe 100644
--- a/base/memory/singleton.h
+++ b/base/memory/singleton.h
@@ -153,14 +153,17 @@
 // Example usage:
 //
 // In your header:
-//   template <typename T> struct DefaultSingletonTraits;
+//   namespace base {
+//   template <typename T>
+//   struct DefaultSingletonTraits;
+//   }
 //   class FooClass {
 //    public:
 //     static FooClass* GetInstance();  <-- See comment below on this.
 //     void Bar() { ... }
 //    private:
 //     FooClass() { ... }
-//     friend struct DefaultSingletonTraits<FooClass>;
+//     friend struct base::DefaultSingletonTraits<FooClass>;
 //
 //     DISALLOW_COPY_AND_ASSIGN(FooClass);
 //   };
@@ -168,7 +171,14 @@
 // In your source file:
 //  #include "base/memory/singleton.h"
 //  FooClass* FooClass::GetInstance() {
-//    return Singleton<FooClass>::get();
+//    return base::Singleton<FooClass>::get();
+//  }
+//
+// Or for leaky singletons:
+//  #include "base/memory/singleton.h"
+//  FooClass* FooClass::GetInstance() {
+//    return base::Singleton<
+//        FooClass, base::LeakySingletonTraits<FooClass>>::get();
 //  }
 //
 // And to call methods on FooClass:
diff --git a/base/trace_event/memory_dump_manager.cc b/base/trace_event/memory_dump_manager.cc
index f86b9e9..31016f9 100644
--- a/base/trace_event/memory_dump_manager.cc
+++ b/base/trace_event/memory_dump_manager.cc
@@ -156,6 +156,7 @@
 
 MemoryDumpManager::MemoryDumpManager()
     : delegate_(nullptr),
+      is_coordinator_(false),
       memory_tracing_enabled_(0),
       tracing_process_id_(kInvalidTracingProcessId),
       dumper_registrations_ignored_for_testing_(false),
@@ -213,12 +214,14 @@
   heap_profiling_enabled_ = true;
 }
 
-void MemoryDumpManager::Initialize(MemoryDumpManagerDelegate* delegate) {
+void MemoryDumpManager::Initialize(MemoryDumpManagerDelegate* delegate,
+                                   bool is_coordinator) {
   {
     AutoLock lock(lock_);
     DCHECK(delegate);
     DCHECK(!delegate_);
     delegate_ = delegate;
+    is_coordinator_ = is_coordinator;
     EnableHeapProfilingIfNeeded();
   }
 
@@ -457,10 +460,21 @@
                                     TRACE_ID_MANGLE(guid));
   MemoryDumpCallback wrapped_callback = Bind(&OnGlobalDumpDone, callback);
 
+  // Technically there is no need to grab the |lock_| here as the delegate is
+  // long-lived and can only be set by Initialize(), which is locked and
+  // necessarily happens before memory_tracing_enabled_ == true.
+  // Not taking the |lock_|, though, is lakely make TSan barf and, at this point
+  // (memory-infra is enabled) we're not in the fast-path anymore.
+  MemoryDumpManagerDelegate* delegate;
+  {
+    AutoLock lock(lock_);
+    delegate = delegate_;
+  }
+
   // The delegate will coordinate the IPC broadcast and at some point invoke
   // CreateProcessDump() to get a dump for the current process.
   MemoryDumpRequestArgs args = {guid, dump_type, level_of_detail};
-  delegate_->RequestGlobalMemoryDump(args, wrapped_callback);
+  delegate->RequestGlobalMemoryDump(args, wrapped_callback);
 }
 
 void MemoryDumpManager::RequestGlobalDump(
@@ -848,7 +862,7 @@
       dump_scheduler_->NotifyPollingSupported();
 
     // Only coordinator process triggers periodic global memory dumps.
-    if (delegate_->IsCoordinator())
+    if (is_coordinator_)
       dump_scheduler_->NotifyPeriodicTriggerSupported();
   }
 
@@ -893,6 +907,10 @@
   return session_state_->IsDumpModeAllowed(dump_mode);
 }
 
+uint64_t MemoryDumpManager::GetTracingProcessId() const {
+  return delegate_->GetTracingProcessId();
+}
+
 MemoryDumpManager::MemoryDumpProviderInfo::MemoryDumpProviderInfo(
     MemoryDumpProvider* dump_provider,
     const char* name,
diff --git a/base/trace_event/memory_dump_manager.h b/base/trace_event/memory_dump_manager.h
index c1f565c..92cc2f4 100644
--- a/base/trace_event/memory_dump_manager.h
+++ b/base/trace_event/memory_dump_manager.h
@@ -22,14 +22,6 @@
 #include "base/trace_event/process_memory_dump.h"
 #include "base/trace_event/trace_event.h"
 
-// Forward declare |MemoryDumpManagerDelegateImplTest| so that we can make it a
-// friend of |MemoryDumpManager| and give it access to |SetInstanceForTesting|.
-namespace memory_instrumentation {
-
-class MemoryDumpManagerDelegateImplTest;
-
-}  // namespace memory_instrumentation
-
 namespace base {
 
 class SingleThreadTaskRunner;
@@ -62,10 +54,13 @@
   // On the other side, the MemoryDumpManager will not be fully operational
   // (i.e. will NACK any RequestGlobalMemoryDump()) until initialized.
   // Arguments:
+  //  is_coordinator: if true this MemoryDumpManager instance will act as a
+  //      coordinator and schedule periodic dumps (if enabled via TraceConfig);
+  //      false when the MemoryDumpManager is initialized in a slave process.
   //  delegate: inversion-of-control interface for embedder-specific behaviors
   //      (multiprocess handshaking). See the lifetime and thread-safety
   //      requirements in the |MemoryDumpManagerDelegate| docstring.
-  void Initialize(MemoryDumpManagerDelegate* delegate);
+  void Initialize(MemoryDumpManagerDelegate* delegate, bool is_coordinator);
 
   // (Un)Registers a MemoryDumpProvider instance.
   // Args:
@@ -140,10 +135,7 @@
   // retrieved by child processes only when tracing is enabled. This is
   // intended to express cross-process sharing of memory dumps on the
   // child-process side, without having to know its own child process id.
-  uint64_t GetTracingProcessId() const { return tracing_process_id_; }
-  void set_tracing_process_id(uint64_t tracing_process_id) {
-    tracing_process_id_ = tracing_process_id;
-  }
+  uint64_t GetTracingProcessId() const;
 
   // Returns the name for a the allocated_objects dump. Use this to declare
   // suballocator dumps from other dump providers.
@@ -164,7 +156,6 @@
   friend class MemoryDumpManagerDelegate;
   friend class MemoryDumpManagerTest;
   friend class MemoryDumpScheduler;
-  friend class memory_instrumentation::MemoryDumpManagerDelegateImplTest;
 
   // Descriptor used to hold information about registered MDPs.
   // Some important considerations about lifetime of this object:
@@ -359,6 +350,9 @@
 
   MemoryDumpManagerDelegate* delegate_;  // Not owned.
 
+  // When true, this instance is in charge of coordinating periodic dumps.
+  bool is_coordinator_;
+
   // Protects from concurrent accesses to the |dump_providers_*| and |delegate_|
   // to guard against disabling logging while dumping on another thread.
   Lock lock_;
@@ -394,7 +388,9 @@
   virtual void RequestGlobalMemoryDump(const MemoryDumpRequestArgs& args,
                                        const MemoryDumpCallback& callback) = 0;
 
-  virtual bool IsCoordinator() const = 0;
+  // Returns tracing process id of the current process. This is used by
+  // MemoryDumpManager::GetTracingProcessId.
+  virtual uint64_t GetTracingProcessId() const = 0;
 
  protected:
   MemoryDumpManagerDelegate() {}
diff --git a/base/trace_event/memory_dump_manager_unittest.cc b/base/trace_event/memory_dump_manager_unittest.cc
index 330c75d..51d4194 100644
--- a/base/trace_event/memory_dump_manager_unittest.cc
+++ b/base/trace_event/memory_dump_manager_unittest.cc
@@ -119,8 +119,7 @@
 // requests locally to the MemoryDumpManager instead of performing IPC dances.
 class MemoryDumpManagerDelegateForTesting : public MemoryDumpManagerDelegate {
  public:
-  MemoryDumpManagerDelegateForTesting(bool is_coordinator)
-      : is_coordinator_(is_coordinator) {
+  MemoryDumpManagerDelegateForTesting() {
     ON_CALL(*this, RequestGlobalMemoryDump(_, _))
         .WillByDefault(Invoke(
             this, &MemoryDumpManagerDelegateForTesting::CreateProcessDump));
@@ -130,13 +129,13 @@
                void(const MemoryDumpRequestArgs& args,
                     const MemoryDumpCallback& callback));
 
-  bool IsCoordinator() const override { return is_coordinator_; }
+  uint64_t GetTracingProcessId() const override {
+    NOTREACHED();
+    return MemoryDumpManager::kInvalidTracingProcessId;
+  }
 
   // Promote the CreateProcessDump to public so it can be used by test fixtures.
   using MemoryDumpManagerDelegate::CreateProcessDump;
-
- private:
-  bool is_coordinator_;
 };
 
 class MockMemoryDumpProvider : public MemoryDumpProvider {
@@ -221,12 +220,13 @@
     mdm_.reset(new MemoryDumpManager());
     MemoryDumpManager::SetInstanceForTesting(mdm_.get());
     ASSERT_EQ(mdm_.get(), MemoryDumpManager::GetInstance());
+    delegate_.reset(new MemoryDumpManagerDelegateForTesting);
   }
 
   void TearDown() override {
     MemoryDumpManager::SetInstanceForTesting(nullptr);
-    delegate_.reset();
     mdm_.reset();
+    delegate_.reset();
     message_loop_.reset();
     TraceLog::DeleteForTesting();
   }
@@ -248,8 +248,7 @@
  protected:
   void InitializeMemoryDumpManager(bool is_coordinator) {
     mdm_->set_dumper_registrations_ignored_for_testing(true);
-    delegate_.reset(new MemoryDumpManagerDelegateForTesting(is_coordinator));
-    mdm_->Initialize(delegate_.get());
+    mdm_->Initialize(delegate_.get(), is_coordinator);
   }
 
   void RequestGlobalDumpAndWait(MemoryDumpType dump_type,
@@ -898,6 +897,7 @@
   // initialization gets NACK-ed cleanly.
   {
     EXPECT_CALL(mdp, OnMemoryDump(_, _)).Times(0);
+    EXPECT_CALL(*delegate_, RequestGlobalMemoryDump(_, _)).Times(0);
     RequestGlobalDumpAndWait(MemoryDumpType::EXPLICITLY_TRIGGERED,
                              MemoryDumpLevelOfDetail::DETAILED);
     EXPECT_FALSE(last_callback_success_);
@@ -906,9 +906,9 @@
   // Now late-initialize the MemoryDumpManager and check that the
   // RequestGlobalDump completes successfully.
   {
-    InitializeMemoryDumpManager(false /* is_coordinator */);
     EXPECT_CALL(mdp, OnMemoryDump(_, _)).Times(1);
     EXPECT_CALL(*delegate_, RequestGlobalMemoryDump(_, _)).Times(1);
+    InitializeMemoryDumpManager(false /* is_coordinator */);
     RequestGlobalDumpAndWait(MemoryDumpType::EXPLICITLY_TRIGGERED,
                              MemoryDumpLevelOfDetail::DETAILED);
     EXPECT_TRUE(last_callback_success_);
diff --git a/cc/playback/display_item_list.cc b/cc/playback/display_item_list.cc
index 8591c37..779fc81 100644
--- a/cc/playback/display_item_list.cc
+++ b/cc/playback/display_item_list.cc
@@ -247,7 +247,9 @@
   gfx::Rect bounds = rtree_.GetBounds();
   DiscardableImageMap::ScopedMetadataGenerator generator(
       &image_map_, gfx::Size(bounds.right(), bounds.bottom()));
-  Raster(generator.canvas(), nullptr, gfx::Rect(), 1.f);
+  auto* canvas = generator.canvas();
+  for (const auto& item : inputs_.items)
+    item.Raster(canvas, nullptr);
 }
 
 void DisplayItemList::GetDiscardableImagesInRect(
diff --git a/cc/surfaces/compositor_frame_sink_support_unittest.cc b/cc/surfaces/compositor_frame_sink_support_unittest.cc
index d738e21..28cb344 100644
--- a/cc/surfaces/compositor_frame_sink_support_unittest.cc
+++ b/cc/surfaces/compositor_frame_sink_support_unittest.cc
@@ -984,5 +984,73 @@
                                          CompositorFrame());
 }
 
+// Verifies that if a surface is marked destroyed and a new frame arrives for
+// it, it will be recovered.
+TEST_F(CompositorFrameSinkSupportTest, SurfaceResurrection) {
+  const SurfaceId parent_id = MakeSurfaceId(kParentFrameSink, 1);
+  const SurfaceId child_id = MakeSurfaceId(kChildFrameSink1, 3);
+
+  // Add a reference from the parent to the child.
+  parent_support().SubmitCompositorFrame(parent_id.local_surface_id(),
+                                         MakeCompositorFrame({child_id}));
+
+  // Create the child surface by submitting a frame to it.
+  EXPECT_EQ(nullptr, surface_manager().GetSurfaceForId(child_id));
+  child_support1().SubmitCompositorFrame(child_id.local_surface_id(),
+                                         CompositorFrame());
+
+  // Verify that the child surface is created.
+  Surface* surface = surface_manager().GetSurfaceForId(child_id);
+  EXPECT_NE(nullptr, surface);
+
+  // Attempt to destroy the child surface. The surface must still exist since
+  // the parent needs it but it will be marked as destroyed.
+  child_support1().EvictFrame();
+  surface = surface_manager().GetSurfaceForId(child_id);
+  EXPECT_NE(nullptr, surface);
+  EXPECT_TRUE(surface->destroyed());
+
+  // Child submits another frame to the same local surface id that is marked
+  // destroyed.
+  child_support1().SubmitCompositorFrame(child_id.local_surface_id(),
+                                         CompositorFrame());
+
+  // Verify that the surface that was marked destroyed is recovered and is being
+  // used again.
+  Surface* surface2 = surface_manager().GetSurfaceForId(child_id);
+  EXPECT_EQ(surface, surface2);
+  EXPECT_FALSE(surface2->destroyed());
+}
+
+// Verifies that if a LocalSurfaceId belonged to a surface that doesn't exist
+// anymore, it can still be reused for new surfaces.
+TEST_F(CompositorFrameSinkSupportTest, LocalSurfaceIdIsReusable) {
+  const SurfaceId parent_id = MakeSurfaceId(kParentFrameSink, 1);
+  const SurfaceId child_id = MakeSurfaceId(kChildFrameSink1, 3);
+
+  // Add a reference from parent.
+  parent_support().SubmitCompositorFrame(parent_id.local_surface_id(),
+                                         MakeCompositorFrame({child_id}));
+
+  // Submit the first frame. Creates the surface.
+  child_support1().SubmitCompositorFrame(child_id.local_surface_id(),
+                                         CompositorFrame());
+  EXPECT_NE(nullptr, surface_manager().GetSurfaceForId(child_id));
+
+  // Remove the reference from parant. This allows us to destroy the surface.
+  parent_support().SubmitCompositorFrame(parent_id.local_surface_id(),
+                                         CompositorFrame());
+
+  // Destroy the surface.
+  child_support1().EvictFrame();
+  EXPECT_EQ(nullptr, surface_manager().GetSurfaceForId(child_id));
+
+  // Submit another frame with the same local surface id. This should work fine
+  // and a new surface must be created.
+  child_support1().SubmitCompositorFrame(child_id.local_surface_id(),
+                                         CompositorFrame());
+  EXPECT_NE(nullptr, surface_manager().GetSurfaceForId(child_id));
+}
+
 }  // namespace test
 }  // namespace cc
diff --git a/cc/surfaces/surface_factory.cc b/cc/surfaces/surface_factory.cc
index f641274..c475793 100644
--- a/cc/surfaces/surface_factory.cc
+++ b/cc/surfaces/surface_factory.cc
@@ -150,17 +150,16 @@
 
 std::unique_ptr<Surface> SurfaceFactory::Create(
     const LocalSurfaceId& local_surface_id) {
-  auto surface = base::MakeUnique<Surface>(
-      SurfaceId(frame_sink_id_, local_surface_id), weak_factory_.GetWeakPtr());
   seen_first_frame_activation_ = false;
-  manager_->RegisterSurface(surface.get());
+  std::unique_ptr<Surface> surface =
+      manager_->CreateSurface(weak_factory_.GetWeakPtr(), local_surface_id);
   surface->AddObserver(this);
   return surface;
 }
 
 void SurfaceFactory::Destroy(std::unique_ptr<Surface> surface) {
   surface->RemoveObserver(this);
-  manager_->Destroy(std::move(surface));
+  manager_->DestroySurface(std::move(surface));
 }
 
 }  // namespace cc
diff --git a/cc/surfaces/surface_manager.cc b/cc/surfaces/surface_manager.cc
index bf72fce..753191d7 100644
--- a/cc/surfaces/surface_manager.cc
+++ b/cc/surfaces/surface_manager.cc
@@ -56,7 +56,7 @@
   for (SurfaceDestroyList::iterator it = surfaces_to_destroy_.begin();
        it != surfaces_to_destroy_.end();
        ++it) {
-    DeregisterSurface((*it)->surface_id());
+    UnregisterSurface((*it)->surface_id());
   }
   surfaces_to_destroy_.clear();
 
@@ -88,22 +88,45 @@
     dependency_tracker_->RequestSurfaceResolution(pending_surface);
 }
 
-void SurfaceManager::RegisterSurface(Surface* surface) {
+std::unique_ptr<Surface> SurfaceManager::CreateSurface(
+    base::WeakPtr<SurfaceFactory> surface_factory,
+    const LocalSurfaceId& local_surface_id) {
   DCHECK(thread_checker_.CalledOnValidThread());
-  DCHECK(surface);
-  DCHECK(!surface_map_.count(surface->surface_id()));
-  surface_map_[surface->surface_id()] = surface;
+  DCHECK(local_surface_id.is_valid() && surface_factory);
+
+  SurfaceId surface_id(surface_factory->frame_sink_id(), local_surface_id);
+
+  // If no surface with this SurfaceId exists, simply create the surface and
+  // return.
+  auto surface_iter = surface_map_.find(surface_id);
+  if (surface_iter == surface_map_.end()) {
+    auto surface = base::MakeUnique<Surface>(surface_id, surface_factory);
+    surface_map_[surface->surface_id()] = surface.get();
+    return surface;
+  }
+
+  // If a surface with this SurfaceId exists and it's not marked as destroyed,
+  // we should not receive a request to create a new surface with the same
+  // SurfaceId.
+  DCHECK(surface_iter->second->destroyed());
+
+  // If a surface with this SurfaceId exists and it's marked as destroyed,
+  // it means it's in the garbage collector's queue. We simply take it out of
+  // the queue and reuse it.
+  auto it =
+      std::find_if(surfaces_to_destroy_.begin(), surfaces_to_destroy_.end(),
+                   [&surface_id](const std::unique_ptr<Surface>& surface) {
+                     return surface->surface_id() == surface_id;
+                   });
+  DCHECK(it != surfaces_to_destroy_.end());
+  std::unique_ptr<Surface> surface = std::move(*it);
+  surfaces_to_destroy_.erase(it);
+  surface->set_destroyed(false);
+  DCHECK_EQ(surface_factory.get(), surface->factory().get());
+  return surface;
 }
 
-void SurfaceManager::DeregisterSurface(const SurfaceId& surface_id) {
-  DCHECK(thread_checker_.CalledOnValidThread());
-  SurfaceMap::iterator it = surface_map_.find(surface_id);
-  DCHECK(it != surface_map_.end());
-  surface_map_.erase(it);
-  RemoveAllSurfaceReferences(surface_id);
-}
-
-void SurfaceManager::Destroy(std::unique_ptr<Surface> surface) {
+void SurfaceManager::DestroySurface(std::unique_ptr<Surface> surface) {
   DCHECK(thread_checker_.CalledOnValidThread());
   surface->set_destroyed(true);
   surfaces_to_destroy_.push_back(std::move(surface));
@@ -211,7 +234,7 @@
        iter != surfaces_to_destroy_.end();) {
     SurfaceId surface_id = (*iter)->surface_id();
     if (reachable_surfaces.count(surface_id) == 0) {
-      DeregisterSurface(surface_id);
+      UnregisterSurface(surface_id);
       surfaces_to_delete.push_back(std::move(*iter));
       iter = surfaces_to_destroy_.erase(iter);
     } else {
@@ -616,6 +639,14 @@
     observer.OnSurfaceCreated(surface_info);
 }
 
+void SurfaceManager::UnregisterSurface(const SurfaceId& surface_id) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  SurfaceMap::iterator it = surface_map_.find(surface_id);
+  DCHECK(it != surface_map_.end());
+  surface_map_.erase(it);
+  RemoveAllSurfaceReferences(surface_id);
+}
+
 #if DCHECK_IS_ON()
 void SurfaceManager::SurfaceReferencesToStringImpl(const SurfaceId& surface_id,
                                                    std::string indent,
diff --git a/cc/surfaces/surface_manager.h b/cc/surfaces/surface_manager.h
index e4b6f17..c0c8e76 100644
--- a/cc/surfaces/surface_manager.h
+++ b/cc/surfaces/surface_manager.h
@@ -36,6 +36,7 @@
 class BeginFrameSource;
 class CompositorFrame;
 class Surface;
+class SurfaceFactory;
 class SurfaceFactoryClient;
 
 namespace test {
@@ -65,11 +66,12 @@
 
   void RequestSurfaceResolution(Surface* pending_surface);
 
-  void RegisterSurface(Surface* surface);
-  void DeregisterSurface(const SurfaceId& surface_id);
+  std::unique_ptr<Surface> CreateSurface(
+      base::WeakPtr<SurfaceFactory> surface_factory,
+      const LocalSurfaceId& local_surface_id);
 
   // Destroy the Surface once a set of sequence numbers has been satisfied.
-  void Destroy(std::unique_ptr<Surface> surface);
+  void DestroySurface(std::unique_ptr<Surface> surface);
 
   Surface* GetSurfaceForId(const SurfaceId& surface_id);
 
@@ -216,6 +218,10 @@
   // |surface_id| that were added before |surface_id| will also be removed.
   void RemoveTemporaryReference(const SurfaceId& surface_id, bool remove_range);
 
+  // Called when a surface is destroyed and it needs to be removed from the
+  // surface map.
+  void UnregisterSurface(const SurfaceId& surface_id);
+
 #if DCHECK_IS_ON()
   // Recursively prints surface references starting at |surface_id| to |str|.
   void SurfaceReferencesToStringImpl(const SurfaceId& surface_id,
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ChromeFeatureList.java b/chrome/android/java/src/org/chromium/chrome/browser/ChromeFeatureList.java
index 528b7092..8bba280 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeFeatureList.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeFeatureList.java
@@ -119,6 +119,7 @@
     public static final String ANDROID_PAY_INTEGRATION_V1 = "AndroidPayIntegrationV1";
     public static final String ANDROID_PAY_INTEGRATION_V2 = "AndroidPayIntegrationV2";
     public static final String ANDROID_PAYMENT_APPS = "AndroidPaymentApps";
+    public static final String ANDROID_PAYMENT_APPS_FILTER = "AndroidPaymentAppsFilter";
     public static final String AUTOFILL_SCAN_CARDHOLDER_NAME = "AutofillScanCardholderName";
     public static final String CCT_EXTERNAL_LINK_HANDLING = "CCTExternalLinkHandling";
     public static final String CCT_POST_MESSAGE_API = "CCTPostMessageAPI";
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/banners/AppBannerManager.java b/chrome/android/java/src/org/chromium/chrome/browser/banners/AppBannerManager.java
index 1f76edae..babe917 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/banners/AppBannerManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/banners/AppBannerManager.java
@@ -131,9 +131,7 @@
     /** Returns the language option to use for the add to homescreen dialog and menu item. */
     public static int getHomescreenLanguageOption() {
         int languageOption = nativeGetHomescreenLanguageOption();
-        if (languageOption == LanguageOption.ADD) {
-            return R.string.menu_add_to_homescreen_add;
-        } else if (languageOption == LanguageOption.INSTALL) {
+        if (languageOption == LanguageOption.INSTALL) {
             return R.string.menu_add_to_homescreen_install;
         }
         return R.string.menu_add_to_homescreen;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/CompositorViewHolder.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/CompositorViewHolder.java
index 62a4933..9499106 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/CompositorViewHolder.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/CompositorViewHolder.java
@@ -55,9 +55,9 @@
 import org.chromium.chrome.browser.widget.ControlContainer;
 import org.chromium.content.browser.ContentView;
 import org.chromium.content.browser.ContentViewCore;
-import org.chromium.content.browser.SPenSupport;
 import org.chromium.ui.UiUtils;
 import org.chromium.ui.base.DeviceFormFactor;
+import org.chromium.ui.base.SPenSupport;
 import org.chromium.ui.base.WindowAndroid;
 import org.chromium.ui.resources.ResourceManager;
 import org.chromium.ui.resources.dynamics.DynamicResourceLoader;
@@ -497,11 +497,7 @@
         ContentViewCore contentViewCore = mTabVisible.getContentViewCore();
         if (contentViewCore == null) return;
 
-        int actionMasked = e.getActionMasked();
-
-        if (SPenSupport.isSPenSupported(getContext())) {
-            actionMasked = SPenSupport.convertSPenEventAction(actionMasked);
-        }
+        int actionMasked = SPenSupport.convertSPenEventAction(e.getActionMasked());
 
         if (actionMasked == MotionEvent.ACTION_DOWN
                 || actionMasked == MotionEvent.ACTION_HOVER_ENTER) {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/LayoutManager.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/LayoutManager.java
index ff2d528c..f76fe48 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/LayoutManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/LayoutManager.java
@@ -33,7 +33,7 @@
 import org.chromium.chrome.browser.tabmodel.EmptyTabModelSelectorObserver;
 import org.chromium.chrome.browser.tabmodel.TabCreatorManager;
 import org.chromium.chrome.browser.tabmodel.TabModelSelector;
-import org.chromium.content.browser.SPenSupport;
+import org.chromium.ui.base.SPenSupport;
 import org.chromium.ui.resources.ResourceManager;
 import org.chromium.ui.resources.dynamics.DynamicResourceLoader;
 
@@ -159,10 +159,7 @@
     }
 
     private PointF getMotionOffsets(MotionEvent e) {
-        int actionMasked = e.getActionMasked();
-        if (SPenSupport.isSPenSupported(mHost.getContext())) {
-            actionMasked = SPenSupport.convertSPenEventAction(actionMasked);
-        }
+        int actionMasked = SPenSupport.convertSPenEventAction(e.getActionMasked());
 
         if (actionMasked == MotionEvent.ACTION_DOWN
                 || actionMasked == MotionEvent.ACTION_HOVER_ENTER) {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/engagement/SiteEngagementService.java b/chrome/android/java/src/org/chromium/chrome/browser/engagement/SiteEngagementService.java
index 316aa147..e9ef64d5 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/engagement/SiteEngagementService.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/engagement/SiteEngagementService.java
@@ -43,10 +43,10 @@
      * Sets the provided URL to have the provided engagement score.
      * Must be called on the UI thread.
      */
-    public void resetScoreForUrl(String url, double score) {
+    public void resetBaseScoreForUrl(String url, double score) {
         assert ThreadUtils.runningOnUiThread();
         if (mNativePointer == 0) return;
-        nativeResetScoreForURL(mNativePointer, url, score);
+        nativeResetBaseScoreForURL(mNativePointer, url, score);
     }
 
     @CalledByNative
@@ -67,6 +67,6 @@
     private static native SiteEngagementService nativeSiteEngagementServiceForProfile(
             Profile profile);
     private native double nativeGetScore(long nativeSiteEngagementServiceAndroid, String url);
-    private native void nativeResetScoreForURL(
+    private native void nativeResetBaseScoreForURL(
             long nativeSiteEngagementServiceAndroid, String url, double score);
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/payments/AndroidPaymentAppFactory.java b/chrome/android/java/src/org/chromium/chrome/browser/payments/AndroidPaymentAppFactory.java
index 9eaf457..ac44d119 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/payments/AndroidPaymentAppFactory.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/payments/AndroidPaymentAppFactory.java
@@ -14,6 +14,7 @@
 
 import org.chromium.base.ContextUtils;
 import org.chromium.chrome.browser.ChromeActivity;
+import org.chromium.chrome.browser.ChromeFeatureList;
 import org.chromium.chrome.browser.UrlConstants;
 import org.chromium.chrome.browser.payments.PaymentAppFactory.PaymentAppCreatedCallback;
 import org.chromium.chrome.browser.payments.PaymentAppFactory.PaymentAppFactoryAddition;
@@ -51,6 +52,10 @@
         PackageManager pm = context.getPackageManager();
         Intent payIntent = new Intent();
 
+        boolean paymentAppsFilterEnabled =
+                ChromeFeatureList.isEnabled(ChromeFeatureList.ANDROID_PAYMENT_APPS_FILTER);
+        Intent filterIntent = new Intent(AndroidPaymentApp.ACTION_PAY);
+
         for (String methodName : methods) {
             if (methodName.startsWith(UrlConstants.HTTPS_URL_PREFIX)) {
                 payIntent.setAction(AndroidPaymentApp.ACTION_PAY);
@@ -66,6 +71,10 @@
             for (int i = 0; i < matches.size(); i++) {
                 ResolveInfo match = matches.get(i);
                 String packageName = match.activityInfo.packageName;
+                if (paymentAppsFilterEnabled) {
+                    filterIntent.setPackage(packageName);
+                    if (pm.resolveActivity(filterIntent, 0) == null) continue;
+                }
                 // Do not recommend disabled apps.
                 if (!PaymentPreferencesUtil.isAndroidPaymentAppEnabled(packageName)) continue;
                 AndroidPaymentApp installedApp = installedApps.get(packageName);
diff --git a/chrome/android/java/strings/android_chrome_strings.grd b/chrome/android/java/strings/android_chrome_strings.grd
index e751de3..fdb29a0 100644
--- a/chrome/android/java/strings/android_chrome_strings.grd
+++ b/chrome/android/java/strings/android_chrome_strings.grd
@@ -1607,9 +1607,6 @@
       <message name="IDS_OPEN_WEBAPK_FAILED" desc="Opening of a WebAPK failed. [CHAR-LIMIT=27]">
         Could not launch app
       </message>
-      <message name="IDS_MENU_ADD_TO_HOMESCREEN_ADD" desc="Menu item for adding a shortcut to the Home screen. [CHAR-LIMIT=27]">
-        Add shortcut
-      </message>
       <message name="IDS_MENU_ADD_TO_HOMESCREEN_INSTALL" desc="Menu item for adding a shortcut to the Home screen. [CHAR-LIMIT=27]">
         Install web app
       </message>
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/banners/AppBannerManagerTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/banners/AppBannerManagerTest.java
index 8d57540..2d2cfe5 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/banners/AppBannerManagerTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/banners/AppBannerManagerTest.java
@@ -214,7 +214,7 @@
             @Override
             public void run() {
                 SiteEngagementService.getForProfile(Profile.getLastUsedProfile())
-                        .resetScoreForUrl(url, engagement);
+                        .resetBaseScoreForUrl(url, engagement);
             }
         });
     }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/engagement/SiteEngagementServiceTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/engagement/SiteEngagementServiceTest.java
index 955da594..140f3f72 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/engagement/SiteEngagementServiceTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/engagement/SiteEngagementServiceTest.java
@@ -33,10 +33,10 @@
                 getActivity().getActivityTab().getProfile());
 
         assertEquals(0.0, service.getScore(url));
-        service.resetScoreForUrl(url, 5.0);
+        service.resetBaseScoreForUrl(url, 5.0);
         assertEquals(5.0, service.getScore(url));
 
-        service.resetScoreForUrl(url, 2.0);
+        service.resetBaseScoreForUrl(url, 2.0);
         assertEquals(2.0, service.getScore(url));
     }
 
@@ -51,10 +51,10 @@
         Profile profile = getActivity().getActivityTab().getProfile();
 
         assertEquals(0.0, SiteEngagementService.getForProfile(profile).getScore(url));
-        SiteEngagementService.getForProfile(profile).resetScoreForUrl(url, 5.0);
+        SiteEngagementService.getForProfile(profile).resetBaseScoreForUrl(url, 5.0);
         assertEquals(5.0, SiteEngagementService.getForProfile(profile).getScore(url));
 
-        SiteEngagementService.getForProfile(profile).resetScoreForUrl(url, 2.0);
+        SiteEngagementService.getForProfile(profile).resetBaseScoreForUrl(url, 2.0);
         assertEquals(2.0, SiteEngagementService.getForProfile(profile).getScore(url));
     }
 
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd
index b876e9d..20521d5 100644
--- a/chrome/app/generated_resources.grd
+++ b/chrome/app/generated_resources.grd
@@ -14628,6 +14628,12 @@
       <message name="IDS_FLAGS_ANDROID_PAYMENT_APPS_DESCRIPTION" desc="Description for the flag to enable third party Android payment apps" translateable="false">
         Enable third party Android apps to integrate as payment apps
       </message>
+      <message name="IDS_FLAGS_ANDROID_PAYMENT_APPS_FILTER_TITLE" desc="Title of the flag to enable filtering out third party Android payment apps" translateable="false">
+        Android payment apps filter
+      </message>
+      <message name="IDS_FLAGS_ANDROID_PAYMENT_APPS_FILTER_DESCRIPTION" desc="Description for the flag to enable filtering out third party Android payment apps" translateable="false">
+        Enable filtering out third party Android apps that can not be shown in settings
+      </message>
     </if>
 
     <message name="IDS_FLAGS_FEATURE_POLICY_NAME" desc="Name for the flag to enable feature policy.">
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index 534f72d..5b52f01 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -1576,7 +1576,6 @@
     "//rlz/features",
     "//services/image_decoder/public/cpp",
     "//services/preferences/public/interfaces/",
-    "//services/resource_coordinator:lib",
     "//services/service_manager/public/cpp",
     "//services/shape_detection/public/interfaces",
     "//skia",
@@ -2077,6 +2076,7 @@
     deps += [
       "//chrome/browser/safe_browsing:chunk_proto",
       "//chrome/common/safe_browsing:proto",
+      "//components/safe_browsing:csd_proto",
       "//components/safe_browsing:safe_browsing",
       "//components/safe_browsing/common:common",
       "//components/safe_browsing/password_protection",
@@ -4338,6 +4338,7 @@
     "//components/password_manager/core/browser:test_support",
     "//components/policy/core/browser:test_support",
     "//components/prefs:test_support",
+    "//components/safe_browsing:csd_proto",
     "//components/search_engines:test_support",
     "//components/sessions:test_support",
     "//components/subresource_filter/core/browser:test_support",
diff --git a/chrome/browser/DEPS b/chrome/browser/DEPS
index 449db42..3b311d4 100644
--- a/chrome/browser/DEPS
+++ b/chrome/browser/DEPS
@@ -50,7 +50,6 @@
   "+services/image_decoder/public/interfaces",
   "+services/preferences/public/cpp",
   "+services/preferences/public/interfaces",
-  "+services/resource_coordinator",
   "+services/service_manager",
   "+services/shape_detection/public/interfaces",
   "+services/ui/public",
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index 372f8fd..0c03ffd 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -2074,6 +2074,9 @@
     {"android-payment-apps", IDS_FLAGS_ANDROID_PAYMENT_APPS_NAME,
      IDS_FLAGS_ANDROID_PAYMENT_APPS_DESCRIPTION, kOsAndroid,
      FEATURE_VALUE_TYPE(chrome::android::kAndroidPaymentApps)},
+    {"android-payment-apps-filter", IDS_FLAGS_ANDROID_PAYMENT_APPS_FILTER_TITLE,
+     IDS_FLAGS_ANDROID_PAYMENT_APPS_FILTER_DESCRIPTION, kOsAndroid,
+     FEATURE_VALUE_TYPE(chrome::android::kAndroidPaymentAppsFilter)},
 #endif  // OS_ANDROID
 #if defined(OS_CHROMEOS)
     {"disable-eol-notification", IDS_FLAGS_EOL_NOTIFICATION_NAME,
diff --git a/chrome/browser/android/chrome_feature_list.cc b/chrome/browser/android/chrome_feature_list.cc
index 867ed096..43eaf42 100644
--- a/chrome/browser/android/chrome_feature_list.cc
+++ b/chrome/browser/android/chrome_feature_list.cc
@@ -45,6 +45,7 @@
     &kAndroidPayIntegrationV1,
     &kAndroidPayIntegrationV2,
     &kAndroidPaymentApps,
+    &kAndroidPaymentAppsFilter,
     &kCCTExternalLinkHandling,
     &kCCTPostMessageAPI,
     &kChromeHomeFeature,
@@ -101,6 +102,9 @@
 const base::Feature kAndroidPaymentApps{"AndroidPaymentApps",
                                         base::FEATURE_DISABLED_BY_DEFAULT};
 
+const base::Feature kAndroidPaymentAppsFilter{
+    "AndroidPaymentAppsFilter", base::FEATURE_DISABLED_BY_DEFAULT};
+
 const base::Feature kCCTExternalLinkHandling{"CCTExternalLinkHandling",
                                              base::FEATURE_ENABLED_BY_DEFAULT};
 
diff --git a/chrome/browser/android/chrome_feature_list.h b/chrome/browser/android/chrome_feature_list.h
index 2d83a24c..35e667e 100644
--- a/chrome/browser/android/chrome_feature_list.h
+++ b/chrome/browser/android/chrome_feature_list.h
@@ -16,6 +16,7 @@
 extern const base::Feature kAndroidPayIntegrationV1;
 extern const base::Feature kAndroidPayIntegrationV2;
 extern const base::Feature kAndroidPaymentApps;
+extern const base::Feature kAndroidPaymentAppsFilter;
 extern const base::Feature kCCTExternalLinkHandling;
 extern const base::Feature kCCTPostMessageAPI;
 extern const base::Feature kChromeHomeFeature;
diff --git a/chrome/browser/android/metrics/OWNERS b/chrome/browser/android/metrics/OWNERS
index 7755f6b..52aa22b 100644
--- a/chrome/browser/android/metrics/OWNERS
+++ b/chrome/browser/android/metrics/OWNERS
@@ -2,3 +2,4 @@
 dfalcantara@chromium.org
 mariakhomenko@chromium.org
 
+# COMPONENT: Internals>Metrics
diff --git a/chrome/browser/android/tab_android.cc b/chrome/browser/android/tab_android.cc
index 93d072b..45756ff8 100644
--- a/chrome/browser/android/tab_android.cc
+++ b/chrome/browser/android/tab_android.cc
@@ -655,6 +655,7 @@
                                    const JavaParamRef<jobject>& obj) {
   content::RenderFrameHost* render_frame_host =
       web_contents()->GetFocusedFrame();
+  // TODO(nigeltao): convert this to Mojo.
   render_frame_host->Send(new ChromeViewMsg_RequestReloadImageForContextNode(
       render_frame_host->GetRoutingID()));
 }
diff --git a/chrome/browser/apps/guest_view/web_view_browsertest.cc b/chrome/browser/apps/guest_view/web_view_browsertest.cc
index afe2f6d0..05b19b2 100644
--- a/chrome/browser/apps/guest_view/web_view_browsertest.cc
+++ b/chrome/browser/apps/guest_view/web_view_browsertest.cc
@@ -63,6 +63,7 @@
 #include "content/public/test/browser_test_utils.h"
 #include "content/public/test/download_test_observer.h"
 #include "content/public/test/fake_speech_recognition_manager.h"
+#include "content/public/test/test_navigation_observer.h"
 #include "content/public/test/test_renderer_host.h"
 #include "content/public/test/test_utils.h"
 #include "extensions/browser/api/declarative/rules_registry.h"
@@ -3232,6 +3233,40 @@
             mime_handler_view_container_bounds.origin());
 }
 
+// Test that context menu Back/Forward items in a MimeHandlerViewGuest affect
+// the embedder WebContents. See crbug.com/587355.
+IN_PROC_BROWSER_TEST_P(WebViewTest, ContextMenuNavigationInMimeHandlerView) {
+  TestHelper("testNavigateToPDFInWebview", "web_view/shim", NO_TEST_SERVER);
+
+  std::vector<content::WebContents*> guest_web_contents_list;
+  GetGuestViewManager()->WaitForNumGuestsCreated(2u);
+  GetGuestViewManager()->GetGuestWebContentsList(&guest_web_contents_list);
+  ASSERT_EQ(2u, guest_web_contents_list.size());
+
+  content::WebContents* web_view_contents = guest_web_contents_list[0];
+  content::WebContents* mime_handler_view_contents = guest_web_contents_list[1];
+  ASSERT_TRUE(pdf_extension_test_util::EnsurePDFHasLoaded(web_view_contents));
+
+  // Ensure the <webview> has a previous entry, so we can navigate back to it.
+  ASSERT_TRUE(web_view_contents->GetController().CanGoBack());
+
+  // Open a context menu for the MimeHandlerViewGuest. Since the <webview> can
+  // navigate back, the Back item should be enabled.
+  content::ContextMenuParams params;
+  TestRenderViewContextMenu menu(mime_handler_view_contents->GetMainFrame(),
+                                 params);
+  menu.Init();
+  ASSERT_TRUE(menu.IsCommandIdEnabled(IDC_BACK));
+
+  // Verify that the Back item causes the <webview> to navigate back to the
+  // previous entry.
+  content::TestNavigationObserver observer(web_view_contents);
+  menu.ExecuteCommand(IDC_BACK, 0);
+  observer.Wait();
+  EXPECT_EQ(GURL(url::kAboutBlankURL),
+            web_view_contents->GetLastCommittedURL());
+}
+
 IN_PROC_BROWSER_TEST_P(WebViewTest, Shim_TestMailtoLink) {
   TestHelper("testMailtoLink", "web_view/shim", NEEDS_TEST_SERVER);
 }
diff --git a/chrome/browser/background/background_application_list_model_unittest.cc b/chrome/browser/background/background_application_list_model_unittest.cc
index e1af42d..1de77cae 100644
--- a/chrome/browser/background/background_application_list_model_unittest.cc
+++ b/chrome/browser/background/background_application_list_model_unittest.cc
@@ -89,12 +89,9 @@
   std::string error;
   scoped_refptr<Extension> extension;
 
-  extension = Extension::Create(
-      bogus_file_pathname(name),
-      extensions::Manifest::INVALID_LOCATION,
-      manifest,
-      Extension::NO_FLAGS,
-      &error);
+  extension = Extension::Create(bogus_file_pathname(name),
+                                extensions::Manifest::INTERNAL, manifest,
+                                Extension::NO_FLAGS, &error);
 
   // Cannot ASSERT_* here because that attempts an illegitimate return.
   // Cannot EXPECT_NE here because that assumes non-pointers unlike EXPECT_EQ
diff --git a/chrome/browser/banners/app_banner_manager_browsertest.cc b/chrome/browser/banners/app_banner_manager_browsertest.cc
index d61d0b17..12148de1 100644
--- a/chrome/browser/banners/app_banner_manager_browsertest.cc
+++ b/chrome/browser/banners/app_banner_manager_browsertest.cc
@@ -143,7 +143,7 @@
         histograms.ExpectTotalCount(banners::kInstallableStatusCodeHistogram,
                                     0);
       }
-      service->ResetScoreForURL(test_url, engagement);
+      service->ResetBaseScoreForURL(test_url, engagement);
       ++iterations;
     }
 
diff --git a/chrome/browser/banners/app_banner_settings_helper_unittest.cc b/chrome/browser/banners/app_banner_settings_helper_unittest.cc
index a4035e9..84d1a03 100644
--- a/chrome/browser/banners/app_banner_settings_helper_unittest.cc
+++ b/chrome/browser/banners/app_banner_settings_helper_unittest.cc
@@ -92,12 +92,12 @@
       AppBannerSettingsHelper::HasSufficientEngagement(service->GetScore(url)));
 
   // Add 1 engagement, it still should not be shown.
-  service->ResetScoreForURL(url, 1);
+  service->ResetBaseScoreForURL(url, 1);
   EXPECT_FALSE(
       AppBannerSettingsHelper::HasSufficientEngagement(service->GetScore(url)));
 
   // Add 1 more engagement; now it should be shown.
-  service->ResetScoreForURL(url, 2);
+  service->ResetBaseScoreForURL(url, 2);
   EXPECT_TRUE(
       AppBannerSettingsHelper::HasSufficientEngagement(service->GetScore(url)));
 }
@@ -116,7 +116,7 @@
       AppBannerSettingsHelper::HasSufficientEngagement(service->GetScore(url)));
 
   // Add engagement such that the banner should show.
-  service->ResetScoreForURL(url, 4);
+  service->ResetBaseScoreForURL(url, 4);
   EXPECT_TRUE(
       AppBannerSettingsHelper::HasSufficientEngagement(service->GetScore(url)));
   EXPECT_EQ(NO_ERROR_DETECTED,
@@ -161,7 +161,7 @@
       AppBannerSettingsHelper::HasSufficientEngagement(service->GetScore(url)));
 
   // Add engagement such that the banner should show.
-  service->ResetScoreForURL(url, 4);
+  service->ResetBaseScoreForURL(url, 4);
   EXPECT_TRUE(
       AppBannerSettingsHelper::HasSufficientEngagement(service->GetScore(url)));
   EXPECT_EQ(NO_ERROR_DETECTED,
@@ -204,7 +204,7 @@
       AppBannerSettingsHelper::HasSufficientEngagement(service->GetScore(url)));
 
   // Add engagement such that the banner should show.
-  service->ResetScoreForURL(url, 4);
+  service->ResetBaseScoreForURL(url, 4);
   EXPECT_TRUE(
       AppBannerSettingsHelper::HasSufficientEngagement(service->GetScore(url)));
   EXPECT_EQ(NO_ERROR_DETECTED,
@@ -230,7 +230,7 @@
       AppBannerSettingsHelper::HasSufficientEngagement(service->GetScore(url)));
 
   // Add engagement such that the banner should show.
-  service->ResetScoreForURL(url, 4);
+  service->ResetBaseScoreForURL(url, 4);
   EXPECT_TRUE(
       AppBannerSettingsHelper::HasSufficientEngagement(service->GetScore(url)));
 
@@ -255,23 +255,23 @@
       AppBannerSettingsHelper::HasSufficientEngagement(service->GetScore(url)));
 
   // Add engagement such that the banner should show.
-  service->ResetScoreForURL(url, 2);
+  service->ResetBaseScoreForURL(url, 2);
   EXPECT_FALSE(
       AppBannerSettingsHelper::HasSufficientEngagement(service->GetScore(url)));
 
-  service->ResetScoreForURL(url, 4);
+  service->ResetBaseScoreForURL(url, 4);
   EXPECT_FALSE(
       AppBannerSettingsHelper::HasSufficientEngagement(service->GetScore(url)));
 
-  service->ResetScoreForURL(url, 6);
+  service->ResetBaseScoreForURL(url, 6);
   EXPECT_FALSE(
       AppBannerSettingsHelper::HasSufficientEngagement(service->GetScore(url)));
 
-  service->ResetScoreForURL(url, 8);
+  service->ResetBaseScoreForURL(url, 8);
   EXPECT_FALSE(
       AppBannerSettingsHelper::HasSufficientEngagement(service->GetScore(url)));
 
-  service->ResetScoreForURL(url, 10);
+  service->ResetBaseScoreForURL(url, 10);
   EXPECT_TRUE(
       AppBannerSettingsHelper::HasSufficientEngagement(service->GetScore(url)));
 
diff --git a/chrome/browser/budget_service/budget_database_unittest.cc b/chrome/browser/budget_service/budget_database_unittest.cc
index 1f942b78..fc2752f 100644
--- a/chrome/browser/budget_service/budget_database_unittest.cc
+++ b/chrome/browser/budget_service/budget_database_unittest.cc
@@ -92,7 +92,7 @@
 
   void SetSiteEngagementScore(double score) {
     SiteEngagementService* service = SiteEngagementService::Get(&profile_);
-    service->ResetScoreForURL(GURL(kTestOrigin), score);
+    service->ResetBaseScoreForURL(GURL(kTestOrigin), score);
   }
 
  protected:
diff --git a/chrome/browser/budget_service/budget_manager_browsertest.cc b/chrome/browser/budget_service/budget_manager_browsertest.cc
index 299abe21..65e56f76 100644
--- a/chrome/browser/budget_service/budget_manager_browsertest.cc
+++ b/chrome/browser/budget_service/budget_manager_browsertest.cc
@@ -60,7 +60,7 @@
   void SetSiteEngagementScore(double score) {
     SiteEngagementService* service =
         SiteEngagementService::Get(browser()->profile());
-    service->ResetScoreForURL(https_server_->GetURL(kTestURL), score);
+    service->ResetBaseScoreForURL(https_server_->GetURL(kTestURL), score);
   }
 
   bool RunScript(const std::string& script, std::string* result) {
diff --git a/chrome/browser/budget_service/budget_manager_unittest.cc b/chrome/browser/budget_service/budget_manager_unittest.cc
index ab0802f..f891b36 100644
--- a/chrome/browser/budget_service/budget_manager_unittest.cc
+++ b/chrome/browser/budget_service/budget_manager_unittest.cc
@@ -38,7 +38,7 @@
 
   void SetSiteEngagementScore(double score) {
     SiteEngagementService* service = SiteEngagementService::Get(&profile_);
-    service->ResetScoreForURL(GURL(origin().Serialize()), score);
+    service->ResetBaseScoreForURL(GURL(origin().Serialize()), score);
   }
 
   Profile* profile() { return &profile_; }
diff --git a/chrome/browser/chrome_content_browser_client.cc b/chrome/browser/chrome_content_browser_client.cc
index 0992952..aa0fee8 100644
--- a/chrome/browser/chrome_content_browser_client.cc
+++ b/chrome/browser/chrome_content_browser_client.cc
@@ -197,7 +197,6 @@
 #include "printing/features/features.h"
 #include "services/image_decoder/public/interfaces/constants.mojom.h"
 #include "services/preferences/public/interfaces/preferences.mojom.h"
-#include "services/resource_coordinator/memory/coordinator/coordinator_impl.h"
 #include "services/service_manager/public/cpp/interface_provider.h"
 #include "services/service_manager/public/cpp/interface_registry.h"
 #include "services/service_manager/public/cpp/service.h"
@@ -3105,13 +3104,6 @@
       base::Bind(&metrics::LeakDetectorRemoteController::Create),
       ui_task_runner);
 #endif
-
-  registry->AddInterface(
-      base::Bind(
-          &memory_instrumentation::CoordinatorImpl::BindCoordinatorRequest,
-          base::Unretained(
-              memory_instrumentation::CoordinatorImpl::GetInstance())),
-      ui_task_runner);
 }
 
 void ChromeContentBrowserClient::ExposeInterfacesToMediaService(
@@ -3209,15 +3201,6 @@
   registry->AddInterface(
       base::Bind(&metrics::CallStackProfileCollector::Create,
                  metrics::CallStackProfileParams::GPU_PROCESS));
-
-  auto ui_task_runner = content::BrowserThread::GetTaskRunnerForThread(
-      content::BrowserThread::UI);
-  registry->AddInterface(
-      base::Bind(
-          &memory_instrumentation::CoordinatorImpl::BindCoordinatorRequest,
-          base::Unretained(
-              memory_instrumentation::CoordinatorImpl::GetInstance())),
-      ui_task_runner);
 }
 
 void ChromeContentBrowserClient::RegisterInProcessServices(
diff --git a/chrome/browser/chrome_content_renderer_manifest_overlay.json b/chrome/browser/chrome_content_renderer_manifest_overlay.json
index e995d7b..af721e5 100644
--- a/chrome/browser/chrome_content_renderer_manifest_overlay.json
+++ b/chrome/browser/chrome_content_renderer_manifest_overlay.json
@@ -14,6 +14,7 @@
           "autofill::mojom::AutofillAgent",
           "autofill::mojom::PasswordAutofillAgent",
           "autofill::mojom::PasswordGenerationAgent",
+          "chrome::mojom::ImageContextMenuRenderer",
           "chrome::mojom::InsecureContentRenderer",
           "contextual_search::mojom::OverlayPageNotifierService",
           "dom_distiller::mojom::DistillerPageNotifierService"
diff --git a/chrome/browser/chromeos/BUILD.gn b/chrome/browser/chromeos/BUILD.gn
index 26d6c02..2086fd8 100644
--- a/chrome/browser/chromeos/BUILD.gn
+++ b/chrome/browser/chromeos/BUILD.gn
@@ -69,6 +69,7 @@
     "//components/pairing",
     "//components/policy:generated",
     "//components/proxy_config",
+    "//components/safe_browsing:csd_proto",
     "//components/safe_browsing_db:metadata_proto",
     "//components/session_manager/core",
     "//components/sync_wifi",
diff --git a/chrome/browser/chromeos/accessibility/touch_exploration_controller_browsertest.cc b/chrome/browser/chromeos/accessibility/touch_exploration_controller_browsertest.cc
index cb42992..6a743a6 100644
--- a/chrome/browser/chromeos/accessibility/touch_exploration_controller_browsertest.cc
+++ b/chrome/browser/chromeos/accessibility/touch_exploration_controller_browsertest.cc
@@ -58,7 +58,7 @@
 
   void SwitchTouchExplorationMode(bool on) {
     ash::AccessibilityDelegate* delegate =
-        ash::WmShell::Get()->accessibility_delegate();
+        ash::Shell::GetInstance()->accessibility_delegate();
     if (on != delegate->IsSpokenFeedbackEnabled())
       delegate->ToggleSpokenFeedback(ash::A11Y_NOTIFICATION_NONE);
   }
diff --git a/chrome/browser/chromeos/arc/video/gpu_arc_video_service_host.cc b/chrome/browser/chromeos/arc/video/gpu_arc_video_service_host.cc
index 203765d..abefb91 100644
--- a/chrome/browser/chromeos/arc/video/gpu_arc_video_service_host.cc
+++ b/chrome/browser/chromeos/arc/video/gpu_arc_video_service_host.cc
@@ -76,7 +76,8 @@
       base::kNullProcessHandle;
   mojo::edk::PendingProcessConnection process;
   mojo::edk::PlatformChannelPair channel_pair;
-  process.Connect(kUnusedChildProcessHandle, channel_pair.PassServerHandle());
+  process.Connect(kUnusedChildProcessHandle,
+                  mojo::edk::ConnectionParams(channel_pair.PassServerHandle()));
 
   MojoHandle wrapped_handle;
   MojoResult wrap_result = mojo::edk::CreatePlatformHandleWrapper(
diff --git a/chrome/browser/chromeos/arc/wallpaper/arc_wallpaper_service.cc b/chrome/browser/chromeos/arc/wallpaper/arc_wallpaper_service.cc
index 9cb023b..2bcb3aa 100644
--- a/chrome/browser/chromeos/arc/wallpaper/arc_wallpaper_service.cc
+++ b/chrome/browser/chromeos/arc/wallpaper/arc_wallpaper_service.cc
@@ -5,7 +5,7 @@
 #include "chrome/browser/chromeos/arc/wallpaper/arc_wallpaper_service.h"
 
 #include "ash/common/wallpaper/wallpaper_controller.h"
-#include "ash/common/wm_shell.h"
+#include "ash/shell.h"
 #include "base/logging.h"
 #include "base/memory/ptr_util.h"
 #include "base/task_scheduler/post_task.h"
@@ -67,10 +67,9 @@
 }
 
 ash::WallpaperController* GetWallpaperController() {
-  ash::WmShell* wm_shell = ash::WmShell::Get();
-  if (!wm_shell)
+  if (!ash::Shell::HasInstance())
     return nullptr;
-  return wm_shell->wallpaper_controller();
+  return ash::Shell::GetInstance()->wallpaper_controller();
 }
 
 }  // namespace
@@ -97,7 +96,7 @@
       ARC_GET_INSTANCE_FOR_METHOD(arc_bridge_service()->wallpaper(), Init);
   DCHECK(wallpaper_instance);
   wallpaper_instance->Init(binding_.CreateInterfacePtrAndBind());
-  ash::WmShell::Get()->wallpaper_controller()->AddObserver(this);
+  ash::Shell::GetInstance()->wallpaper_controller()->AddObserver(this);
 }
 
 void ArcWallpaperService::OnInstanceClosed() {
@@ -116,7 +115,8 @@
 
 void ArcWallpaperService::GetWallpaper(const GetWallpaperCallback& callback) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-  ash::WallpaperController* wc = ash::WmShell::Get()->wallpaper_controller();
+  ash::WallpaperController* wc =
+      ash::Shell::GetInstance()->wallpaper_controller();
   gfx::ImageSkia wallpaper = wc->GetWallpaper();
   base::PostTaskWithTraitsAndReplyWithResult(
       FROM_HERE, base::TaskTraits().MayBlock().WithPriority(
diff --git a/chrome/browser/chromeos/customization/customization_wallpaper_downloader_browsertest.cc b/chrome/browser/chromeos/customization/customization_wallpaper_downloader_browsertest.cc
index 63becc1c..92da2c0a 100644
--- a/chrome/browser/chromeos/customization/customization_wallpaper_downloader_browsertest.cc
+++ b/chrome/browser/chromeos/customization/customization_wallpaper_downloader_browsertest.cc
@@ -7,7 +7,7 @@
 #include <vector>
 
 #include "ash/common/wallpaper/wallpaper_controller.h"
-#include "ash/common/wm_shell.h"
+#include "ash/shell.h"
 #include "base/command_line.h"
 #include "base/files/scoped_temp_dir.h"
 #include "base/macros.h"
@@ -250,7 +250,7 @@
   WallpaperManager::Get()->SetDefaultWallpaperNow(EmptyAccountId());
   wallpaper_manager_test_utils::WaitAsyncWallpaperLoadFinished();
   EXPECT_TRUE(wallpaper_manager_test_utils::ImageIsNearColor(
-      ash::WmShell::Get()->wallpaper_controller()->GetWallpaper(),
+      ash::Shell::GetInstance()->wallpaper_controller()->GetWallpaper(),
       wallpaper_manager_test_utils::kSmallDefaultWallpaperColor));
 
   WallpaperImageFetcherFactory url_factory(
@@ -268,7 +268,7 @@
 
   observer.WaitForWallpaperAnimationFinished();
   EXPECT_TRUE(wallpaper_manager_test_utils::ImageIsNearColor(
-      ash::WmShell::Get()->wallpaper_controller()->GetWallpaper(),
+      ash::Shell::GetInstance()->wallpaper_controller()->GetWallpaper(),
       wallpaper_manager_test_utils::kCustomWallpaperColor));
   EXPECT_EQ(1U, url_factory.num_attempts());
 }
@@ -279,7 +279,7 @@
   WallpaperManager::Get()->SetDefaultWallpaperNow(EmptyAccountId());
   wallpaper_manager_test_utils::WaitAsyncWallpaperLoadFinished();
   EXPECT_TRUE(wallpaper_manager_test_utils::ImageIsNearColor(
-      ash::WmShell::Get()->wallpaper_controller()->GetWallpaper(),
+      ash::Shell::GetInstance()->wallpaper_controller()->GetWallpaper(),
       wallpaper_manager_test_utils::kSmallDefaultWallpaperColor));
 
   WallpaperImageFetcherFactory url_factory(
@@ -297,7 +297,7 @@
 
   observer.WaitForWallpaperAnimationFinished();
   EXPECT_TRUE(wallpaper_manager_test_utils::ImageIsNearColor(
-      ash::WmShell::Get()->wallpaper_controller()->GetWallpaper(),
+      ash::Shell::GetInstance()->wallpaper_controller()->GetWallpaper(),
       wallpaper_manager_test_utils::kCustomWallpaperColor));
 
   EXPECT_EQ(2U, url_factory.num_attempts());
diff --git a/chrome/browser/chromeos/login/kiosk_browsertest.cc b/chrome/browser/chromeos/login/kiosk_browsertest.cc
index 6db4b38..5d95dd6 100644
--- a/chrome/browser/chromeos/login/kiosk_browsertest.cc
+++ b/chrome/browser/chromeos/login/kiosk_browsertest.cc
@@ -8,7 +8,7 @@
 #include "apps/test/app_window_waiter.h"
 #include "ash/common/wallpaper/wallpaper_controller.h"
 #include "ash/common/wallpaper/wallpaper_controller_observer.h"
-#include "ash/common/wm_shell.h"
+#include "ash/shell.h"
 #include "base/bind.h"
 #include "base/bind_helpers.h"
 #include "base/location.h"
@@ -2329,11 +2329,11 @@
     LoginDisplayHostImpl::DisableRestrictiveProxyCheckForTest();
 
     KioskTest::SetUpOnMainThread();
-    ash::WmShell::Get()->wallpaper_controller()->AddObserver(this);
+    ash::Shell::GetInstance()->wallpaper_controller()->AddObserver(this);
   }
 
   void TearDownOnMainThread() override {
-    ash::WmShell::Get()->wallpaper_controller()->RemoveObserver(this);
+    ash::Shell::GetInstance()->wallpaper_controller()->RemoveObserver(this);
     KioskTest::TearDownOnMainThread();
   }
 
diff --git a/chrome/browser/chromeos/login/lock/screen_locker.cc b/chrome/browser/chromeos/login/lock/screen_locker.cc
index 9e6b0fc..b7011cc4d 100644
--- a/chrome/browser/chromeos/login/lock/screen_locker.cc
+++ b/chrome/browser/chromeos/login/lock/screen_locker.cc
@@ -504,7 +504,7 @@
   ClearErrors();
 
   VLOG(1) << "Moving wallpaper to unlocked container";
-  ash::WmShell::Get()->wallpaper_controller()->MoveToUnlockedContainer();
+  ash::Shell::GetInstance()->wallpaper_controller()->MoveToUnlockedContainer();
 
   screen_locker_ = NULL;
   bool state = false;
@@ -538,7 +538,7 @@
   UMA_HISTOGRAM_TIMES("ScreenLocker.ScreenLockTime", delta);
 
   VLOG(1) << "Moving wallpaper to locked container";
-  ash::WmShell::Get()->wallpaper_controller()->MoveToLockedContainer();
+  ash::Shell::GetInstance()->wallpaper_controller()->MoveToLockedContainer();
 
   bool state = true;
   VLOG(1) << "Emitting SCREEN_LOCK_STATE_CHANGED with state=" << state;
diff --git a/chrome/browser/chromeos/login/supervised/supervised_user_creation_screen.cc b/chrome/browser/chromeos/login/supervised/supervised_user_creation_screen.cc
index 5ef2acb..aabc0ae 100644
--- a/chrome/browser/chromeos/login/supervised/supervised_user_creation_screen.cc
+++ b/chrome/browser/chromeos/login/supervised/supervised_user_creation_screen.cc
@@ -7,7 +7,7 @@
 #include <utility>
 
 #include "ash/common/wallpaper/wallpaper_controller.h"
-#include "ash/common/wm_shell.h"
+#include "ash/shell.h"
 #include "base/memory/ptr_util.h"
 #include "base/rand_util.h"
 #include "base/values.h"
@@ -365,7 +365,7 @@
   DCHECK(controller_.get());
   // For manager user, move wallpaper to locked container so that windows
   // created during the user image picker step are below it.
-  ash::WmShell::Get()->wallpaper_controller()->MoveToLockedContainer();
+  ash::Shell::GetInstance()->wallpaper_controller()->MoveToLockedContainer();
 
   controller_->SetManagerProfile(manager_profile);
   if (view_)
diff --git a/chrome/browser/chromeos/login/ui/login_display_host_impl.cc b/chrome/browser/chromeos/login/ui/login_display_host_impl.cc
index 7661afc..cfa9e34d 100644
--- a/chrome/browser/chromeos/login/ui/login_display_host_impl.cc
+++ b/chrome/browser/chromeos/login/ui/login_display_host_impl.cc
@@ -561,7 +561,9 @@
   // See crbug.com/541864.
   if (ash::WmShell::HasInstance() &&
       finalize_animation_type_ != ANIMATION_ADD_USER) {
-    ash::WmShell::Get()->wallpaper_controller()->MoveToUnlockedContainer();
+    ash::Shell::GetInstance()
+        ->wallpaper_controller()
+        ->MoveToUnlockedContainer();
   }
 
   switch (finalize_animation_type_) {
@@ -685,7 +687,7 @@
         ash::kShellWindowId_LockScreenContainersContainer);
     lock_container->layer()->SetOpacity(1.0);
 
-    ash::WmShell::Get()->wallpaper_controller()->MoveToLockedContainer();
+    ash::Shell::GetInstance()->wallpaper_controller()->MoveToLockedContainer();
   } else {
     NOTIMPLEMENTED();
   }
@@ -948,7 +950,9 @@
     if (!ash_util::IsRunningInMash()) {
       // For new user, move wallpaper to lock container so that windows created
       // during the user image picker step are below it.
-      ash::WmShell::Get()->wallpaper_controller()->MoveToLockedContainer();
+      ash::Shell::GetInstance()
+          ->wallpaper_controller()
+          ->MoveToLockedContainer();
     } else {
       NOTIMPLEMENTED();
     }
@@ -1118,7 +1122,9 @@
   if (ash::Shell::HasInstance() &&
       finalize_animation_type_ == ANIMATION_ADD_USER) {
     if (!ash_util::IsRunningInMash()) {
-      ash::WmShell::Get()->wallpaper_controller()->MoveToUnlockedContainer();
+      ash::Shell::GetInstance()
+          ->wallpaper_controller()
+          ->MoveToUnlockedContainer();
     } else {
       NOTIMPLEMENTED();
     }
diff --git a/chrome/browser/chromeos/login/users/wallpaper/wallpaper_manager.cc b/chrome/browser/chromeos/login/users/wallpaper/wallpaper_manager.cc
index 78bbda38..823b4604 100644
--- a/chrome/browser/chromeos/login/users/wallpaper/wallpaper_manager.cc
+++ b/chrome/browser/chromeos/login/users/wallpaper/wallpaper_manager.cc
@@ -8,8 +8,8 @@
 
 #include "ash/common/ash_constants.h"
 #include "ash/common/wallpaper/wallpaper_controller.h"
-#include "ash/common/wm_shell.h"
 #include "ash/public/interfaces/constants.mojom.h"
+#include "ash/shell.h"
 #include "base/bind.h"
 #include "base/bind_helpers.h"
 #include "base/command_line.h"
@@ -210,11 +210,11 @@
     // TODO(crbug.com/655875): Optimize ash wallpaper transport; avoid sending
     // large bitmaps over Mojo; use shared memory like BitmapUploader, etc.
     wallpaper_controller->SetWallpaper(*image.bitmap(), layout);
-  } else if (ash::WmShell::HasInstance()) {
+  } else if (ash::Shell::HasInstance()) {
     // Note: Wallpaper setting is skipped in unit tests without shell instances.
     // In classic ash, interact with the WallpaperController class directly.
-    ash::WmShell::Get()->wallpaper_controller()->SetWallpaperImage(image,
-                                                                   layout);
+    ash::Shell::GetInstance()->wallpaper_controller()->SetWallpaperImage(
+        image, layout);
   }
 }
 
@@ -473,7 +473,7 @@
   // Zero delays is also set in autotests.
   if (WizardController::IsZeroDelayEnabled()) {
     // Ensure tests have some sort of wallpaper.
-    ash::WmShell::Get()->wallpaper_controller()->CreateEmptyWallpaper();
+    ash::Shell::GetInstance()->wallpaper_controller()->CreateEmptyWallpaper();
     return;
   }
 
@@ -1369,7 +1369,7 @@
   default_large_wallpaper_file_ = default_large_wallpaper_file;
 
   ash::WallpaperController* controller =
-      ash::WmShell::Get()->wallpaper_controller();
+      ash::Shell::GetInstance()->wallpaper_controller();
 
   // |need_update_screen| is true if the previous default wallpaper is visible
   // now, so we need to update wallpaper on the screen.
diff --git a/chrome/browser/chromeos/login/users/wallpaper/wallpaper_manager_browsertest.cc b/chrome/browser/chromeos/login/users/wallpaper/wallpaper_manager_browsertest.cc
index 11bf9fd..2609a93 100644
--- a/chrome/browser/chromeos/login/users/wallpaper/wallpaper_manager_browsertest.cc
+++ b/chrome/browser/chromeos/login/users/wallpaper/wallpaper_manager_browsertest.cc
@@ -71,7 +71,7 @@
   ~WallpaperManagerBrowserTest() override {}
 
   void SetUpOnMainThread() override {
-    controller_ = ash::WmShell::Get()->wallpaper_controller();
+    controller_ = ash::Shell::GetInstance()->wallpaper_controller();
     controller_->set_wallpaper_reload_delay_for_test(0);
     local_state_ = g_browser_process->local_state();
     UpdateDisplay("800x600");
diff --git a/chrome/browser/chromeos/login/users/wallpaper/wallpaper_manager_policy_browsertest.cc b/chrome/browser/chromeos/login/users/wallpaper/wallpaper_manager_policy_browsertest.cc
index 17b8e64..6e9746fb 100644
--- a/chrome/browser/chromeos/login/users/wallpaper/wallpaper_manager_policy_browsertest.cc
+++ b/chrome/browser/chromeos/login/users/wallpaper/wallpaper_manager_policy_browsertest.cc
@@ -10,7 +10,7 @@
 
 #include "ash/common/wallpaper/wallpaper_controller.h"
 #include "ash/common/wallpaper/wallpaper_controller_observer.h"
-#include "ash/common/wm_shell.h"
+#include "ash/shell.h"
 #include "base/command_line.h"
 #include "base/compiler_specific.h"
 #include "base/files/file_path.h"
@@ -123,7 +123,7 @@
 // Obtain wallpaper image and return its average ARGB color.
 SkColor GetAverageWallpaperColor() {
   const gfx::ImageSkia image =
-      ash::WmShell::Get()->wallpaper_controller()->GetWallpaper();
+      ash::Shell::GetInstance()->wallpaper_controller()->GetWallpaper();
 
   const gfx::ImageSkiaRep& representation = image.GetRepresentation(1.);
   if (representation.is_null()) {
@@ -219,7 +219,7 @@
 
   void SetUpOnMainThread() override {
     LoginManagerTest::SetUpOnMainThread();
-    ash::WmShell::Get()->wallpaper_controller()->AddObserver(this);
+    ash::Shell::GetInstance()->wallpaper_controller()->AddObserver(this);
 
     // Set up policy signing.
     user_policy_builders_[0] = GetUserPolicyBuilder(testUsers_[0]);
@@ -227,7 +227,7 @@
   }
 
   void TearDownOnMainThread() override {
-    ash::WmShell::Get()->wallpaper_controller()->RemoveObserver(this);
+    ash::Shell::GetInstance()->wallpaper_controller()->RemoveObserver(this);
     LoginManagerTest::TearDownOnMainThread();
   }
 
diff --git a/chrome/browser/download/DEPS b/chrome/browser/download/DEPS
index 55e3579..dba1ce6 100644
--- a/chrome/browser/download/DEPS
+++ b/chrome/browser/download/DEPS
@@ -1,3 +1,4 @@
 include_rules = [
   "+components/drive/drive_pref_names.h",
+  "+components/safe_browsing/csd.pb.h",
 ]
diff --git a/chrome/browser/download/download_browsertest.cc b/chrome/browser/download/download_browsertest.cc
index b4691f4..a675f43c 100644
--- a/chrome/browser/download/download_browsertest.cc
+++ b/chrome/browser/download/download_browsertest.cc
@@ -62,7 +62,6 @@
 #include "chrome/common/chrome_paths.h"
 #include "chrome/common/chrome_switches.h"
 #include "chrome/common/pref_names.h"
-#include "chrome/common/safe_browsing/csd.pb.h"
 #include "chrome/common/safe_browsing/download_file_types.pb.h"
 #include "chrome/common/url_constants.h"
 #include "chrome/grit/generated_resources.h"
@@ -75,6 +74,7 @@
 #include "components/infobars/core/confirm_infobar_delegate.h"
 #include "components/infobars/core/infobar.h"
 #include "components/prefs/pref_service.h"
+#include "components/safe_browsing/csd.pb.h"
 #include "components/safe_browsing_db/safe_browsing_prefs.h"
 #include "content/public/browser/download_danger_type.h"
 #include "content/public/browser/download_interrupt_reasons.h"
diff --git a/chrome/browser/download/download_commands.cc b/chrome/browser/download/download_commands.cc
index c09d6ad..62ebc70a 100644
--- a/chrome/browser/download/download_commands.cc
+++ b/chrome/browser/download/download_commands.cc
@@ -23,11 +23,11 @@
 #include "chrome/browser/safe_browsing/safe_browsing_service.h"
 #include "chrome/browser/ui/browser_finder.h"
 #include "chrome/browser/ui/scoped_tabbed_browser_displayer.h"
-#include "chrome/common/safe_browsing/csd.pb.h"
 #include "chrome/common/safe_browsing/file_type_policies.h"
 #include "chrome/common/url_constants.h"
 #include "chrome/grit/theme_resources.h"
 #include "components/google/core/browser/google_util.h"
+#include "components/safe_browsing/csd.pb.h"
 #include "net/base/url_util.h"
 #include "ui/base/clipboard/scoped_clipboard_writer.h"
 #include "ui/base/resource/resource_bundle.h"
diff --git a/chrome/browser/download/download_danger_prompt.h b/chrome/browser/download/download_danger_prompt.h
index 5c3bb410..55fa38e7a7 100644
--- a/chrome/browser/download/download_danger_prompt.h
+++ b/chrome/browser/download/download_danger_prompt.h
@@ -6,7 +6,7 @@
 #define CHROME_BROWSER_DOWNLOAD_DOWNLOAD_DANGER_PROMPT_H_
 
 #include "base/callback_forward.h"
-#include "chrome/common/safe_browsing/csd.pb.h"
+#include "components/safe_browsing/csd.pb.h"
 
 namespace content {
 class DownloadItem;
diff --git a/chrome/browser/download/download_danger_prompt_browsertest.cc b/chrome/browser/download/download_danger_prompt_browsertest.cc
index d87d79b..ae710e8 100644
--- a/chrome/browser/download/download_danger_prompt_browsertest.cc
+++ b/chrome/browser/download/download_danger_prompt_browsertest.cc
@@ -15,9 +15,9 @@
 #include "chrome/browser/ui/browser_commands.h"
 #include "chrome/browser/ui/browser_tabstrip.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
-#include "chrome/common/safe_browsing/csd.pb.h"
 #include "chrome/test/base/in_process_browser_test.h"
 #include "chrome/test/base/ui_test_utils.h"
+#include "components/safe_browsing/csd.pb.h"
 #include "components/safe_browsing_db/database_manager.h"
 #include "content/public/test/mock_download_item.h"
 #include "testing/gmock/include/gmock/gmock.h"
diff --git a/chrome/browser/download/drag_download_item_views.cc b/chrome/browser/download/drag_download_item_views.cc
index 95fdabb5..c5f8109 100644
--- a/chrome/browser/download/drag_download_item_views.cc
+++ b/chrome/browser/download/drag_download_item_views.cc
@@ -14,13 +14,13 @@
 #include "ui/aura/window.h"
 #include "ui/aura/window_event_dispatcher.h"
 #include "ui/base/dragdrop/drag_drop_types.h"
+#include "ui/base/dragdrop/drag_utils.h"
 #include "ui/base/dragdrop/file_info.h"
 #include "ui/base/dragdrop/os_exchange_data.h"
 #include "ui/display/screen.h"
 #include "ui/gfx/geometry/point.h"
 #include "ui/gfx/image/image.h"
 #include "ui/gfx/image/image_skia.h"
-#include "ui/views/button_drag_utils.h"
 #include "ui/views/widget/widget.h"
 #include "url/gurl.h"
 
@@ -37,10 +37,10 @@
   // Set up our OLE machinery
   ui::OSExchangeData data;
 
-  button_drag_utils::SetDragImage(
-      GURL(), download->GetFileNameToReportUser().BaseName().LossyDisplayName(),
-      icon ? icon->AsImageSkia() : gfx::ImageSkia(), nullptr, &data,
-      views::Widget::GetWidgetForNativeView(view));
+  drag_utils::CreateDragImageForFile(
+      download->GetFileNameToReportUser(),
+      icon ? icon->AsImageSkia() : gfx::ImageSkia(),
+      &data);
 
   base::FilePath full_path = download->GetTargetFilePath();
 #if defined(OS_CHROMEOS)
diff --git a/chrome/browser/engagement/important_sites_util.cc b/chrome/browser/engagement/important_sites_util.cc
index c266bd0..96b8b01 100644
--- a/chrome/browser/engagement/important_sites_util.cc
+++ b/chrome/browser/engagement/important_sites_util.cc
@@ -483,7 +483,7 @@
   // First get data from site engagement.
   SiteEngagementService* site_engagement_service =
       SiteEngagementService::Get(profile);
-  site_engagement_service->ResetScoreForURL(
+  site_engagement_service->ResetBaseScoreForURL(
       origin, SiteEngagementScore::GetMediumEngagementBoundary());
   DCHECK(site_engagement_service->IsEngagementAtLeast(
       origin, blink::mojom::EngagementLevel::MEDIUM));
diff --git a/chrome/browser/engagement/important_sites_util_unittest.cc b/chrome/browser/engagement/important_sites_util_unittest.cc
index 028eb7c..20eea71e 100644
--- a/chrome/browser/engagement/important_sites_util_unittest.cc
+++ b/chrome/browser/engagement/important_sites_util_unittest.cc
@@ -154,13 +154,13 @@
   GURL url6("https://youtube.com/");
   GURL url7("https://foo.bar/");
 
-  service->ResetScoreForURL(url1, 5);
-  service->ResetScoreForURL(url2, 2);  // Below medium engagement (5).
-  service->ResetScoreForURL(url3, 7);
-  service->ResetScoreForURL(url4, 8);
-  service->ResetScoreForURL(url5, 9);
-  service->ResetScoreForURL(url6, 1);  // Below the medium engagement (5).
-  service->ResetScoreForURL(url7, 11);
+  service->ResetBaseScoreForURL(url1, 5);
+  service->ResetBaseScoreForURL(url2, 2);  // Below medium engagement (5).
+  service->ResetBaseScoreForURL(url3, 7);
+  service->ResetBaseScoreForURL(url4, 8);
+  service->ResetBaseScoreForURL(url5, 9);
+  service->ResetBaseScoreForURL(url6, 1);  // Below the medium engagement (5).
+  service->ResetBaseScoreForURL(url7, 11);
 
   // Here we should have:
   // 1: removed domains below minimum engagement,
@@ -239,9 +239,9 @@
 
   // If we add some site engagement, they should show up (even though the site
   // engagement score is too low for a signal by itself).
-  service->ResetScoreForURL(url1, 2);
-  service->ResetScoreForURL(url4, 3);
-  service->ResetScoreForURL(url7, 0);
+  service->ResetBaseScoreForURL(url1, 2);
+  service->ResetBaseScoreForURL(url4, 3);
+  service->ResetBaseScoreForURL(url7, 0);
 
   important_sites = ImportantSitesUtil::GetImportantRegisterableDomains(
       profile(), kNumImportantSites);
@@ -261,7 +261,7 @@
   GURL url2("http://www.gmail.com/");
 
   // Set a bunch of positive signals.
-  service->ResetScoreForURL(url1, 5);
+  service->ResetBaseScoreForURL(url1, 5);
   AddBookmark(url2);
   AddContentSetting(CONTENT_SETTINGS_TYPE_NOTIFICATIONS, CONTENT_SETTING_ALLOW,
                     url1);
@@ -316,7 +316,7 @@
   GURL url2("http://www.gmail.com/");
 
   // Set a bunch of positive signals.
-  service->ResetScoreForURL(url1, 5);
+  service->ResetBaseScoreForURL(url1, 5);
   AddBookmark(url2);
   AddContentSetting(CONTENT_SETTINGS_TYPE_NOTIFICATIONS, CONTENT_SETTING_ALLOW,
                     url1);
@@ -385,7 +385,7 @@
   base::HistogramTester histogram_tester;
 
   GURL url1("http://www.google.com/");
-  service->ResetScoreForURL(url1, 5);
+  service->ResetBaseScoreForURL(url1, 5);
   AddContentSetting(CONTENT_SETTINGS_TYPE_NOTIFICATIONS, CONTENT_SETTING_ALLOW,
                     url1);
 
@@ -430,7 +430,7 @@
   GURL url2("http://www.yahoo.com/");
 
   // Set a bunch of positive signals.
-  service->ResetScoreForURL(url2, 5);
+  service->ResetBaseScoreForURL(url2, 5);
   AddBookmark(url1);
   AddContentSetting(CONTENT_SETTINGS_TYPE_NOTIFICATIONS, CONTENT_SETTING_ALLOW,
                     url1);
diff --git a/chrome/browser/engagement/site_engagement_score.cc b/chrome/browser/engagement/site_engagement_score.cc
index d9cae36..a9493061 100644
--- a/chrome/browser/engagement/site_engagement_score.cc
+++ b/chrome/browser/engagement/site_engagement_score.cc
@@ -15,6 +15,8 @@
 #include "chrome/browser/content_settings/host_content_settings_map_factory.h"
 #include "chrome/browser/engagement/site_engagement_metrics.h"
 #include "components/content_settings/core/browser/host_content_settings_map.h"
+#include "components/content_settings/core/common/content_settings.h"
+#include "components/content_settings/core/common/content_settings_types.h"
 #include "components/variations/variations_associated_data.h"
 
 namespace {
@@ -88,6 +90,8 @@
   param_values[MAX_DECAYS_PER_SCORE] = {"max_decays_per_score", 4};
   param_values[LAST_ENGAGEMENT_GRACE_PERIOD_IN_HOURS] = {
       "last_engagement_grace_period_in_hours", 1};
+  param_values[NOTIFICATION_PERMISSION_POINTS] = {
+      "notification_permission_points", 5};
   return param_values;
 }
 
@@ -155,6 +159,10 @@
   return GetParamValues()[LAST_ENGAGEMENT_GRACE_PERIOD_IN_HOURS].second;
 }
 
+double SiteEngagementScore::GetNotificationPermissionPoints() {
+  return GetParamValues()[NOTIFICATION_PERMISSION_POINTS].second;
+}
+
 // static
 void SiteEngagementScore::UpdateFromVariations(const char* param_name) {
   double param_vals[MAX_VARIATION];
@@ -333,7 +341,8 @@
       last_engagement_time_(),
       last_shortcut_launch_time_(),
       score_dict_(score_dict.release()),
-      origin_(origin) {
+      origin_(origin),
+      settings_map_(nullptr) {
   if (!score_dict_)
     return;
 
@@ -363,12 +372,21 @@
 }
 
 double SiteEngagementScore::BonusScore() const {
+  double bonus = 0;
   int days_since_shortcut_launch =
       (clock_->Now() - last_shortcut_launch_time_).InDays();
   if (days_since_shortcut_launch <= kMaxDaysSinceShortcutLaunch)
-    return GetWebAppInstalledPoints();
+    bonus += GetWebAppInstalledPoints();
 
-  return 0;
+  // TODO(dominickn, raymes): call PermissionManager::GetPermissionStatus when
+  // the PermissionManager is thread-safe.
+  if (settings_map_ && settings_map_->GetContentSetting(
+                           origin_, GURL(), CONTENT_SETTINGS_TYPE_NOTIFICATIONS,
+                           std::string()) == CONTENT_SETTING_ALLOW) {
+    bonus += GetNotificationPermissionPoints();
+  }
+
+  return bonus;
 }
 
 void SiteEngagementScore::SetParamValuesForTesting() {
@@ -385,6 +403,7 @@
   GetParamValues()[HIGH_ENGAGEMENT_BOUNDARY].second = 50;
   GetParamValues()[MAX_DECAYS_PER_SCORE].second = 1;
   GetParamValues()[LAST_ENGAGEMENT_GRACE_PERIOD_IN_HOURS].second = 72;
+  GetParamValues()[NOTIFICATION_PERMISSION_POINTS].second = 5;
 
   // This is set to values that avoid interference with tests and are set when
   // testing these features.
diff --git a/chrome/browser/engagement/site_engagement_score.h b/chrome/browser/engagement/site_engagement_score.h
index bb29a78..861799a 100644
--- a/chrome/browser/engagement/site_engagement_score.h
+++ b/chrome/browser/engagement/site_engagement_score.h
@@ -84,6 +84,9 @@
     // period prior to clock_->Now().
     LAST_ENGAGEMENT_GRACE_PERIOD_IN_HOURS,
 
+    // THe number of points given for having notification permission granted.
+    NOTIFICATION_PERMISSION_POINTS,
+
     MAX_VARIATION
   };
 
@@ -106,6 +109,7 @@
   static double GetHighEngagementBoundary();
   static double GetMaxDecaysPerScore();
   static double GetLastEngagementGracePeriodInHours();
+  static double GetNotificationPermissionPoints();
 
   // Update the default engagement settings via variations.
   static void UpdateFromVariations(const char* param_name);
@@ -164,6 +168,7 @@
   friend class ChromePluginServiceFilterTest;
   friend class ImportantSitesUtil;
   friend class ImportantSitesUtilTest;
+  friend class PushMessagingBrowserTest;
   friend class SiteEngagementHelperTest;
   friend class SiteEngagementScoreTest;
   friend class SiteEngagementServiceTest;
diff --git a/chrome/browser/engagement/site_engagement_service.cc b/chrome/browser/engagement/site_engagement_service.cc
index 7ebb7b6..edda88c4 100644
--- a/chrome/browser/engagement/site_engagement_service.cc
+++ b/chrome/browser/engagement/site_engagement_service.cc
@@ -192,7 +192,8 @@
   observer_list_.RemoveObserver(observer);
 }
 
-void SiteEngagementService::ResetScoreForURL(const GURL& url, double score) {
+void SiteEngagementService::ResetBaseScoreForURL(const GURL& url,
+                                                 double score) {
   SiteEngagementScore engagement_score = CreateEngagementScore(url);
   engagement_score.Reset(score, clock_->Now());
   engagement_score.Commit();
diff --git a/chrome/browser/engagement/site_engagement_service.h b/chrome/browser/engagement/site_engagement_service.h
index cb1d47b..c97eb4a 100644
--- a/chrome/browser/engagement/site_engagement_service.h
+++ b/chrome/browser/engagement/site_engagement_service.h
@@ -120,8 +120,10 @@
   bool IsEngagementAtLeast(const GURL& url,
                            blink::mojom::EngagementLevel level) const;
 
-  // Resets the engagement score |url| to |score|, clearing daily limits.
-  void ResetScoreForURL(const GURL& url, double score);
+  // Resets the base engagement for |url| to |score|, clearing daily limits. Any
+  // bonus engagement that |url| has acquired is not affected by this method, so
+  // the result of GetScore(|url|) may not be the same as |score|.
+  void ResetBaseScoreForURL(const GURL& url, double score);
 
   // Update the last time |url| was opened from an installed shortcut to be
   // clock_->Now().
@@ -151,6 +153,7 @@
   FRIEND_TEST_ALL_PREFIXES(SiteEngagementServiceTest, GetTotalUserInputPoints);
   FRIEND_TEST_ALL_PREFIXES(SiteEngagementServiceTest, RestrictedToHTTPAndHTTPS);
   FRIEND_TEST_ALL_PREFIXES(SiteEngagementServiceTest, LastShortcutLaunch);
+  FRIEND_TEST_ALL_PREFIXES(SiteEngagementServiceTest, NotificationPermission);
   FRIEND_TEST_ALL_PREFIXES(SiteEngagementServiceTest,
                            CleanupOriginsOnHistoryDeletion);
   FRIEND_TEST_ALL_PREFIXES(SiteEngagementServiceTest, IsBootstrapped);
diff --git a/chrome/browser/engagement/site_engagement_service_android.cc b/chrome/browser/engagement/site_engagement_service_android.cc
index 9d71bf9..75308db 100644
--- a/chrome/browser/engagement/site_engagement_service_android.cc
+++ b/chrome/browser/engagement/site_engagement_service_android.cc
@@ -57,13 +57,13 @@
       GURL(base::android::ConvertJavaStringToUTF16(env, jurl)));
 }
 
-void SiteEngagementServiceAndroid::ResetScoreForURL(
+void SiteEngagementServiceAndroid::ResetBaseScoreForURL(
     JNIEnv* env,
     const JavaParamRef<jobject>& caller,
     const JavaParamRef<jstring>& jurl,
     double score) {
   if (jurl) {
-    service_->ResetScoreForURL(
+    service_->ResetBaseScoreForURL(
         GURL(base::android::ConvertJavaStringToUTF16(env, jurl)), score);
   }
 }
diff --git a/chrome/browser/engagement/site_engagement_service_android.h b/chrome/browser/engagement/site_engagement_service_android.h
index c68906a..d3a7186 100644
--- a/chrome/browser/engagement/site_engagement_service_android.h
+++ b/chrome/browser/engagement/site_engagement_service_android.h
@@ -36,10 +36,10 @@
                   const base::android::JavaParamRef<jobject>& caller,
                   const base::android::JavaParamRef<jstring>& jurl) const;
 
-  void ResetScoreForURL(JNIEnv* env,
-                        const base::android::JavaParamRef<jobject>& caller,
-                        const base::android::JavaParamRef<jstring>& jurl,
-                        double score);
+  void ResetBaseScoreForURL(JNIEnv* env,
+                            const base::android::JavaParamRef<jobject>& caller,
+                            const base::android::JavaParamRef<jstring>& jurl,
+                            double score);
 
  private:
   base::android::ScopedJavaGlobalRef<jobject> java_service_;
diff --git a/chrome/browser/engagement/site_engagement_service_unittest.cc b/chrome/browser/engagement/site_engagement_service_unittest.cc
index c7dca74..95c77a3 100644
--- a/chrome/browser/engagement/site_engagement_service_unittest.cc
+++ b/chrome/browser/engagement/site_engagement_service_unittest.cc
@@ -25,6 +25,8 @@
 #include "chrome/test/base/testing_profile.h"
 #include "components/content_settings/core/browser/content_settings_observer.h"
 #include "components/content_settings/core/browser/host_content_settings_map.h"
+#include "components/content_settings/core/common/content_settings.h"
+#include "components/content_settings/core/common/content_settings_types.h"
 #include "components/history/core/browser/history_database_params.h"
 #include "components/history/core/browser/history_service.h"
 #include "components/history/core/test/test_history_database.h"
@@ -482,6 +484,51 @@
   EXPECT_DOUBLE_EQ(0.0, service->GetScore(url2));
 }
 
+TEST_F(SiteEngagementServiceTest, NotificationPermission) {
+  base::SimpleTestClock* clock = new base::SimpleTestClock();
+  std::unique_ptr<SiteEngagementService> service(
+      new SiteEngagementService(profile(), base::WrapUnique(clock)));
+
+  GURL url1("https://www.google.com/");
+  GURL url2("http://www.google.com/");
+  GURL url3("https://drive.google.com/");
+  clock->SetNow(GetReferenceTime());
+
+  EXPECT_EQ(0, service->GetScore(url1));
+  EXPECT_EQ(0, service->GetScore(url2));
+  EXPECT_EQ(0, service->GetScore(url3));
+
+  HostContentSettingsMap* settings_map =
+      HostContentSettingsMapFactory::GetForProfile(profile());
+
+  settings_map->SetContentSettingDefaultScope(
+      url1, url1, CONTENT_SETTINGS_TYPE_NOTIFICATIONS, std::string(),
+      CONTENT_SETTING_ALLOW);
+
+  settings_map->SetContentSettingDefaultScope(
+      url2, url2, CONTENT_SETTINGS_TYPE_NOTIFICATIONS, std::string(),
+      CONTENT_SETTING_BLOCK);
+
+  settings_map->SetContentSettingDefaultScope(
+      url3, url3, CONTENT_SETTINGS_TYPE_NOTIFICATIONS, std::string(),
+      CONTENT_SETTING_ASK);
+
+  EXPECT_EQ(5, service->GetScore(url1));
+  EXPECT_EQ(0, service->GetScore(url2));
+  EXPECT_EQ(0, service->GetScore(url3));
+
+  service->AddPoints(url1, 1.0);
+  service->AddPoints(url2, 3.0);
+  EXPECT_EQ(6, service->GetScore(url1));
+  EXPECT_EQ(3, service->GetScore(url2));
+
+  settings_map->SetContentSettingDefaultScope(
+      url1, url1, CONTENT_SETTINGS_TYPE_NOTIFICATIONS, std::string(),
+      CONTENT_SETTING_BLOCK);
+
+  EXPECT_EQ(1, service->GetScore(url1));
+}
+
 TEST_F(SiteEngagementServiceTest, CheckHistograms) {
   base::HistogramTester histograms;
 
@@ -1639,7 +1686,7 @@
   clock->SetNow(current_day);
 
   GURL origin("http://www.google.com/");
-  service->ResetScoreForURL(origin, 5);
+  service->ResetBaseScoreForURL(origin, 5);
   service->AddPoints(origin, 5);
   EXPECT_EQ(10, service->GetScore(origin));
   EXPECT_EQ(current_day, service->GetLastEngagementTime());
diff --git a/chrome/browser/extensions/api/declarative/rules_registry_with_cache_unittest.cc b/chrome/browser/extensions/api/declarative/rules_registry_with_cache_unittest.cc
index b990d137..ec4d41a 100644
--- a/chrome/browser/extensions/api/declarative/rules_registry_with_cache_unittest.cc
+++ b/chrome/browser/extensions/api/declarative/rules_registry_with_cache_unittest.cc
@@ -342,13 +342,9 @@
 
   // 1. Add an extension, before rules registry gets created.
   std::string error;
-  scoped_refptr<Extension> extension(
-      LoadManifestUnchecked("permissions",
-                            "web_request_all_host_permissions.json",
-                            Manifest::INVALID_LOCATION,
-                            Extension::NO_FLAGS,
-                            extension1_->id(),
-                            &error));
+  scoped_refptr<Extension> extension(LoadManifestUnchecked(
+      "permissions", "web_request_all_host_permissions.json",
+      Manifest::UNPACKED, Extension::NO_FLAGS, extension1_->id(), &error));
   ASSERT_TRUE(error.empty());
   extension_service->AddExtension(extension.get());
   EXPECT_TRUE(extensions::ExtensionRegistry::Get(env_.profile())
diff --git a/chrome/browser/extensions/api/developer_private/extension_info_generator_unittest.cc b/chrome/browser/extensions/api/developer_private/extension_info_generator_unittest.cc
index 30d379c8..d1d54f7 100644
--- a/chrome/browser/extensions/api/developer_private/extension_info_generator_unittest.cc
+++ b/chrome/browser/extensions/api/developer_private/extension_info_generator_unittest.cc
@@ -172,7 +172,7 @@
 
     // Produce test output.
     std::unique_ptr<developer::ExtensionInfo> info =
-        CreateExtensionInfoFromPath(extension_path, Manifest::INVALID_LOCATION);
+        CreateExtensionInfoFromPath(extension_path, Manifest::UNPACKED);
     info->views = std::move(views);
     std::unique_ptr<base::DictionaryValue> actual_output_data = info->ToValue();
     ASSERT_TRUE(actual_output_data);
diff --git a/chrome/browser/extensions/extension_action_icon_factory_unittest.cc b/chrome/browser/extensions/extension_action_icon_factory_unittest.cc
index 692e9df..fb0f0cc09 100644
--- a/chrome/browser/extensions/extension_action_icon_factory_unittest.cc
+++ b/chrome/browser/extensions/extension_action_icon_factory_unittest.cc
@@ -96,8 +96,7 @@
     quit_in_icon_updated_ = false;
   }
 
-  scoped_refptr<Extension> CreateExtension(const char* name,
-                                           Manifest::Location location) {
+  scoped_refptr<Extension> CreateExtension(const char* name) {
     // Create and load an extension.
     base::FilePath test_file;
     if (!PathService::Get(chrome::DIR_TEST_DATA, &test_file)) {
@@ -121,7 +120,7 @@
       return NULL;
 
     scoped_refptr<Extension> extension =
-        Extension::Create(test_file, location, *valid_value,
+        Extension::Create(test_file, Manifest::UNPACKED, *valid_value,
                           Extension::NO_FLAGS, &error);
     EXPECT_TRUE(extension.get()) << error;
     if (extension.get())
@@ -191,8 +190,7 @@
 TEST_P(ExtensionActionIconFactoryTest, NoIcons) {
   // Load an extension that has browser action without default icon set in the
   // manifest and does not call |SetIcon| by default.
-  scoped_refptr<Extension> extension(CreateExtension(
-      "browser_action/no_icon", Manifest::INVALID_LOCATION));
+  scoped_refptr<Extension> extension(CreateExtension("browser_action/no_icon"));
   ASSERT_TRUE(extension.get() != NULL);
   ExtensionAction* browser_action = GetBrowserAction(*extension);
   ASSERT_TRUE(browser_action);
@@ -216,8 +214,7 @@
   // Load an extension that has browser action without default icon set in the
   // manifest and does not call |SetIcon| by default (but has an browser action
   // icon resource).
-  scoped_refptr<Extension> extension(CreateExtension(
-      "browser_action/no_icon", Manifest::INVALID_LOCATION));
+  scoped_refptr<Extension> extension(CreateExtension("browser_action/no_icon"));
   ASSERT_TRUE(extension.get() != NULL);
   ExtensionAction* browser_action = GetBrowserAction(*extension);
   ASSERT_TRUE(browser_action);
@@ -255,8 +252,7 @@
   // Load an extension that has browser action without default icon set in the
   // manifest and does not call |SetIcon| by default (but has an browser action
   // icon resource).
-  scoped_refptr<Extension> extension(CreateExtension(
-      "browser_action/no_icon", Manifest::INVALID_LOCATION));
+  scoped_refptr<Extension> extension(CreateExtension("browser_action/no_icon"));
   ASSERT_TRUE(extension.get() != NULL);
   ExtensionAction* browser_action = GetBrowserAction(*extension);
   ASSERT_TRUE(browser_action);
@@ -264,7 +260,7 @@
   ASSERT_TRUE(browser_action->GetExplicitlySetIcon(0 /*tab id*/).IsEmpty());
 
   scoped_refptr<const Extension> extension_with_icon =
-      CreateExtension("browser_action_with_icon", Manifest::INVALID_LOCATION);
+      CreateExtension("browser_action_with_icon");
   ASSERT_TRUE(extension_with_icon);
 
   int icon_size = ExtensionAction::ActionIconSize();
diff --git a/chrome/browser/extensions/extension_service.cc b/chrome/browser/extensions/extension_service.cc
index 7a283b5..46ddb77b 100644
--- a/chrome/browser/extensions/extension_service.cc
+++ b/chrome/browser/extensions/extension_service.cc
@@ -13,6 +13,8 @@
 #include <utility>
 
 #include "base/command_line.h"
+#include "base/debug/alias.h"
+#include "base/debug/dump_without_crashing.h"
 #include "base/location.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/single_thread_task_runner.h"
@@ -1481,6 +1483,26 @@
 }
 
 void ExtensionService::AddExtension(const Extension* extension) {
+  if (!Manifest::IsValidLocation(extension->location())) {
+    // TODO(devlin): We should *never* add an extension with an invalid
+    // location, but some bugs (e.g. crbug.com/692069) seem to indicate we do.
+    // Track down the cases when this can happen, and remove this
+    // DumpWithoutCrashing() (possibly replacing it with a CHECK).
+    NOTREACHED();
+    char extension_id_copy[33];
+    base::strlcpy(extension_id_copy, extension->id().c_str(),
+                  arraysize(extension_id_copy));
+    Manifest::Location location = extension->location();
+    int creation_flags = extension->creation_flags();
+    Manifest::Type type = extension->manifest()->type();
+    base::debug::Alias(extension_id_copy);
+    base::debug::Alias(&location);
+    base::debug::Alias(&creation_flags);
+    base::debug::Alias(&type);
+    base::debug::DumpWithoutCrashing();
+    return;
+  }
+
   // TODO(jstritar): We may be able to get rid of this branch by overriding the
   // default extension state to DISABLED when the --disable-extensions flag
   // is set (http://crbug.com/29067).
diff --git a/chrome/browser/plugins/chrome_plugin_service_filter_unittest.cc b/chrome/browser/plugins/chrome_plugin_service_filter_unittest.cc
index 77543c05..d0f518f 100644
--- a/chrome/browser/plugins/chrome_plugin_service_filter_unittest.cc
+++ b/chrome/browser/plugins/chrome_plugin_service_filter_unittest.cc
@@ -210,7 +210,7 @@
       ChromePluginServiceFilter::kEngagementNoSettingHistogram, 0, 1);
 
   SiteEngagementService* service = SiteEngagementService::Get(profile());
-  service->ResetScoreForURL(url, 10.0);
+  service->ResetBaseScoreForURL(url, 10.0);
 
   // Should still be blocked.
   EXPECT_FALSE(IsPluginAvailable(
@@ -219,7 +219,7 @@
       ChromePluginServiceFilter::kEngagementNoSettingHistogram, 10, 1);
 
   // Reaching 30.0 engagement should allow Flash.
-  service->ResetScoreForURL(url, 30.0);
+  service->ResetBaseScoreForURL(url, 30.0);
   EXPECT_TRUE(IsPluginAvailable(url, main_frame_origin,
                                 profile()->GetResourceContext(), flash_plugin));
 
@@ -285,16 +285,16 @@
 
   // Should still be blocked until engagement reaches 50.
   SiteEngagementService* service = SiteEngagementService::Get(profile());
-  service->ResetScoreForURL(url, 0.0);
+  service->ResetBaseScoreForURL(url, 0.0);
   EXPECT_FALSE(IsPluginAvailable(
       url, main_frame_origin, profile()->GetResourceContext(), flash_plugin));
-  service->ResetScoreForURL(url, 10.0);
+  service->ResetBaseScoreForURL(url, 10.0);
   EXPECT_FALSE(IsPluginAvailable(
       url, main_frame_origin, profile()->GetResourceContext(), flash_plugin));
-  service->ResetScoreForURL(url, 40.0);
+  service->ResetBaseScoreForURL(url, 40.0);
   EXPECT_FALSE(IsPluginAvailable(
       url, main_frame_origin, profile()->GetResourceContext(), flash_plugin));
-  service->ResetScoreForURL(url, 60.0);
+  service->ResetBaseScoreForURL(url, 60.0);
   EXPECT_TRUE(IsPluginAvailable(url, main_frame_origin,
                                 profile()->GetResourceContext(), flash_plugin));
 
@@ -337,7 +337,7 @@
 
   // Add sufficient engagement to allow Flash in the original profile.
   SiteEngagementService* service = SiteEngagementService::Get(profile());
-  service->ResetScoreForURL(url, 30.0);
+  service->ResetBaseScoreForURL(url, 30.0);
 
   // We should still fail the engagement check due to the block.
   EXPECT_FALSE(IsPluginAvailable(
@@ -397,7 +397,7 @@
 
   // Add sufficient engagement to allow Flash in the incognito profile.
   SiteEngagementService* service = SiteEngagementService::Get(incognito);
-  service->ResetScoreForURL(url, 30.0);
+  service->ResetBaseScoreForURL(url, 30.0);
 
   // Ensure we pass the engagement check in the incognito profile.
   EXPECT_TRUE(IsPluginAvailable(url, main_frame_origin,
@@ -428,9 +428,9 @@
   url::Origin main_frame_origin(url);
   NavigateAndCommit(url);
 
-  service->ResetScoreForURL(url, 30.0);
+  service->ResetBaseScoreForURL(url, 30.0);
   // Reaching 30.0 engagement would usually allow Flash, but not for enterprise.
-  service->ResetScoreForURL(url, 0);
+  service->ResetBaseScoreForURL(url, 0);
   EXPECT_FALSE(IsPluginAvailable(
       url, main_frame_origin, profile()->GetResourceContext(), flash_plugin));
 
diff --git a/chrome/browser/precache/precache_util.cc b/chrome/browser/precache/precache_util.cc
index 407a5ef45..ca37327 100644
--- a/chrome/browser/precache/precache_util.cc
+++ b/chrome/browser/precache/precache_util.cc
@@ -31,7 +31,6 @@
 
 void UpdatePrecacheMetricsAndStateOnUIThread(const GURL& url,
                                              const GURL& referrer,
-                                             base::TimeDelta latency,
                                              const base::Time& fetch_time,
                                              const net::HttpResponseInfo& info,
                                              int64_t size,
@@ -50,7 +49,7 @@
     return;
 
   precache_manager->UpdatePrecacheMetricsAndState(
-      url, referrer, latency, fetch_time, info, size, is_user_traffic,
+      url, referrer, fetch_time, info, size, is_user_traffic,
       base::Bind(&precache::RegisterPrecacheSyntheticFieldTrial));
 }
 
@@ -67,14 +66,13 @@
   // specified with the Content-Length header, which may be inaccurate,
   // or missing, as is the case with chunked encoding.
   int64_t received_content_length = request->received_response_content_length();
-  base::TimeDelta latency = base::TimeTicks::Now() - request->creation_time();
 
   // Record precache metrics when a fetch is completed successfully, if
   // precaching is allowed.
   content::BrowserThread::PostTask(
       content::BrowserThread::UI, FROM_HERE,
       base::Bind(&UpdatePrecacheMetricsAndStateOnUIThread, request->url(),
-                 GURL(request->referrer()), latency, base::Time::Now(),
+                 GURL(request->referrer()), base::Time::Now(),
                  request->response_info(), received_content_length,
                  data_use_measurement::IsUserRequest(*request), profile_id));
 }
diff --git a/chrome/browser/profile_resetter/profile_resetter_unittest.cc b/chrome/browser/profile_resetter/profile_resetter_unittest.cc
index 5bbfd4a6..018ce88d 100644
--- a/chrome/browser/profile_resetter/profile_resetter_unittest.cc
+++ b/chrome/browser/profile_resetter/profile_resetter_unittest.cc
@@ -560,8 +560,8 @@
   ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
 
   scoped_refptr<Extension> theme = CreateExtension(
-      base::ASCIIToUTF16("example1"), temp_dir.GetPath(),
-      Manifest::INVALID_LOCATION, extensions::Manifest::TYPE_THEME, false);
+      base::ASCIIToUTF16("example1"), temp_dir.GetPath(), Manifest::UNPACKED,
+      extensions::Manifest::TYPE_THEME, false);
   service_->FinishInstallationForTest(theme.get());
   // Let ThemeService finish creating the theme pack.
   base::RunLoop().RunUntilIdle();
@@ -572,10 +572,8 @@
 
   scoped_refptr<Extension> ext2 = CreateExtension(
       base::ASCIIToUTF16("example2"),
-      base::FilePath(FILE_PATH_LITERAL("//nonexistent")),
-      Manifest::INVALID_LOCATION,
-      extensions::Manifest::TYPE_EXTENSION,
-      false);
+      base::FilePath(FILE_PATH_LITERAL("//nonexistent")), Manifest::UNPACKED,
+      extensions::Manifest::TYPE_EXTENSION, false);
   service_->AddExtension(ext2.get());
   // Component extensions and policy-managed extensions shouldn't be disabled.
   scoped_refptr<Extension> ext3 = CreateExtension(
@@ -622,18 +620,14 @@
 TEST_F(ProfileResetterTest, ResetExtensionsByDisablingNonOrganic) {
   scoped_refptr<Extension> ext2 = CreateExtension(
       base::ASCIIToUTF16("example2"),
-      base::FilePath(FILE_PATH_LITERAL("//nonexistent")),
-      Manifest::INVALID_LOCATION,
-      extensions::Manifest::TYPE_EXTENSION,
-      false);
+      base::FilePath(FILE_PATH_LITERAL("//nonexistent")), Manifest::UNPACKED,
+      extensions::Manifest::TYPE_EXTENSION, false);
   service_->AddExtension(ext2.get());
   // Components and external policy extensions shouldn't be deleted.
   scoped_refptr<Extension> ext3 = CreateExtension(
       base::ASCIIToUTF16("example3"),
-      base::FilePath(FILE_PATH_LITERAL("//nonexistent2")),
-      Manifest::INVALID_LOCATION,
-      extensions::Manifest::TYPE_EXTENSION,
-      false);
+      base::FilePath(FILE_PATH_LITERAL("//nonexistent2")), Manifest::UNPACKED,
+      extensions::Manifest::TYPE_EXTENSION, false);
   service_->AddExtension(ext3.get());
   EXPECT_EQ(2u, registry()->enabled_extensions().size());
 
@@ -653,8 +647,8 @@
   ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
 
   scoped_refptr<Extension> ext1 = CreateExtension(
-      base::ASCIIToUTF16("example1"), temp_dir.GetPath(),
-      Manifest::INVALID_LOCATION, extensions::Manifest::TYPE_THEME, false);
+      base::ASCIIToUTF16("example1"), temp_dir.GetPath(), Manifest::UNPACKED,
+      extensions::Manifest::TYPE_THEME, false);
   service_->FinishInstallationForTest(ext1.get());
   // Let ThemeService finish creating the theme pack.
   base::RunLoop().RunUntilIdle();
@@ -663,20 +657,16 @@
       ThemeServiceFactory::GetForProfile(profile());
   EXPECT_FALSE(theme_service->UsingDefaultTheme());
 
-  scoped_refptr<Extension> ext2 =
-      CreateExtension(base::ASCIIToUTF16("example2"),
-                      base::FilePath(FILE_PATH_LITERAL("//nonexistent2")),
-                      Manifest::INVALID_LOCATION,
-                      extensions::Manifest::TYPE_EXTENSION,
-                      false);
+  scoped_refptr<Extension> ext2 = CreateExtension(
+      base::ASCIIToUTF16("example2"),
+      base::FilePath(FILE_PATH_LITERAL("//nonexistent2")), Manifest::UNPACKED,
+      extensions::Manifest::TYPE_EXTENSION, false);
   service_->AddExtension(ext2.get());
 
-  scoped_refptr<Extension> ext3 =
-      CreateExtension(base::ASCIIToUTF16("example2"),
-                      base::FilePath(FILE_PATH_LITERAL("//nonexistent3")),
-                      Manifest::INVALID_LOCATION,
-                      extensions::Manifest::TYPE_HOSTED_APP,
-                      true);
+  scoped_refptr<Extension> ext3 = CreateExtension(
+      base::ASCIIToUTF16("example2"),
+      base::FilePath(FILE_PATH_LITERAL("//nonexistent3")), Manifest::UNPACKED,
+      extensions::Manifest::TYPE_HOSTED_APP, true);
   service_->AddExtension(ext3.get());
   EXPECT_EQ(3u, registry()->enabled_extensions().size());
 
@@ -849,10 +839,8 @@
 
   scoped_refptr<Extension> ext = CreateExtension(
       base::ASCIIToUTF16("example"),
-      base::FilePath(FILE_PATH_LITERAL("//nonexistent")),
-      Manifest::INVALID_LOCATION,
-      extensions::Manifest::TYPE_EXTENSION,
-      false);
+      base::FilePath(FILE_PATH_LITERAL("//nonexistent")), Manifest::UNPACKED,
+      extensions::Manifest::TYPE_EXTENSION, false);
   ASSERT_TRUE(ext.get());
   service_->AddExtension(ext.get());
 
@@ -938,10 +926,8 @@
 
   scoped_refptr<Extension> ext = CreateExtension(
       base::ASCIIToUTF16("example"),
-      base::FilePath(FILE_PATH_LITERAL("//nonexistent")),
-      Manifest::INVALID_LOCATION,
-      extensions::Manifest::TYPE_EXTENSION,
-      false);
+      base::FilePath(FILE_PATH_LITERAL("//nonexistent")), Manifest::UNPACKED,
+      extensions::Manifest::TYPE_EXTENSION, false);
   ASSERT_TRUE(ext.get());
   service_->AddExtension(ext.get());
 
@@ -1002,10 +988,8 @@
 TEST_F(ProfileResetterTest, GetReadableFeedback) {
   scoped_refptr<Extension> ext = CreateExtension(
       base::WideToUTF16(L"Tiësto"),
-      base::FilePath(FILE_PATH_LITERAL("//nonexistent")),
-      Manifest::INVALID_LOCATION,
-      extensions::Manifest::TYPE_EXTENSION,
-      false);
+      base::FilePath(FILE_PATH_LITERAL("//nonexistent")), Manifest::UNPACKED,
+      extensions::Manifest::TYPE_EXTENSION, false);
   ASSERT_TRUE(ext.get());
   service_->AddExtension(ext.get());
 
diff --git a/chrome/browser/push_messaging/push_messaging_browsertest.cc b/chrome/browser/push_messaging/push_messaging_browsertest.cc
index 024accd..50645a7 100644
--- a/chrome/browser/push_messaging/push_messaging_browsertest.cc
+++ b/chrome/browser/push_messaging/push_messaging_browsertest.cc
@@ -23,6 +23,7 @@
 #include "chrome/browser/browsing_data/browsing_data_remover_factory.h"
 #include "chrome/browser/browsing_data/browsing_data_remover_test_util.h"
 #include "chrome/browser/content_settings/host_content_settings_map_factory.h"
+#include "chrome/browser/engagement/site_engagement_score.h"
 #include "chrome/browser/engagement/site_engagement_service.h"
 #include "chrome/browser/gcm/fake_gcm_profile_service.h"
 #include "chrome/browser/gcm/gcm_profile_service_factory.h"
@@ -124,6 +125,7 @@
 
     notification_manager_.reset(new StubNotificationUIManager);
 
+    SiteEngagementScore::SetParamValuesForTesting();
     InProcessBrowserTest::SetUp();
   }
   void SetUpCommandLine(base::CommandLine* command_line) override {
@@ -272,10 +274,15 @@
 
   PushMessagingServiceImpl* push_service() const { return push_service_; }
 
-  void SetSiteEngagementScore(const GURL& url, double score) {
+  void SetSiteEngagementScore(const GURL& url,
+                              double score,
+                              double expected_score) {
+    // There will be a bonus of 5.0 points for having notification permission
+    // granted, so we assert that the final score is as expected.
     SiteEngagementService* service =
         SiteEngagementService::Get(GetBrowser()->profile());
-    service->ResetScoreForURL(url, score);
+    service->ResetBaseScoreForURL(url, score);
+    EXPECT_EQ(expected_score, service->GetScore(url));
   }
 
  protected:
@@ -1240,7 +1247,7 @@
   // Set the site engagement score for the site. Setting it to 10 means it
   // should have a budget of 4, enough for two non-shown notification, which
   // cost 2 each.
-  SetSiteEngagementScore(web_contents->GetURL(), 10.0);
+  SetSiteEngagementScore(web_contents->GetURL(), 5.0, 10.0);
 
   // If the site is visible in an active tab, we should not force a notification
   // to be shown. Try it twice, since we allow one mistake per 10 push events.
@@ -1342,16 +1349,14 @@
   content::WebContents* web_contents =
       GetBrowser()->tab_strip_model()->GetActiveWebContents();
 
+  SetSiteEngagementScore(web_contents->GetURL(), 0.0, 5.0);
+
   ui_test_utils::NavigateToURLWithDisposition(
       GetBrowser(), GURL("about:blank"),
       WindowOpenDisposition::NEW_FOREGROUND_TAB,
       ui_test_utils::BROWSER_TEST_WAIT_FOR_TAB);
 
-  SetSiteEngagementScore(web_contents->GetURL(), 0.0);
-
-  // If the Service Worker push event handler does not show a notification, we
-  // should show a forced one providing there is no foreground tab and the
-  // origin ran out of budget.
+  // Send a missed notification to use up the budget.
   gcm::IncomingMessage message;
   message.sender_id = GetTestApplicationServerKey();
   message.raw_data = "testdata";
@@ -1360,6 +1365,14 @@
   SendMessageAndWaitUntilHandled(app_identifier, message);
   ASSERT_TRUE(RunScript("resultQueue.pop()", &script_result, web_contents));
   EXPECT_EQ("testdata", script_result);
+  EXPECT_EQ(0u, notification_manager()->GetNotificationCount());
+
+  // If the Service Worker push event handler does not show a notification, we
+  // should show a forced one providing there is no foreground tab and the
+  // origin ran out of budget.
+  SendMessageAndWaitUntilHandled(app_identifier, message);
+  ASSERT_TRUE(RunScript("resultQueue.pop()", &script_result, web_contents));
+  EXPECT_EQ("testdata", script_result);
 
   // Because the --allow-silent-push command line flag has not been passed,
   // this should have shown a default notification.
diff --git a/chrome/browser/renderer_context_menu/render_view_context_menu.cc b/chrome/browser/renderer_context_menu/render_view_context_menu.cc
index 0589a34..e55c14ed 100644
--- a/chrome/browser/renderer_context_menu/render_view_context_menu.cc
+++ b/chrome/browser/renderer_context_menu/render_view_context_menu.cc
@@ -60,6 +60,7 @@
 #include "chrome/common/chrome_constants.h"
 #include "chrome/common/chrome_switches.h"
 #include "chrome/common/content_restriction.h"
+#include "chrome/common/image_context_menu_renderer.mojom.h"
 #include "chrome/common/pref_names.h"
 #include "chrome/common/render_messages.h"
 #include "chrome/common/url_constants.h"
@@ -108,6 +109,7 @@
 #include "net/base/escape.h"
 #include "ppapi/features/features.h"
 #include "printing/features/features.h"
+#include "services/service_manager/public/cpp/interface_provider.h"
 #include "third_party/WebKit/public/public_features.h"
 #include "third_party/WebKit/public/web/WebContextMenuData.h"
 #include "third_party/WebKit/public/web/WebMediaPlayerAction.h"
@@ -411,12 +413,10 @@
 content::WebContents* GetWebContentsToUse(content::WebContents* web_contents) {
 #if BUILDFLAG(ENABLE_EXTENSIONS)
   // If we're viewing in a MimeHandlerViewGuest, use its embedder WebContents.
-  if (extensions::MimeHandlerViewGuest::FromWebContents(web_contents)) {
-    WebContents* top_level_web_contents =
-        guest_view::GuestViewBase::GetTopLevelWebContents(web_contents);
-    if (top_level_web_contents)
-      return top_level_web_contents;
-  }
+  auto* guest_view =
+      extensions::MimeHandlerViewGuest::FromWebContents(web_contents);
+  if (guest_view)
+    return guest_view->embedder_web_contents();
 #endif
   return web_contents;
 }
@@ -2346,8 +2346,9 @@
   RenderFrameHost* render_frame_host = GetRenderFrameHost();
   if (!render_frame_host)
     return;
-  render_frame_host->Send(new ChromeViewMsg_RequestReloadImageForContextNode(
-      render_frame_host->GetRoutingID()));
+  chrome::mojom::ImageContextMenuRendererPtr renderer;
+  render_frame_host->GetRemoteInterfaces()->GetInterface(&renderer);
+  renderer->RequestReloadImageForContextNode();
 }
 
 void RenderViewContextMenu::ExecPlayPause() {
diff --git a/chrome/browser/resources/chromeos/login/login_shared.js b/chrome/browser/resources/chromeos/login/login_shared.js
index 160da795..314c671 100644
--- a/chrome/browser/resources/chromeos/login/login_shared.js
+++ b/chrome/browser/resources/chromeos/login/login_shared.js
@@ -441,5 +441,10 @@
   document.addEventListener('DOMContentLoaded', function() {
     Oobe.initialize();
   });
+
+  // Install a global error handler so stack traces are included in logs.
+  window.onerror = function(message, file, line, column, error) {
+    console.error(error.stack);
+  }
 })();
 
diff --git a/chrome/browser/safe_browsing/browser_feature_extractor.cc b/chrome/browser/safe_browsing/browser_feature_extractor.cc
index aee391d..5b1967a 100644
--- a/chrome/browser/safe_browsing/browser_feature_extractor.cc
+++ b/chrome/browser/safe_browsing/browser_feature_extractor.cc
@@ -22,9 +22,9 @@
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/safe_browsing/browser_features.h"
 #include "chrome/browser/safe_browsing/client_side_detection_host.h"
-#include "chrome/common/safe_browsing/csd.pb.h"
 #include "components/history/core/browser/history_service.h"
 #include "components/history/core/browser/history_types.h"
+#include "components/safe_browsing/csd.pb.h"
 #include "components/safe_browsing_db/database_manager.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/navigation_controller.h"
diff --git a/chrome/browser/safe_browsing/browser_feature_extractor_unittest.cc b/chrome/browser/safe_browsing/browser_feature_extractor_unittest.cc
index bcefc31..240438f 100644
--- a/chrome/browser/safe_browsing/browser_feature_extractor_unittest.cc
+++ b/chrome/browser/safe_browsing/browser_feature_extractor_unittest.cc
@@ -20,11 +20,11 @@
 #include "chrome/browser/safe_browsing/client_side_detection_host.h"
 #include "chrome/browser/safe_browsing/safe_browsing_service.h"
 #include "chrome/browser/safe_browsing/ui_manager.h"
-#include "chrome/common/safe_browsing/csd.pb.h"
 #include "chrome/test/base/chrome_render_view_host_test_harness.h"
 #include "chrome/test/base/testing_profile.h"
 #include "components/history/core/browser/history_backend.h"
 #include "components/history/core/browser/history_service.h"
+#include "components/safe_browsing/csd.pb.h"
 #include "components/safe_browsing_db/database_manager.h"
 #include "components/safe_browsing_db/test_database_manager.h"
 #include "content/public/browser/navigation_controller.h"
diff --git a/chrome/browser/safe_browsing/client_side_detection_host.cc b/chrome/browser/safe_browsing/client_side_detection_host.cc
index 4bba2f07..b5eb0d7 100644
--- a/chrome/browser/safe_browsing/client_side_detection_host.cc
+++ b/chrome/browser/safe_browsing/client_side_detection_host.cc
@@ -20,9 +20,9 @@
 #include "chrome/browser/safe_browsing/client_side_detection_service.h"
 #include "chrome/browser/safe_browsing/safe_browsing_service.h"
 #include "chrome/common/pref_names.h"
-#include "chrome/common/safe_browsing/csd.pb.h"
 #include "components/prefs/pref_service.h"
 #include "components/safe_browsing/common/safebrowsing_messages.h"
+#include "components/safe_browsing/csd.pb.h"
 #include "components/safe_browsing_db/database_manager.h"
 #include "components/safe_browsing_db/safe_browsing_prefs.h"
 #include "content/public/browser/browser_thread.h"
diff --git a/chrome/browser/safe_browsing/client_side_detection_host_unittest.cc b/chrome/browser/safe_browsing/client_side_detection_host_unittest.cc
index 5ca8bf2..dff9a32 100644
--- a/chrome/browser/safe_browsing/client_side_detection_host_unittest.cc
+++ b/chrome/browser/safe_browsing/client_side_detection_host_unittest.cc
@@ -19,10 +19,10 @@
 #include "chrome/browser/safe_browsing/safe_browsing_service.h"
 #include "chrome/browser/safe_browsing/ui_manager.h"
 #include "chrome/common/chrome_switches.h"
-#include "chrome/common/safe_browsing/csd.pb.h"
 #include "chrome/test/base/chrome_render_view_host_test_harness.h"
 #include "chrome/test/base/testing_profile.h"
 #include "components/safe_browsing/common/safebrowsing_messages.h"
+#include "components/safe_browsing/csd.pb.h"
 #include "components/safe_browsing_db/database_manager.h"
 #include "components/safe_browsing_db/test_database_manager.h"
 #include "content/public/browser/navigation_entry.h"
diff --git a/chrome/browser/safe_browsing/client_side_detection_service.cc b/chrome/browser/safe_browsing/client_side_detection_service.cc
index df4e6e5..275e8146 100644
--- a/chrome/browser/safe_browsing/client_side_detection_service.cc
+++ b/chrome/browser/safe_browsing/client_side_detection_service.cc
@@ -21,10 +21,10 @@
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/common/pref_names.h"
 #include "chrome/common/safe_browsing/client_model.pb.h"
-#include "chrome/common/safe_browsing/csd.pb.h"
 #include "components/data_use_measurement/core/data_use_user_data.h"
 #include "components/prefs/pref_service.h"
 #include "components/safe_browsing/common/safebrowsing_messages.h"
+#include "components/safe_browsing/csd.pb.h"
 #include "components/safe_browsing_db/safe_browsing_prefs.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/notification_service.h"
diff --git a/chrome/browser/safe_browsing/client_side_detection_service_unittest.cc b/chrome/browser/safe_browsing/client_side_detection_service_unittest.cc
index 9498d88..b2a4889 100644
--- a/chrome/browser/safe_browsing/client_side_detection_service_unittest.cc
+++ b/chrome/browser/safe_browsing/client_side_detection_service_unittest.cc
@@ -22,7 +22,7 @@
 #include "base/strings/string_number_conversions.h"
 #include "base/time/time.h"
 #include "chrome/common/safe_browsing/client_model.pb.h"
-#include "chrome/common/safe_browsing/csd.pb.h"
+#include "components/safe_browsing/csd.pb.h"
 #include "components/variations/variations_associated_data.h"
 #include "content/public/test/test_browser_thread.h"
 #include "crypto/sha2.h"
diff --git a/chrome/browser/safe_browsing/client_side_model_loader.cc b/chrome/browser/safe_browsing/client_side_model_loader.cc
index 30e181a..bd2418b 100644
--- a/chrome/browser/safe_browsing/client_side_model_loader.cc
+++ b/chrome/browser/safe_browsing/client_side_model_loader.cc
@@ -17,10 +17,10 @@
 #include "base/time/time.h"
 #include "chrome/browser/safe_browsing/protocol_manager.h"
 #include "chrome/common/safe_browsing/client_model.pb.h"
-#include "chrome/common/safe_browsing/csd.pb.h"
 #include "components/data_use_measurement/core/data_use_user_data.h"
 #include "components/safe_browsing/common/safebrowsing_messages.h"
 #include "components/safe_browsing/common/safebrowsing_switches.h"
+#include "components/safe_browsing/csd.pb.h"
 #include "components/variations/variations_associated_data.h"
 #include "net/http/http_response_headers.h"
 #include "net/http/http_status_code.h"
diff --git a/chrome/browser/safe_browsing/client_side_model_loader_unittest.cc b/chrome/browser/safe_browsing/client_side_model_loader_unittest.cc
index 8afe6e8..f30d5590 100644
--- a/chrome/browser/safe_browsing/client_side_model_loader_unittest.cc
+++ b/chrome/browser/safe_browsing/client_side_model_loader_unittest.cc
@@ -17,7 +17,7 @@
 #include "base/strings/string_number_conversions.h"
 #include "base/time/time.h"
 #include "chrome/common/safe_browsing/client_model.pb.h"
-#include "chrome/common/safe_browsing/csd.pb.h"
+#include "components/safe_browsing/csd.pb.h"
 #include "components/variations/variations_associated_data.h"
 #include "content/public/test/test_browser_thread_bundle.h"
 #include "net/http/http_status_code.h"
diff --git a/chrome/browser/safe_browsing/download_feedback.cc b/chrome/browser/safe_browsing/download_feedback.cc
index 1b2ca55..0edcc4bd 100644
--- a/chrome/browser/safe_browsing/download_feedback.cc
+++ b/chrome/browser/safe_browsing/download_feedback.cc
@@ -10,7 +10,7 @@
 #include "base/memory/ptr_util.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/task_runner.h"
-#include "chrome/common/safe_browsing/csd.pb.h"
+#include "components/safe_browsing/csd.pb.h"
 #include "content/public/browser/browser_thread.h"
 #include "net/base/net_errors.h"
 
diff --git a/chrome/browser/safe_browsing/download_feedback_unittest.cc b/chrome/browser/safe_browsing/download_feedback_unittest.cc
index 4cf20b7d..f18adfeb 100644
--- a/chrome/browser/safe_browsing/download_feedback_unittest.cc
+++ b/chrome/browser/safe_browsing/download_feedback_unittest.cc
@@ -11,7 +11,7 @@
 #include "base/run_loop.h"
 #include "base/single_thread_task_runner.h"
 #include "chrome/browser/safe_browsing/two_phase_uploader.h"
-#include "chrome/common/safe_browsing/csd.pb.h"
+#include "components/safe_browsing/csd.pb.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/test/test_browser_thread_bundle.h"
 #include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
diff --git a/chrome/browser/safe_browsing/download_protection_service.cc b/chrome/browser/safe_browsing/download_protection_service.cc
index c9f6953..1a199a0 100644
--- a/chrome/browser/safe_browsing/download_protection_service.cc
+++ b/chrome/browser/safe_browsing/download_protection_service.cc
@@ -45,7 +45,6 @@
 #include "chrome/browser/ui/browser_list.h"
 #include "chrome/common/pref_names.h"
 #include "chrome/common/safe_browsing/binary_feature_extractor.h"
-#include "chrome/common/safe_browsing/csd.pb.h"
 #include "chrome/common/safe_browsing/download_protection_util.h"
 #include "chrome/common/safe_browsing/file_type_policies.h"
 #include "chrome/common/safe_browsing/zip_analyzer_results.h"
@@ -55,6 +54,7 @@
 #include "components/history/core/browser/history_service.h"
 #include "components/prefs/pref_service.h"
 #include "components/safe_browsing/common/safebrowsing_switches.h"
+#include "components/safe_browsing/csd.pb.h"
 #include "components/safe_browsing_db/safe_browsing_prefs.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/download_item.h"
diff --git a/chrome/browser/safe_browsing/download_protection_service_unittest.cc b/chrome/browser/safe_browsing/download_protection_service_unittest.cc
index 5c40564..c88eecb 100644
--- a/chrome/browser/safe_browsing/download_protection_service_unittest.cc
+++ b/chrome/browser/safe_browsing/download_protection_service_unittest.cc
@@ -34,12 +34,12 @@
 #include "chrome/browser/safe_browsing/local_database_manager.h"
 #include "chrome/browser/safe_browsing/safe_browsing_service.h"
 #include "chrome/common/safe_browsing/binary_feature_extractor.h"
-#include "chrome/common/safe_browsing/csd.pb.h"
 #include "chrome/common/safe_browsing/file_type_policies_test_util.h"
 #include "chrome/test/base/testing_profile.h"
 #include "components/history/core/browser/history_service.h"
 #include "components/prefs/pref_service.h"
 #include "components/safe_browsing/common/safebrowsing_switches.h"
+#include "components/safe_browsing/csd.pb.h"
 #include "components/safe_browsing_db/database_manager.h"
 #include "components/safe_browsing_db/safe_browsing_prefs.h"
 #include "components/safe_browsing_db/test_database_manager.h"
diff --git a/chrome/browser/safe_browsing/incident_reporting/binary_integrity_analyzer.cc b/chrome/browser/safe_browsing/incident_reporting/binary_integrity_analyzer.cc
index 3ae87d2..bf35ec2 100644
--- a/chrome/browser/safe_browsing/incident_reporting/binary_integrity_analyzer.cc
+++ b/chrome/browser/safe_browsing/incident_reporting/binary_integrity_analyzer.cc
@@ -20,7 +20,7 @@
 #include "chrome/browser/safe_browsing/incident_reporting/binary_integrity_incident.h"
 #include "chrome/browser/safe_browsing/incident_reporting/incident_receiver.h"
 #include "chrome/browser/safe_browsing/safe_browsing_service.h"
-#include "chrome/common/safe_browsing/csd.pb.h"
+#include "components/safe_browsing/csd.pb.h"
 
 namespace safe_browsing {
 
diff --git a/chrome/browser/safe_browsing/incident_reporting/binary_integrity_analyzer_mac.cc b/chrome/browser/safe_browsing/incident_reporting/binary_integrity_analyzer_mac.cc
index 9cae1aa2..80dc4812 100644
--- a/chrome/browser/safe_browsing/incident_reporting/binary_integrity_analyzer_mac.cc
+++ b/chrome/browser/safe_browsing/incident_reporting/binary_integrity_analyzer_mac.cc
@@ -14,7 +14,7 @@
 #include "chrome/browser/safe_browsing/incident_reporting/binary_integrity_incident.h"
 #include "chrome/browser/safe_browsing/incident_reporting/incident_receiver.h"
 #include "chrome/browser/safe_browsing/signature_evaluator_mac.h"
-#include "chrome/common/safe_browsing/csd.pb.h"
+#include "components/safe_browsing/csd.pb.h"
 
 #define DEVELOPER_ID_APPLICATION_OID "field.1.2.840.113635.100.6.1.13"
 #define DEVELOPER_ID_INTERMEDIATE_OID "field.1.2.840.113635.100.6.2.6"
diff --git a/chrome/browser/safe_browsing/incident_reporting/binary_integrity_analyzer_mac_unittest.cc b/chrome/browser/safe_browsing/incident_reporting/binary_integrity_analyzer_mac_unittest.cc
index 9eb43c79..469b736 100644
--- a/chrome/browser/safe_browsing/incident_reporting/binary_integrity_analyzer_mac_unittest.cc
+++ b/chrome/browser/safe_browsing/incident_reporting/binary_integrity_analyzer_mac_unittest.cc
@@ -16,7 +16,7 @@
 #include "chrome/browser/safe_browsing/incident_reporting/incident.h"
 #include "chrome/browser/safe_browsing/incident_reporting/mock_incident_receiver.h"
 #include "chrome/common/chrome_paths.h"
-#include "chrome/common/safe_browsing/csd.pb.h"
+#include "components/safe_browsing/csd.pb.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
diff --git a/chrome/browser/safe_browsing/incident_reporting/binary_integrity_analyzer_win.cc b/chrome/browser/safe_browsing/incident_reporting/binary_integrity_analyzer_win.cc
index d8c45f8..3b6e7ca 100644
--- a/chrome/browser/safe_browsing/incident_reporting/binary_integrity_analyzer_win.cc
+++ b/chrome/browser/safe_browsing/incident_reporting/binary_integrity_analyzer_win.cc
@@ -18,7 +18,7 @@
 #include "chrome/browser/safe_browsing/incident_reporting/incident_receiver.h"
 #include "chrome/common/chrome_version.h"
 #include "chrome/common/safe_browsing/binary_feature_extractor.h"
-#include "chrome/common/safe_browsing/csd.pb.h"
+#include "components/safe_browsing/csd.pb.h"
 
 namespace safe_browsing {
 
diff --git a/chrome/browser/safe_browsing/incident_reporting/binary_integrity_analyzer_win_unittest.cc b/chrome/browser/safe_browsing/incident_reporting/binary_integrity_analyzer_win_unittest.cc
index 6ff204f..2c17aa4 100644
--- a/chrome/browser/safe_browsing/incident_reporting/binary_integrity_analyzer_win_unittest.cc
+++ b/chrome/browser/safe_browsing/incident_reporting/binary_integrity_analyzer_win_unittest.cc
@@ -16,7 +16,7 @@
 #include "chrome/browser/safe_browsing/incident_reporting/mock_incident_receiver.h"
 #include "chrome/common/chrome_paths.h"
 #include "chrome/common/chrome_version.h"
-#include "chrome/common/safe_browsing/csd.pb.h"
+#include "components/safe_browsing/csd.pb.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
diff --git a/chrome/browser/safe_browsing/incident_reporting/binary_integrity_incident.cc b/chrome/browser/safe_browsing/incident_reporting/binary_integrity_incident.cc
index 35e0763..a914bfba 100644
--- a/chrome/browser/safe_browsing/incident_reporting/binary_integrity_incident.cc
+++ b/chrome/browser/safe_browsing/incident_reporting/binary_integrity_incident.cc
@@ -6,7 +6,7 @@
 
 #include "base/logging.h"
 #include "chrome/browser/safe_browsing/incident_reporting/incident_handler_util.h"
-#include "chrome/common/safe_browsing/csd.pb.h"
+#include "components/safe_browsing/csd.pb.h"
 
 namespace safe_browsing {
 
diff --git a/chrome/browser/safe_browsing/incident_reporting/binary_integrity_incident_unittest.cc b/chrome/browser/safe_browsing/incident_reporting/binary_integrity_incident_unittest.cc
index a6c4503..bb86600 100644
--- a/chrome/browser/safe_browsing/incident_reporting/binary_integrity_incident_unittest.cc
+++ b/chrome/browser/safe_browsing/incident_reporting/binary_integrity_incident_unittest.cc
@@ -11,7 +11,7 @@
 
 #include "base/macros.h"
 #include "base/memory/ptr_util.h"
-#include "chrome/common/safe_browsing/csd.pb.h"
+#include "components/safe_browsing/csd.pb.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace safe_browsing {
diff --git a/chrome/browser/safe_browsing/incident_reporting/blacklist_load_analyzer_win.cc b/chrome/browser/safe_browsing/incident_reporting/blacklist_load_analyzer_win.cc
index 97af8bfe..4349fc84 100644
--- a/chrome/browser/safe_browsing/incident_reporting/blacklist_load_analyzer_win.cc
+++ b/chrome/browser/safe_browsing/incident_reporting/blacklist_load_analyzer_win.cc
@@ -21,8 +21,8 @@
 #include "chrome/browser/safe_browsing/path_sanitizer.h"
 #include "chrome/browser/safe_browsing/safe_browsing_service.h"
 #include "chrome/common/safe_browsing/binary_feature_extractor.h"
-#include "chrome/common/safe_browsing/csd.pb.h"
 #include "chrome_elf/blacklist/blacklist.h"
+#include "components/safe_browsing/csd.pb.h"
 
 namespace safe_browsing {
 
diff --git a/chrome/browser/safe_browsing/incident_reporting/blacklist_load_incident.cc b/chrome/browser/safe_browsing/incident_reporting/blacklist_load_incident.cc
index d0b0502..c62e2b6f 100644
--- a/chrome/browser/safe_browsing/incident_reporting/blacklist_load_incident.cc
+++ b/chrome/browser/safe_browsing/incident_reporting/blacklist_load_incident.cc
@@ -6,7 +6,7 @@
 
 #include "base/logging.h"
 #include "chrome/browser/safe_browsing/incident_reporting/incident_handler_util.h"
-#include "chrome/common/safe_browsing/csd.pb.h"
+#include "components/safe_browsing/csd.pb.h"
 
 namespace safe_browsing {
 
diff --git a/chrome/browser/safe_browsing/incident_reporting/blacklist_load_incident_unittest.cc b/chrome/browser/safe_browsing/incident_reporting/blacklist_load_incident_unittest.cc
index ff597e0..cd88c8c 100644
--- a/chrome/browser/safe_browsing/incident_reporting/blacklist_load_incident_unittest.cc
+++ b/chrome/browser/safe_browsing/incident_reporting/blacklist_load_incident_unittest.cc
@@ -8,7 +8,7 @@
 #include <utility>
 
 #include "base/memory/ptr_util.h"
-#include "chrome/common/safe_browsing/csd.pb.h"
+#include "components/safe_browsing/csd.pb.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace safe_browsing {
diff --git a/chrome/browser/safe_browsing/incident_reporting/download_metadata_manager.cc b/chrome/browser/safe_browsing/incident_reporting/download_metadata_manager.cc
index 287d293..d503f1e1 100644
--- a/chrome/browser/safe_browsing/incident_reporting/download_metadata_manager.cc
+++ b/chrome/browser/safe_browsing/incident_reporting/download_metadata_manager.cc
@@ -21,7 +21,7 @@
 #include "base/metrics/histogram_macros.h"
 #include "base/sequenced_task_runner.h"
 #include "base/threading/sequenced_worker_pool.h"
-#include "chrome/common/safe_browsing/csd.pb.h"
+#include "components/safe_browsing/csd.pb.h"
 #include "content/public/browser/browser_context.h"
 #include "content/public/browser/download_item.h"
 
diff --git a/chrome/browser/safe_browsing/incident_reporting/download_metadata_manager_unittest.cc b/chrome/browser/safe_browsing/incident_reporting/download_metadata_manager_unittest.cc
index af0ed6a..d06c0f3 100644
--- a/chrome/browser/safe_browsing/incident_reporting/download_metadata_manager_unittest.cc
+++ b/chrome/browser/safe_browsing/incident_reporting/download_metadata_manager_unittest.cc
@@ -15,8 +15,8 @@
 #include "base/files/file_util.h"
 #include "base/run_loop.h"
 #include "base/threading/thread_task_runner_handle.h"
-#include "chrome/common/safe_browsing/csd.pb.h"
 #include "chrome/test/base/testing_profile.h"
+#include "components/safe_browsing/csd.pb.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/download_manager.h"
 #include "content/public/test/mock_download_item.h"
diff --git a/chrome/browser/safe_browsing/incident_reporting/environment_data_collection.cc b/chrome/browser/safe_browsing/incident_reporting/environment_data_collection.cc
index 97b8344e..faecf27ca 100644
--- a/chrome/browser/safe_browsing/incident_reporting/environment_data_collection.cc
+++ b/chrome/browser/safe_browsing/incident_reporting/environment_data_collection.cc
@@ -15,7 +15,7 @@
 #include "base/threading/sequenced_worker_pool.h"
 #include "build/build_config.h"
 #include "chrome/common/channel_info.h"
-#include "chrome/common/safe_browsing/csd.pb.h"
+#include "components/safe_browsing/csd.pb.h"
 #include "components/version_info/version_info.h"
 #include "content/public/browser/browser_thread.h"
 
diff --git a/chrome/browser/safe_browsing/incident_reporting/environment_data_collection_win.cc b/chrome/browser/safe_browsing/incident_reporting/environment_data_collection_win.cc
index c108780..63211c2 100644
--- a/chrome/browser/safe_browsing/incident_reporting/environment_data_collection_win.cc
+++ b/chrome/browser/safe_browsing/incident_reporting/environment_data_collection_win.cc
@@ -25,8 +25,8 @@
 #include "chrome/browser/safe_browsing/incident_reporting/module_integrity_verifier_win.h"
 #include "chrome/browser/safe_browsing/path_sanitizer.h"
 #include "chrome/common/safe_browsing/binary_feature_extractor.h"
-#include "chrome/common/safe_browsing/csd.pb.h"
 #include "chrome_elf/chrome_elf_constants.h"
+#include "components/safe_browsing/csd.pb.h"
 #include "components/variations/variations_associated_data.h"
 
 namespace safe_browsing {
diff --git a/chrome/browser/safe_browsing/incident_reporting/environment_data_collection_win_unittest.cc b/chrome/browser/safe_browsing/incident_reporting/environment_data_collection_win_unittest.cc
index e918e61b..46c05a50 100644
--- a/chrome/browser/safe_browsing/incident_reporting/environment_data_collection_win_unittest.cc
+++ b/chrome/browser/safe_browsing/incident_reporting/environment_data_collection_win_unittest.cc
@@ -20,8 +20,8 @@
 #include "chrome/browser/safe_browsing/incident_reporting/module_integrity_unittest_util_win.h"
 #include "chrome/browser/safe_browsing/incident_reporting/module_integrity_verifier_win.h"
 #include "chrome/browser/safe_browsing/path_sanitizer.h"
-#include "chrome/common/safe_browsing/csd.pb.h"
 #include "chrome_elf/chrome_elf_constants.h"
+#include "components/safe_browsing/csd.pb.h"
 #include "net/base/winsock_init.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
diff --git a/chrome/browser/safe_browsing/incident_reporting/extension_data_collection.cc b/chrome/browser/safe_browsing/incident_reporting/extension_data_collection.cc
index d2723ab..d60f976 100644
--- a/chrome/browser/safe_browsing/incident_reporting/extension_data_collection.cc
+++ b/chrome/browser/safe_browsing/incident_reporting/extension_data_collection.cc
@@ -12,7 +12,7 @@
 #include "chrome/browser/extensions/install_signer.h"
 #include "chrome/browser/profiles/profile_manager.h"
 #include "chrome/browser/safe_browsing/incident_reporting/incident_reporting_service.h"
-#include "chrome/common/safe_browsing/csd.pb.h"
+#include "components/safe_browsing/csd.pb.h"
 #include "extensions/browser/extension_prefs.h"
 #include "extensions/browser/extension_prefs_factory.h"
 #include "extensions/browser/extension_registry.h"
diff --git a/chrome/browser/safe_browsing/incident_reporting/extension_data_collection_unittest.cc b/chrome/browser/safe_browsing/incident_reporting/extension_data_collection_unittest.cc
index c5141eb..07fadbc4 100644
--- a/chrome/browser/safe_browsing/incident_reporting/extension_data_collection_unittest.cc
+++ b/chrome/browser/safe_browsing/incident_reporting/extension_data_collection_unittest.cc
@@ -17,10 +17,10 @@
 #include "chrome/browser/prefs/browser_prefs.h"
 #include "chrome/browser/profiles/profile_manager.h"
 #include "chrome/common/pref_names.h"
-#include "chrome/common/safe_browsing/csd.pb.h"
 #include "chrome/test/base/testing_browser_process.h"
 #include "chrome/test/base/testing_profile.h"
 #include "chrome/test/base/testing_profile_manager.h"
+#include "components/safe_browsing/csd.pb.h"
 #include "components/safe_browsing_db/safe_browsing_prefs.h"
 #include "components/sync_preferences/testing_pref_service_syncable.h"
 #include "content/public/test/test_browser_thread_bundle.h"
diff --git a/chrome/browser/safe_browsing/incident_reporting/incident.cc b/chrome/browser/safe_browsing/incident_reporting/incident.cc
index f121dd41..21d0b1b8 100644
--- a/chrome/browser/safe_browsing/incident_reporting/incident.cc
+++ b/chrome/browser/safe_browsing/incident_reporting/incident.cc
@@ -8,7 +8,7 @@
 
 #include "base/logging.h"
 #include "base/time/time.h"
-#include "chrome/common/safe_browsing/csd.pb.h"
+#include "components/safe_browsing/csd.pb.h"
 
 namespace safe_browsing {
 
diff --git a/chrome/browser/safe_browsing/incident_reporting/incident_report_uploader_impl.cc b/chrome/browser/safe_browsing/incident_reporting/incident_report_uploader_impl.cc
index 59e1ee3..735522ff 100644
--- a/chrome/browser/safe_browsing/incident_reporting/incident_report_uploader_impl.cc
+++ b/chrome/browser/safe_browsing/incident_reporting/incident_report_uploader_impl.cc
@@ -7,8 +7,8 @@
 #include <utility>
 
 #include "base/metrics/histogram_macros.h"
-#include "chrome/common/safe_browsing/csd.pb.h"
 #include "components/data_use_measurement/core/data_use_user_data.h"
+#include "components/safe_browsing/csd.pb.h"
 #include "google_apis/google_api_keys.h"
 #include "net/base/escape.h"
 #include "net/base/load_flags.h"
diff --git a/chrome/browser/safe_browsing/incident_reporting/incident_report_uploader_impl_unittest.cc b/chrome/browser/safe_browsing/incident_reporting/incident_report_uploader_impl_unittest.cc
index d55b967..f50fdc44 100644
--- a/chrome/browser/safe_browsing/incident_reporting/incident_report_uploader_impl_unittest.cc
+++ b/chrome/browser/safe_browsing/incident_reporting/incident_report_uploader_impl_unittest.cc
@@ -8,7 +8,7 @@
 #include <utility>
 
 #include "base/test/test_simple_task_runner.h"
-#include "chrome/common/safe_browsing/csd.pb.h"
+#include "components/safe_browsing/csd.pb.h"
 #include "net/base/load_flags.h"
 #include "net/http/http_status_code.h"
 #include "net/url_request/test_url_fetcher_factory.h"
diff --git a/chrome/browser/safe_browsing/incident_reporting/incident_reporting_service.cc b/chrome/browser/safe_browsing/incident_reporting/incident_reporting_service.cc
index a8449df..7c8bf2d 100644
--- a/chrome/browser/safe_browsing/incident_reporting/incident_reporting_service.cc
+++ b/chrome/browser/safe_browsing/incident_reporting/incident_reporting_service.cc
@@ -34,8 +34,8 @@
 #include "chrome/browser/safe_browsing/safe_browsing_service.h"
 #include "chrome/common/chrome_features.h"
 #include "chrome/common/pref_names.h"
-#include "chrome/common/safe_browsing/csd.pb.h"
 #include "components/prefs/pref_service.h"
+#include "components/safe_browsing/csd.pb.h"
 #include "components/safe_browsing_db/database_manager.h"
 #include "components/safe_browsing_db/safe_browsing_prefs.h"
 #include "content/public/browser/browser_thread.h"
diff --git a/chrome/browser/safe_browsing/incident_reporting/incident_reporting_service_unittest.cc b/chrome/browser/safe_browsing/incident_reporting/incident_reporting_service_unittest.cc
index 515335c..0212651 100644
--- a/chrome/browser/safe_browsing/incident_reporting/incident_reporting_service_unittest.cc
+++ b/chrome/browser/safe_browsing/incident_reporting/incident_reporting_service_unittest.cc
@@ -30,10 +30,10 @@
 #include "chrome/browser/safe_browsing/incident_reporting/last_download_finder.h"
 #include "chrome/browser/safe_browsing/incident_reporting/tracked_preference_incident.h"
 #include "chrome/common/pref_names.h"
-#include "chrome/common/safe_browsing/csd.pb.h"
 #include "chrome/test/base/testing_browser_process.h"
 #include "chrome/test/base/testing_profile.h"
 #include "chrome/test/base/testing_profile_manager.h"
+#include "components/safe_browsing/csd.pb.h"
 #include "components/safe_browsing_db/safe_browsing_prefs.h"
 #include "components/sync_preferences/testing_pref_service_syncable.h"
 #include "content/public/test/test_browser_thread_bundle.h"
diff --git a/chrome/browser/safe_browsing/incident_reporting/last_download_finder.cc b/chrome/browser/safe_browsing/incident_reporting/last_download_finder.cc
index 0199bc0..96d6612a 100644
--- a/chrome/browser/safe_browsing/incident_reporting/last_download_finder.cc
+++ b/chrome/browser/safe_browsing/incident_reporting/last_download_finder.cc
@@ -23,12 +23,12 @@
 #include "chrome/browser/profiles/profile_manager.h"
 #include "chrome/browser/safe_browsing/incident_reporting/incident_reporting_service.h"
 #include "chrome/common/pref_names.h"
-#include "chrome/common/safe_browsing/csd.pb.h"
 #include "chrome/common/safe_browsing/download_protection_util.h"
 #include "chrome/common/safe_browsing/file_type_policies.h"
 #include "components/history/core/browser/download_constants.h"
 #include "components/history/core/browser/history_service.h"
 #include "components/prefs/pref_service.h"
+#include "components/safe_browsing/csd.pb.h"
 #include "content/public/browser/notification_details.h"
 #include "content/public/browser/notification_service.h"
 #include "content/public/browser/notification_source.h"
diff --git a/chrome/browser/safe_browsing/incident_reporting/last_download_finder_unittest.cc b/chrome/browser/safe_browsing/incident_reporting/last_download_finder_unittest.cc
index 3fb14fa..ba840982 100644
--- a/chrome/browser/safe_browsing/incident_reporting/last_download_finder_unittest.cc
+++ b/chrome/browser/safe_browsing/incident_reporting/last_download_finder_unittest.cc
@@ -32,7 +32,6 @@
 #include "chrome/browser/prefs/browser_prefs.h"
 #include "chrome/browser/profiles/profile_manager.h"
 #include "chrome/common/pref_names.h"
-#include "chrome/common/safe_browsing/csd.pb.h"
 #include "chrome/test/base/testing_browser_process.h"
 #include "chrome/test/base/testing_profile.h"
 #include "chrome/test/base/testing_profile_manager.h"
@@ -44,6 +43,7 @@
 #include "components/history/core/browser/history_constants.h"
 #include "components/history/core/browser/history_database_params.h"
 #include "components/history/core/browser/history_service.h"
+#include "components/safe_browsing/csd.pb.h"
 #include "components/safe_browsing_db/safe_browsing_prefs.h"
 #include "components/sync_preferences/testing_pref_service_syncable.h"
 #include "content/public/test/test_browser_thread_bundle.h"
diff --git a/chrome/browser/safe_browsing/incident_reporting/module_integrity_verifier_win.cc b/chrome/browser/safe_browsing/incident_reporting/module_integrity_verifier_win.cc
index 286e0617..c1d99e93 100644
--- a/chrome/browser/safe_browsing/incident_reporting/module_integrity_verifier_win.cc
+++ b/chrome/browser/safe_browsing/incident_reporting/module_integrity_verifier_win.cc
@@ -17,7 +17,7 @@
 #include "base/scoped_native_library.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/win/pe_image.h"
-#include "chrome/common/safe_browsing/csd.pb.h"
+#include "components/safe_browsing/csd.pb.h"
 
 namespace safe_browsing {
 
diff --git a/chrome/browser/safe_browsing/incident_reporting/module_integrity_verifier_win_unittest.cc b/chrome/browser/safe_browsing/incident_reporting/module_integrity_verifier_win_unittest.cc
index 4cd6c656..9d91af9a 100644
--- a/chrome/browser/safe_browsing/incident_reporting/module_integrity_verifier_win_unittest.cc
+++ b/chrome/browser/safe_browsing/incident_reporting/module_integrity_verifier_win_unittest.cc
@@ -21,7 +21,7 @@
 #include "base/strings/utf_string_conversions.h"
 #include "base/win/pe_image.h"
 #include "chrome/browser/safe_browsing/incident_reporting/module_integrity_unittest_util_win.h"
-#include "chrome/common/safe_browsing/csd.pb.h"
+#include "components/safe_browsing/csd.pb.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace safe_browsing {
diff --git a/chrome/browser/safe_browsing/incident_reporting/module_load_analyzer_win.cc b/chrome/browser/safe_browsing/incident_reporting/module_load_analyzer_win.cc
index 941dcd1..c39055b 100644
--- a/chrome/browser/safe_browsing/incident_reporting/module_load_analyzer_win.cc
+++ b/chrome/browser/safe_browsing/incident_reporting/module_load_analyzer_win.cc
@@ -22,7 +22,7 @@
 #include "chrome/browser/safe_browsing/incident_reporting/suspicious_module_incident.h"
 #include "chrome/browser/safe_browsing/path_sanitizer.h"
 #include "chrome/common/safe_browsing/binary_feature_extractor.h"
-#include "chrome/common/safe_browsing/csd.pb.h"
+#include "components/safe_browsing/csd.pb.h"
 #include "content/public/browser/browser_thread.h"
 
 #if defined(SAFE_BROWSING_DB_LOCAL)
diff --git a/chrome/browser/safe_browsing/incident_reporting/module_load_analyzer_win_unittest.cc b/chrome/browser/safe_browsing/incident_reporting/module_load_analyzer_win_unittest.cc
index fe5e886..834ce4f 100644
--- a/chrome/browser/safe_browsing/incident_reporting/module_load_analyzer_win_unittest.cc
+++ b/chrome/browser/safe_browsing/incident_reporting/module_load_analyzer_win_unittest.cc
@@ -16,7 +16,7 @@
 #include "chrome/browser/safe_browsing/incident_reporting/incident.h"
 #include "chrome/browser/safe_browsing/incident_reporting/incident_receiver.h"
 #include "chrome/browser/safe_browsing/incident_reporting/mock_incident_receiver.h"
-#include "chrome/common/safe_browsing/csd.pb.h"
+#include "components/safe_browsing/csd.pb.h"
 #include "components/safe_browsing_db/database_manager.h"
 #include "components/safe_browsing_db/test_database_manager.h"
 #include "content/public/test/test_browser_thread_bundle.h"
diff --git a/chrome/browser/safe_browsing/incident_reporting/preference_validation_delegate.cc b/chrome/browser/safe_browsing/incident_reporting/preference_validation_delegate.cc
index 0b7d284..636a8fe7 100644
--- a/chrome/browser/safe_browsing/incident_reporting/preference_validation_delegate.cc
+++ b/chrome/browser/safe_browsing/incident_reporting/preference_validation_delegate.cc
@@ -12,7 +12,7 @@
 #include "base/memory/ptr_util.h"
 #include "chrome/browser/safe_browsing/incident_reporting/incident_receiver.h"
 #include "chrome/browser/safe_browsing/incident_reporting/tracked_preference_incident.h"
-#include "chrome/common/safe_browsing/csd.pb.h"
+#include "components/safe_browsing/csd.pb.h"
 #include "components/user_prefs/tracked/pref_hash_store_transaction.h"
 #include "components/user_prefs/tracked/tracked_preference_helper.h"
 
diff --git a/chrome/browser/safe_browsing/incident_reporting/preference_validation_delegate_unittest.cc b/chrome/browser/safe_browsing/incident_reporting/preference_validation_delegate_unittest.cc
index 8fb94e4..1f5691b 100644
--- a/chrome/browser/safe_browsing/incident_reporting/preference_validation_delegate_unittest.cc
+++ b/chrome/browser/safe_browsing/incident_reporting/preference_validation_delegate_unittest.cc
@@ -16,7 +16,7 @@
 #include "base/values.h"
 #include "chrome/browser/safe_browsing/incident_reporting/incident.h"
 #include "chrome/browser/safe_browsing/incident_reporting/mock_incident_receiver.h"
-#include "chrome/common/safe_browsing/csd.pb.h"
+#include "components/safe_browsing/csd.pb.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
diff --git a/chrome/browser/safe_browsing/incident_reporting/resource_request_detector.cc b/chrome/browser/safe_browsing/incident_reporting/resource_request_detector.cc
index 4515c990..9c8351fe 100644
--- a/chrome/browser/safe_browsing/incident_reporting/resource_request_detector.cc
+++ b/chrome/browser/safe_browsing/incident_reporting/resource_request_detector.cc
@@ -10,7 +10,7 @@
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/safe_browsing/incident_reporting/incident_receiver.h"
 #include "chrome/browser/safe_browsing/incident_reporting/resource_request_incident.h"
-#include "chrome/common/safe_browsing/csd.pb.h"
+#include "components/safe_browsing/csd.pb.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/render_frame_host.h"
 #include "content/public/browser/render_process_host.h"
diff --git a/chrome/browser/safe_browsing/incident_reporting/resource_request_detector_unittest.cc b/chrome/browser/safe_browsing/incident_reporting/resource_request_detector_unittest.cc
index cbaf712..90802044 100644
--- a/chrome/browser/safe_browsing/incident_reporting/resource_request_detector_unittest.cc
+++ b/chrome/browser/safe_browsing/incident_reporting/resource_request_detector_unittest.cc
@@ -12,7 +12,7 @@
 #include "chrome/browser/safe_browsing/incident_reporting/incident.h"
 #include "chrome/browser/safe_browsing/incident_reporting/incident_receiver.h"
 #include "chrome/browser/safe_browsing/incident_reporting/mock_incident_receiver.h"
-#include "chrome/common/safe_browsing/csd.pb.h"
+#include "components/safe_browsing/csd.pb.h"
 #include "components/safe_browsing_db/test_database_manager.h"
 #include "content/public/browser/resource_request_info.h"
 #include "content/public/common/previews_state.h"
diff --git a/chrome/browser/safe_browsing/incident_reporting/resource_request_incident.cc b/chrome/browser/safe_browsing/incident_reporting/resource_request_incident.cc
index 8b8a466..109b499 100644
--- a/chrome/browser/safe_browsing/incident_reporting/resource_request_incident.cc
+++ b/chrome/browser/safe_browsing/incident_reporting/resource_request_incident.cc
@@ -6,7 +6,7 @@
 
 #include "base/logging.h"
 #include "chrome/browser/safe_browsing/incident_reporting/incident_handler_util.h"
-#include "chrome/common/safe_browsing/csd.pb.h"
+#include "components/safe_browsing/csd.pb.h"
 
 namespace safe_browsing {
 
diff --git a/chrome/browser/safe_browsing/incident_reporting/suspicious_module_incident.cc b/chrome/browser/safe_browsing/incident_reporting/suspicious_module_incident.cc
index 26d7936..2f5589e 100644
--- a/chrome/browser/safe_browsing/incident_reporting/suspicious_module_incident.cc
+++ b/chrome/browser/safe_browsing/incident_reporting/suspicious_module_incident.cc
@@ -6,7 +6,7 @@
 
 #include "base/logging.h"
 #include "chrome/browser/safe_browsing/incident_reporting/incident_handler_util.h"
-#include "chrome/common/safe_browsing/csd.pb.h"
+#include "components/safe_browsing/csd.pb.h"
 
 namespace safe_browsing {
 
diff --git a/chrome/browser/safe_browsing/incident_reporting/suspicious_module_incident_unittest.cc b/chrome/browser/safe_browsing/incident_reporting/suspicious_module_incident_unittest.cc
index 88d34b3..27450a5f 100644
--- a/chrome/browser/safe_browsing/incident_reporting/suspicious_module_incident_unittest.cc
+++ b/chrome/browser/safe_browsing/incident_reporting/suspicious_module_incident_unittest.cc
@@ -8,7 +8,7 @@
 #include <utility>
 
 #include "base/memory/ptr_util.h"
-#include "chrome/common/safe_browsing/csd.pb.h"
+#include "components/safe_browsing/csd.pb.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace safe_browsing {
diff --git a/chrome/browser/safe_browsing/incident_reporting/tracked_preference_incident.cc b/chrome/browser/safe_browsing/incident_reporting/tracked_preference_incident.cc
index 223895f4..bc999fe 100644
--- a/chrome/browser/safe_browsing/incident_reporting/tracked_preference_incident.cc
+++ b/chrome/browser/safe_browsing/incident_reporting/tracked_preference_incident.cc
@@ -6,7 +6,7 @@
 
 #include "base/logging.h"
 #include "chrome/browser/safe_browsing/incident_reporting/incident_handler_util.h"
-#include "chrome/common/safe_browsing/csd.pb.h"
+#include "components/safe_browsing/csd.pb.h"
 
 namespace safe_browsing {
 
diff --git a/chrome/browser/safe_browsing/incident_reporting/tracked_preference_incident_unittest.cc b/chrome/browser/safe_browsing/incident_reporting/tracked_preference_incident_unittest.cc
index 49d4b37..c5a9e335b 100644
--- a/chrome/browser/safe_browsing/incident_reporting/tracked_preference_incident_unittest.cc
+++ b/chrome/browser/safe_browsing/incident_reporting/tracked_preference_incident_unittest.cc
@@ -8,7 +8,7 @@
 #include <utility>
 
 #include "base/memory/ptr_util.h"
-#include "chrome/common/safe_browsing/csd.pb.h"
+#include "components/safe_browsing/csd.pb.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace safe_browsing {
diff --git a/chrome/browser/safe_browsing/incident_reporting/variations_seed_signature_analyzer.cc b/chrome/browser/safe_browsing/incident_reporting/variations_seed_signature_analyzer.cc
index fc3b1d0..9949152 100644
--- a/chrome/browser/safe_browsing/incident_reporting/variations_seed_signature_analyzer.cc
+++ b/chrome/browser/safe_browsing/incident_reporting/variations_seed_signature_analyzer.cc
@@ -14,7 +14,7 @@
 #include "chrome/browser/safe_browsing/incident_reporting/incident_receiver.h"
 #include "chrome/browser/safe_browsing/incident_reporting/variations_seed_signature_incident.h"
 #include "chrome/browser/safe_browsing/safe_browsing_service.h"
-#include "chrome/common/safe_browsing/csd.pb.h"
+#include "components/safe_browsing/csd.pb.h"
 #include "components/variations/service/variations_service.h"
 #include "content/public/browser/browser_thread.h"
 
diff --git a/chrome/browser/safe_browsing/incident_reporting/variations_seed_signature_incident.cc b/chrome/browser/safe_browsing/incident_reporting/variations_seed_signature_incident.cc
index c409117e..abd66c1 100644
--- a/chrome/browser/safe_browsing/incident_reporting/variations_seed_signature_incident.cc
+++ b/chrome/browser/safe_browsing/incident_reporting/variations_seed_signature_incident.cc
@@ -6,7 +6,7 @@
 
 #include "base/logging.h"
 #include "chrome/browser/safe_browsing/incident_reporting/incident_handler_util.h"
-#include "chrome/common/safe_browsing/csd.pb.h"
+#include "components/safe_browsing/csd.pb.h"
 
 namespace safe_browsing {
 
diff --git a/chrome/browser/safe_browsing/incident_reporting/variations_seed_signature_incident_unittest.cc b/chrome/browser/safe_browsing/incident_reporting/variations_seed_signature_incident_unittest.cc
index 27622f3..07b0261 100644
--- a/chrome/browser/safe_browsing/incident_reporting/variations_seed_signature_incident_unittest.cc
+++ b/chrome/browser/safe_browsing/incident_reporting/variations_seed_signature_incident_unittest.cc
@@ -8,7 +8,7 @@
 #include <utility>
 
 #include "base/memory/ptr_util.h"
-#include "chrome/common/safe_browsing/csd.pb.h"
+#include "components/safe_browsing/csd.pb.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace safe_browsing {
diff --git a/chrome/browser/safe_browsing/notification_image_reporter.cc b/chrome/browser/safe_browsing/notification_image_reporter.cc
index 984fc43..d9e53ac 100644
--- a/chrome/browser/safe_browsing/notification_image_reporter.cc
+++ b/chrome/browser/safe_browsing/notification_image_reporter.cc
@@ -18,7 +18,7 @@
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/safe_browsing/safe_browsing_service.h"
-#include "chrome/common/safe_browsing/csd.pb.h"
+#include "components/safe_browsing/csd.pb.h"
 #include "components/safe_browsing_db/database_manager.h"
 #include "components/safe_browsing_db/safe_browsing_prefs.h"
 #include "components/variations/variations_associated_data.h"
diff --git a/chrome/browser/safe_browsing/notification_image_reporter_unittest.cc b/chrome/browser/safe_browsing/notification_image_reporter_unittest.cc
index a24fcea..2e0a6baf 100644
--- a/chrome/browser/safe_browsing/notification_image_reporter_unittest.cc
+++ b/chrome/browser/safe_browsing/notification_image_reporter_unittest.cc
@@ -11,9 +11,9 @@
 #include "chrome/browser/safe_browsing/mock_permission_report_sender.h"
 #include "chrome/browser/safe_browsing/ping_manager.h"
 #include "chrome/browser/safe_browsing/test_safe_browsing_service.h"
-#include "chrome/common/safe_browsing/csd.pb.h"
 #include "chrome/test/base/testing_browser_process.h"
 #include "chrome/test/base/testing_profile.h"
+#include "components/safe_browsing/csd.pb.h"
 #include "components/safe_browsing_db/safe_browsing_prefs.h"
 #include "components/safe_browsing_db/test_database_manager.h"
 #include "content/public/browser/browser_thread.h"
diff --git a/chrome/browser/safe_browsing/safe_browsing_navigation_observer_manager.h b/chrome/browser/safe_browsing/safe_browsing_navigation_observer_manager.h
index cac35d20..f37985a8 100644
--- a/chrome/browser/safe_browsing/safe_browsing_navigation_observer_manager.h
+++ b/chrome/browser/safe_browsing/safe_browsing_navigation_observer_manager.h
@@ -8,7 +8,7 @@
 #include <deque>
 #include "base/feature_list.h"
 #include "base/supports_user_data.h"
-#include "chrome/common/safe_browsing/csd.pb.h"
+#include "components/safe_browsing/csd.pb.h"
 #include "content/public/browser/web_contents_observer.h"
 #include "third_party/protobuf/src/google/protobuf/repeated_field.h"
 #include "url/gurl.h"
diff --git a/chrome/browser/safe_browsing/signature_evaluator_mac.mm b/chrome/browser/safe_browsing/signature_evaluator_mac.mm
index 415aaf9..9ffcb66 100644
--- a/chrome/browser/safe_browsing/signature_evaluator_mac.mm
+++ b/chrome/browser/safe_browsing/signature_evaluator_mac.mm
@@ -18,8 +18,8 @@
 #include "base/strings/string_util.h"
 #include "base/strings/sys_string_conversions.h"
 #include "chrome/common/safe_browsing/binary_feature_extractor.h"
-#include "chrome/common/safe_browsing/csd.pb.h"
 #include "chrome/common/safe_browsing/mach_o_image_reader_mac.h"
+#include "components/safe_browsing/csd.pb.h"
 
 namespace safe_browsing {
 
diff --git a/chrome/browser/safe_browsing/signature_evaluator_mac_unittest.cc b/chrome/browser/safe_browsing/signature_evaluator_mac_unittest.cc
index 1ffcc58f..3041104 100644
--- a/chrome/browser/safe_browsing/signature_evaluator_mac_unittest.cc
+++ b/chrome/browser/safe_browsing/signature_evaluator_mac_unittest.cc
@@ -22,7 +22,7 @@
 #include "chrome/browser/safe_browsing/incident_reporting/incident.h"
 #include "chrome/browser/safe_browsing/incident_reporting/mock_incident_receiver.h"
 #include "chrome/common/chrome_paths.h"
-#include "chrome/common/safe_browsing/csd.pb.h"
+#include "components/safe_browsing/csd.pb.h"
 #include "testing/gmock/include/gmock/gmock-matchers.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
diff --git a/chrome/browser/safe_browsing/threat_details.h b/chrome/browser/safe_browsing/threat_details.h
index d1a8aec7..e5f6ac5 100644
--- a/chrome/browser/safe_browsing/threat_details.h
+++ b/chrome/browser/safe_browsing/threat_details.h
@@ -21,8 +21,8 @@
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
 #include "chrome/browser/safe_browsing/ui_manager.h"
-#include "chrome/common/safe_browsing/csd.pb.h"
 #include "components/safe_browsing/common/safebrowsing_types.h"
+#include "components/safe_browsing/csd.pb.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/web_contents_observer.h"
 #include "net/base/completion_callback.h"
diff --git a/chrome/browser/safe_browsing/threat_details_cache.cc b/chrome/browser/safe_browsing/threat_details_cache.cc
index 22aaaf302..462316fa 100644
--- a/chrome/browser/safe_browsing/threat_details_cache.cc
+++ b/chrome/browser/safe_browsing/threat_details_cache.cc
@@ -14,8 +14,8 @@
 #include "base/strings/string_util.h"
 #include "chrome/browser/safe_browsing/safe_browsing_service.h"
 #include "chrome/browser/safe_browsing/threat_details_cache.h"
-#include "chrome/common/safe_browsing/csd.pb.h"
 #include "components/data_use_measurement/core/data_use_user_data.h"
+#include "components/safe_browsing/csd.pb.h"
 #include "content/public/browser/browser_thread.h"
 #include "net/base/host_port_pair.h"
 #include "net/base/load_flags.h"
diff --git a/chrome/browser/safe_browsing/threat_details_cache.h b/chrome/browser/safe_browsing/threat_details_cache.h
index 01d07fc30..ed3b985c 100644
--- a/chrome/browser/safe_browsing/threat_details_cache.h
+++ b/chrome/browser/safe_browsing/threat_details_cache.h
@@ -15,7 +15,7 @@
 #include "base/containers/hash_tables.h"
 #include "base/memory/linked_ptr.h"
 #include "base/memory/ref_counted.h"
-#include "chrome/common/safe_browsing/csd.pb.h"
+#include "components/safe_browsing/csd.pb.h"
 #include "net/base/completion_callback.h"
 #include "net/url_request/url_fetcher_delegate.h"
 
diff --git a/chrome/browser/safe_browsing/threat_details_unittest.cc b/chrome/browser/safe_browsing/threat_details_unittest.cc
index ceea2128..79c3f49 100644
--- a/chrome/browser/safe_browsing/threat_details_unittest.cc
+++ b/chrome/browser/safe_browsing/threat_details_unittest.cc
@@ -18,12 +18,12 @@
 #include "chrome/browser/safe_browsing/threat_details.h"
 #include "chrome/browser/safe_browsing/threat_details_history.h"
 #include "chrome/browser/safe_browsing/ui_manager.h"
-#include "chrome/common/safe_browsing/csd.pb.h"
 #include "chrome/test/base/chrome_render_view_host_test_harness.h"
 #include "chrome/test/base/testing_profile.h"
 #include "components/history/core/browser/history_backend.h"
 #include "components/history/core/browser/history_service.h"
 #include "components/safe_browsing/common/safebrowsing_messages.h"
+#include "components/safe_browsing/csd.pb.h"
 #include "content/public/browser/render_process_host.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/test/web_contents_tester.h"
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn
index bcd20886..c46ac57 100644
--- a/chrome/browser/ui/BUILD.gn
+++ b/chrome/browser/ui/BUILD.gn
@@ -1216,6 +1216,7 @@
     deps += [
       "//chrome/browser/safe_browsing:chunk_proto",
       "//chrome/common/safe_browsing:proto",
+      "//components/safe_browsing:csd_proto",
     ]
   }
 
diff --git a/chrome/browser/ui/app_list/arc/arc_app_list_prefs.cc b/chrome/browser/ui/app_list/arc/arc_app_list_prefs.cc
index 8e8dfd0..7a5f456 100644
--- a/chrome/browser/ui/app_list/arc/arc_app_list_prefs.cc
+++ b/chrome/browser/ui/app_list/arc/arc_app_list_prefs.cc
@@ -274,8 +274,8 @@
 }
 
 void ArcAppListPrefs::StartPrefs() {
-  // Don't tie ArcAppListPrefs created with sync test profie in sync integration
-  // test to ArcSessionManager.
+  // Don't tie ArcAppListPrefs created with sync test profile in sync
+  // integration test to ArcSessionManager.
   if (!ArcAppListPrefsFactory::IsFactorySetForSyncTest()) {
     arc::ArcSessionManager* arc_session_manager = arc::ArcSessionManager::Get();
     CHECK(arc_session_manager);
@@ -628,7 +628,7 @@
 }
 
 void ArcAppListPrefs::OnArcPlayStoreEnabledChanged(bool enabled) {
-  UpdateDefaultAppsHiddenState();
+  SetDefaultAppsFilterLevel();
 
   // TODO(victorhsieh): Implement opt-in and opt-out.
   if (arc::ShouldArcAlwaysStart())
@@ -640,16 +640,22 @@
     RemoveAllApps();
 }
 
-void ArcAppListPrefs::UpdateDefaultAppsHiddenState() {
-  const bool was_hidden = default_apps_.is_hidden();
+void ArcAppListPrefs::SetDefaultAppsFilterLevel() {
   // There is no a blacklisting mechanism for Android apps. Until there is
   // one, we have no option but to ban all pre-installed apps on Android side.
   // Match this requirement and don't show pre-installed apps for managed users
   // in app list.
-  const bool now_hidden = arc::policy_util::IsAccountManaged(profile_);
-  default_apps_.set_hidden(now_hidden);
-  if (was_hidden && !default_apps_.is_hidden())
-    RegisterDefaultApps();
+  if (arc::policy_util::IsAccountManaged(profile_)) {
+    default_apps_.set_filter_level(
+        arc::IsArcPlayStoreEnabledForProfile(profile_)
+            ? ArcDefaultAppList::FilterLevel::OPTIONAL_APPS
+            : ArcDefaultAppList::FilterLevel::ALL);
+  } else {
+    default_apps_.set_filter_level(ArcDefaultAppList::FilterLevel::NOTHING);
+  }
+
+  // Register default apps if it was not registered before.
+  RegisterDefaultApps();
 }
 
 void ArcAppListPrefs::OnDefaultAppsReady() {
@@ -660,7 +666,7 @@
   for (const auto& uninstalled_package_name : uninstalled_package_names)
     default_apps_.MaybeMarkPackageUninstalled(uninstalled_package_name, true);
 
-  UpdateDefaultAppsHiddenState();
+  SetDefaultAppsFilterLevel();
 
   default_apps_ready_ = true;
   if (!default_apps_ready_callback_.is_null())
@@ -670,11 +676,15 @@
 }
 
 void ArcAppListPrefs::RegisterDefaultApps() {
-  // Report default apps first, note, app_map includes uninstalled apps as well.
+  // Report default apps first, note, app_map includes uninstalled and filtered
+  // out apps as well.
   for (const auto& default_app : default_apps_.app_map()) {
     const std::string& app_id = default_app.first;
     if (!default_apps_.HasApp(app_id))
       continue;
+    // Skip already tracked app.
+    if (tracked_apps_.count(app_id))
+      continue;
     const ArcDefaultAppList::AppInfo& app_info = *default_app.second.get();
     AddAppAndShortcut(false /* app_ready */,
                       app_info.name,
diff --git a/chrome/browser/ui/app_list/arc/arc_app_list_prefs.h b/chrome/browser/ui/app_list/arc/arc_app_list_prefs.h
index a1675921..cc30435 100644
--- a/chrome/browser/ui/app_list/arc/arc_app_list_prefs.h
+++ b/chrome/browser/ui/app_list/arc/arc_app_list_prefs.h
@@ -297,7 +297,7 @@
 
   void StartPrefs();
 
-  void UpdateDefaultAppsHiddenState();
+  void SetDefaultAppsFilterLevel();
   void RegisterDefaultApps();
 
   // Returns list of packages from prefs. If |installed| is set to true then
diff --git a/chrome/browser/ui/app_list/arc/arc_app_unittest.cc b/chrome/browser/ui/app_list/arc/arc_app_unittest.cc
index 053ae1e..2f66084 100644
--- a/chrome/browser/ui/app_list/arc/arc_app_unittest.cc
+++ b/chrome/browser/ui/app_list/arc/arc_app_unittest.cc
@@ -21,9 +21,9 @@
 #include "chrome/browser/chromeos/arc/arc_support_host.h"
 #include "chrome/browser/chromeos/arc/arc_util.h"
 #include "chrome/browser/extensions/extension_service.h"
+#include "chrome/browser/extensions/extension_service_test_base.h"
 #include "chrome/browser/policy/profile_policy_connector.h"
 #include "chrome/browser/policy/profile_policy_connector_factory.h"
-#include "chrome/browser/ui/app_list/app_list_test_util.h"
 #include "chrome/browser/ui/app_list/arc/arc_app_icon.h"
 #include "chrome/browser/ui/app_list/arc/arc_app_icon_loader.h"
 #include "chrome/browser/ui/app_list/arc/arc_app_item.h"
@@ -37,9 +37,11 @@
 #include "chrome/browser/ui/app_list/arc/arc_package_syncable_service_factory.h"
 #include "chrome/browser/ui/app_list/test/test_app_list_controller_delegate.h"
 #include "chrome/browser/ui/ash/launcher/arc_app_window_launcher_controller.h"
+#include "chrome/common/pref_names.h"
 #include "chrome/test/base/testing_profile.h"
 #include "components/arc/arc_util.h"
 #include "components/arc/test/fake_app_instance.h"
+#include "components/sync_preferences/testing_pref_service_syncable.h"
 #include "content/public/browser/browser_thread.h"
 #include "extensions/browser/extension_system.h"
 #include "extensions/common/extension.h"
@@ -93,10 +95,37 @@
   } while (!base::PathExists(icon_path));
 }
 
+enum class ArcState {
+  // By default, ARC is non-persistent and Play Store is unmanaged.
+  ARC_PLAY_STORE_UNMANAGED,
+  // ARC is persistent and Play Store is unmanaged
+  ARC_PERSISTENT_PLAY_STORE_UNMANAGED,
+  // ARC is non-persistent and Play Store is managed and enabled.
+  ARC_PLAY_STORE_MANAGED_AND_ENABLED,
+  // ARC is non-persistent and Play Store is managed and disabled.
+  ARC_PLAY_STORE_MANAGED_AND_DISABLED,
+  // ARC is persistent and Play Store is managed and enabled.
+  ARC_PERSISTENT_PLAY_STORE_MANAGED_AND_ENABLED,
+  // ARC is persistent and Play Store is managed and disabled.
+  ARC_PERSISTENT_PLAY_STORE_MANAGED_AND_DISABLED,
+};
+
+constexpr ArcState kManagedArcStates[] = {
+    ArcState::ARC_PLAY_STORE_MANAGED_AND_ENABLED,
+    ArcState::ARC_PLAY_STORE_MANAGED_AND_DISABLED,
+    ArcState::ARC_PERSISTENT_PLAY_STORE_MANAGED_AND_ENABLED,
+    ArcState::ARC_PERSISTENT_PLAY_STORE_MANAGED_AND_DISABLED,
+};
+
+constexpr ArcState kUnmanagedArcStates[] = {
+    ArcState::ARC_PLAY_STORE_UNMANAGED,
+    ArcState::ARC_PERSISTENT_PLAY_STORE_UNMANAGED,
+};
+
 }  // namespace
 
-class ArcAppModelBuilderTest : public AppListTestBase,
-                               public ::testing::WithParamInterface<bool> {
+class ArcAppModelBuilderTest : public extensions::ExtensionServiceTestBase,
+                               public ::testing::WithParamInterface<ArcState> {
  public:
   ArcAppModelBuilderTest() = default;
   ~ArcAppModelBuilderTest() override {
@@ -105,9 +134,27 @@
   }
 
   void SetUp() override {
-    if (GetParam())
-      arc::SetArcAlwaysStartForTesting();
-    AppListTestBase::SetUp();
+    switch (GetParam()) {
+      case ArcState::ARC_PERSISTENT_PLAY_STORE_UNMANAGED:
+      case ArcState::ARC_PERSISTENT_PLAY_STORE_MANAGED_AND_ENABLED:
+      case ArcState::ARC_PERSISTENT_PLAY_STORE_MANAGED_AND_DISABLED:
+        arc::SetArcAlwaysStartForTesting();
+        break;
+      default:
+        break;
+    }
+
+    extensions::ExtensionServiceTestBase::SetUp();
+    InitializeExtensionService(ExtensionServiceInitParams());
+    service_->Init();
+    // ExtensionService needs a real I/O thread.
+    service_->SetFileTaskRunnerForTesting(
+        content::BrowserThread::GetBlockingPool()
+            ->GetSequencedTaskRunnerWithShutdownBehavior(
+                content::BrowserThread::GetBlockingPool()
+                    ->GetNamedSequenceToken("ext_install-"),
+                base::SequencedWorkerPool::SKIP_ON_SHUTDOWN));
+
     OnBeforeArcTestSetup();
     arc_test_.SetUp(profile_.get());
     CreateBuilder();
@@ -336,7 +383,7 @@
 
   AppListControllerDelegate* controller() { return controller_.get(); }
 
-  Profile* profile() { return profile_.get(); }
+  TestingProfile* profile() { return profile_.get(); }
 
   ArcAppTest* arc_test() { return &arc_test_; }
 
@@ -386,26 +433,6 @@
   DISALLOW_COPY_AND_ASSIGN(ArcDefaulAppTest);
 };
 
-class ArcDefaulAppForManagedUserTest : public ArcDefaulAppTest {
- public:
-  ArcDefaulAppForManagedUserTest() = default;
-  ~ArcDefaulAppForManagedUserTest() override = default;
-
- protected:
-  // ArcAppModelBuilderTest:
-  void OnBeforeArcTestSetup() override {
-    ArcDefaulAppTest::OnBeforeArcTestSetup();
-
-    policy::ProfilePolicyConnector* const connector =
-        policy::ProfilePolicyConnectorFactory::GetForBrowserContext(
-            profile());
-    connector->OverrideIsManagedForTesting(true);
-  }
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(ArcDefaulAppForManagedUserTest);
-};
-
 class ArcPlayStoreAppTest : public ArcDefaulAppTest {
  public:
   ArcPlayStoreAppTest() = default;
@@ -441,6 +468,41 @@
   DISALLOW_COPY_AND_ASSIGN(ArcPlayStoreAppTest);
 };
 
+class ArcDefaulAppForManagedUserTest : public ArcPlayStoreAppTest {
+ public:
+  ArcDefaulAppForManagedUserTest() = default;
+  ~ArcDefaulAppForManagedUserTest() override = default;
+
+ protected:
+  bool IsEnabledByPolicy() const {
+    switch (GetParam()) {
+      case ArcState::ARC_PLAY_STORE_MANAGED_AND_ENABLED:
+      case ArcState::ARC_PERSISTENT_PLAY_STORE_MANAGED_AND_ENABLED:
+        return true;
+      case ArcState::ARC_PLAY_STORE_MANAGED_AND_DISABLED:
+      case ArcState::ARC_PERSISTENT_PLAY_STORE_MANAGED_AND_DISABLED:
+        return false;
+      default:
+        NOTREACHED();
+        return false;
+    }
+  }
+
+  // ArcPlayStoreAppTest:
+  void OnBeforeArcTestSetup() override {
+    policy::ProfilePolicyConnector* const connector =
+        policy::ProfilePolicyConnectorFactory::GetForBrowserContext(profile());
+    connector->OverrideIsManagedForTesting(true);
+    profile()->GetTestingPrefService()->SetManagedPref(
+        prefs::kArcEnabled, new base::Value(IsEnabledByPolicy()));
+
+    ArcPlayStoreAppTest::OnBeforeArcTestSetup();
+  }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(ArcDefaulAppForManagedUserTest);
+};
+
 class ArcAppModelBuilderRecreate : public ArcAppModelBuilderTest {
  public:
   ArcAppModelBuilderRecreate() = default;
@@ -1377,10 +1439,31 @@
     EXPECT_FALSE(prefs->IsRegistered(app_id));
     EXPECT_FALSE(prefs->GetApp(app_id));
   }
+
+  // PlayStor exists for managed and enabled state.
+  std::unique_ptr<ArcAppListPrefs::AppInfo> app_info =
+      prefs->GetApp(arc::kPlayStoreAppId);
+  if (IsEnabledByPolicy()) {
+    ASSERT_TRUE(app_info);
+    EXPECT_FALSE(app_info->ready);
+  } else {
+    EXPECT_FALSE(prefs->IsRegistered(arc::kPlayStoreAppId));
+    EXPECT_FALSE(app_info);
+  }
 }
 
-INSTANTIATE_TEST_CASE_P(, ArcAppModelBuilderTest, ::testing::Bool());
-INSTANTIATE_TEST_CASE_P(, ArcDefaulAppTest, ::testing::Bool());
-INSTANTIATE_TEST_CASE_P(, ArcDefaulAppForManagedUserTest, ::testing::Bool());
-INSTANTIATE_TEST_CASE_P(, ArcPlayStoreAppTest, ::testing::Bool());
-INSTANTIATE_TEST_CASE_P(, ArcAppModelBuilderRecreate, ::testing::Bool());
+INSTANTIATE_TEST_CASE_P(,
+                        ArcAppModelBuilderTest,
+                        ::testing::ValuesIn(kUnmanagedArcStates));
+INSTANTIATE_TEST_CASE_P(,
+                        ArcDefaulAppTest,
+                        ::testing::ValuesIn(kUnmanagedArcStates));
+INSTANTIATE_TEST_CASE_P(,
+                        ArcDefaulAppForManagedUserTest,
+                        ::testing::ValuesIn(kManagedArcStates));
+INSTANTIATE_TEST_CASE_P(,
+                        ArcPlayStoreAppTest,
+                        ::testing::ValuesIn(kUnmanagedArcStates));
+INSTANTIATE_TEST_CASE_P(,
+                        ArcAppModelBuilderRecreate,
+                        ::testing::ValuesIn(kUnmanagedArcStates));
diff --git a/chrome/browser/ui/app_list/arc/arc_default_app_list.cc b/chrome/browser/ui/app_list/arc/arc_default_app_list.cc
index c21fac9..78d3f9a 100644
--- a/chrome/browser/ui/app_list/arc/arc_default_app_list.cc
+++ b/chrome/browser/ui/app_list/arc/arc_default_app_list.cc
@@ -171,8 +171,11 @@
 
 const ArcDefaultAppList::AppInfo* ArcDefaultAppList::GetApp(
     const std::string& app_id) const {
-  if (hidden_)
+  if ((filter_level_ == FilterLevel::ALL) ||
+      (filter_level_ == FilterLevel::OPTIONAL_APPS &&
+       app_id != arc::kPlayStoreAppId)) {
     return nullptr;
+  }
   const auto it = apps_.find(app_id);
   if (it == apps_.end())
     return nullptr;
diff --git a/chrome/browser/ui/app_list/arc/arc_default_app_list.h b/chrome/browser/ui/app_list/arc/arc_default_app_list.h
index 6c48d2f8..39f992e 100644
--- a/chrome/browser/ui/app_list/arc/arc_default_app_list.h
+++ b/chrome/browser/ui/app_list/arc/arc_default_app_list.h
@@ -46,6 +46,17 @@
     base::FilePath app_path;  // App folder that contains pre-installed icons.
   };
 
+  enum class FilterLevel {
+    // Filter nothing.
+    NOTHING,
+    // Filter out only optional apps, excluding Play Store for example. Used in
+    // case when Play Store is managed and enabled.
+    OPTIONAL_APPS,
+    // Filter out everything. Used in case when Play Store is managed and
+    // disabled.
+    ALL
+  };
+
   // Defines App id to default AppInfo mapping.
   using AppInfoMap = std::map<std::string, std::unique_ptr<AppInfo>>;
 
@@ -73,10 +84,9 @@
 
   const AppInfoMap& app_map() const { return apps_; }
 
-  // Marks default apps as hidden for user, for example in case ARC is managed
-  // and disabled.
-  void set_hidden(bool hidden) { hidden_ = hidden; }
-  bool is_hidden() const { return hidden_; }
+  void set_filter_level(FilterLevel filter_level) {
+    filter_level_ = filter_level;
+  }
 
  private:
   // Defines mapping package name to uninstalled state.
@@ -88,7 +98,7 @@
   // Unowned pointer.
   Delegate* const delegate_;
   content::BrowserContext* const context_;
-  bool hidden_ = true;
+  FilterLevel filter_level_ = FilterLevel::ALL;
 
   AppInfoMap apps_;
   PackageMap packages_;
diff --git a/chrome/browser/ui/cocoa/bookmarks/OWNERS b/chrome/browser/ui/cocoa/bookmarks/OWNERS
index b9e8da9..1837015 100644
--- a/chrome/browser/ui/cocoa/bookmarks/OWNERS
+++ b/chrome/browser/ui/cocoa/bookmarks/OWNERS
@@ -1 +1,3 @@
 asvitkine@chromium.org
+
+# COMPONENT: UI>Browser>Bookmarks
diff --git a/chrome/browser/ui/cocoa/tab_contents/instant_overlay_controller_mac.h b/chrome/browser/ui/cocoa/tab_contents/instant_overlay_controller_mac.h
deleted file mode 100644
index 4ea3b7d..0000000
--- a/chrome/browser/ui/cocoa/tab_contents/instant_overlay_controller_mac.h
+++ /dev/null
@@ -1,30 +0,0 @@
-// Copyright 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_UI_COCOA_TAB_CONTENTS_INSTANT_OVERLAY_CONTROLLER_MAC_H_
-#define CHROME_BROWSER_UI_COCOA_TAB_CONTENTS_INSTANT_OVERLAY_CONTROLLER_MAC_H_
-
-#include "base/compiler_specific.h"
-#include "base/macros.h"
-#include "chrome/browser/ui/search/instant_overlay_controller.h"
-
-class Browser;
-@class OverlayableContentsController;
-
-class InstantOverlayControllerMac : public InstantOverlayController {
- public:
-  InstantOverlayControllerMac(Browser* browser,
-                              OverlayableContentsController* overlay);
-  ~InstantOverlayControllerMac() override;
-
- private:
-  // Overridden from InstantOverlayController:
-  void OverlayStateChanged(const InstantOverlayModel& model) override;
-
-  OverlayableContentsController* const overlay_;
-
-  DISALLOW_COPY_AND_ASSIGN(InstantOverlayControllerMac);
-};
-
-#endif  // CHROME_BROWSER_UI_COCOA_TAB_CONTENTS_INSTANT_OVERLAY_CONTROLLER_MAC_H_
diff --git a/chrome/browser/ui/cocoa/tab_contents/instant_overlay_controller_mac.mm b/chrome/browser/ui/cocoa/tab_contents/instant_overlay_controller_mac.mm
deleted file mode 100644
index 3510721e..0000000
--- a/chrome/browser/ui/cocoa/tab_contents/instant_overlay_controller_mac.mm
+++ /dev/null
@@ -1,38 +0,0 @@
-// Copyright 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/ui/cocoa/tab_contents/instant_overlay_controller_mac.h"
-
-#import "chrome/browser/ui/cocoa/tab_contents/overlayable_contents_controller.h"
-#include "chrome/browser/ui/search/instant_overlay_model.h"
-
-InstantOverlayControllerMac::InstantOverlayControllerMac(
-    Browser* browser,
-    OverlayableContentsController* overlay)
-    : InstantOverlayController(browser),
-      overlay_(overlay) {
-}
-
-InstantOverlayControllerMac::~InstantOverlayControllerMac() {
-}
-
-void InstantOverlayControllerMac::OverlayStateChanged(
-    const InstantOverlayModel& model) {
-  if (model.mode().is_ntp() || model.mode().is_search_suggestions()) {
-    // Drop shadow is only needed if search mode is not |NTP| and overlay does
-    // not fill up the entire contents page.
-    BOOL drawDropShadow = !model.mode().is_ntp() &&
-        !(model.height() == 100 &&
-          model.height_units() == INSTANT_SIZE_PERCENT);
-    [overlay_ setOverlay:model.GetOverlayContents()
-                  height:model.height()
-             heightUnits:model.height_units()
-          drawDropShadow:drawDropShadow];
-  } else if ([overlay_ isShowingOverlay]) {
-    [overlay_ setOverlay:NULL
-                  height:0
-             heightUnits:INSTANT_SIZE_PIXELS
-          drawDropShadow:NO];
-  }
-}
diff --git a/chrome/browser/ui/views/frame/opaque_browser_frame_view.cc b/chrome/browser/ui/views/frame/opaque_browser_frame_view.cc
index a9692e2..edff63e 100644
--- a/chrome/browser/ui/views/frame/opaque_browser_frame_view.cc
+++ b/chrome/browser/ui/views/frame/opaque_browser_frame_view.cc
@@ -510,7 +510,8 @@
 
 int OpaqueBrowserFrameView::GetTopAreaHeight() const {
   const gfx::ImageSkia frame_image = GetFrameImage();
-  int top_area_height = frame_image.height();  // Returns 0 if isNull().
+  int top_area_height =
+      std::max(frame_image.height(), layout_->NonClientTopHeight(false));
   if (browser_view()->IsTabStripVisible()) {
     top_area_height =
         std::max(top_area_height,
diff --git a/chrome/browser/ui/webui/engagement/site_engagement_ui.cc b/chrome/browser/ui/webui/engagement/site_engagement_ui.cc
index 81d8ed9..c0ff308c 100644
--- a/chrome/browser/ui/webui/engagement/site_engagement_ui.cc
+++ b/chrome/browser/ui/webui/engagement/site_engagement_ui.cc
@@ -62,7 +62,7 @@
     }
 
     SiteEngagementService* service = SiteEngagementService::Get(profile_);
-    service->ResetScoreForURL(origin, score);
+    service->ResetBaseScoreForURL(origin, score);
   }
 
  private:
diff --git a/chrome/browser/ui/webui/print_preview/print_preview_ui.h b/chrome/browser/ui/webui/print_preview/print_preview_ui.h
index 89ff9b7..5a4f8c24 100644
--- a/chrome/browser/ui/webui/print_preview/print_preview_ui.h
+++ b/chrome/browser/ui/webui/print_preview/print_preview_ui.h
@@ -147,8 +147,7 @@
   void OnSetOptionsFromDocument(
       const PrintHostMsg_SetOptionsFromDocument_Params& params);
 
-  // Allows tests to wait until the print preview dialog is loaded. Optionally
-  // also instructs the dialog to auto-cancel, which is used for testing only.
+  // Allows tests to wait until the print preview dialog is loaded.
   class TestingDelegate {
    public:
     virtual void DidGetPreviewPageCount(int page_count) = 0;
diff --git a/chrome/common/BUILD.gn b/chrome/common/BUILD.gn
index 84f0b40..c1f5fdcd 100644
--- a/chrome/common/BUILD.gn
+++ b/chrome/common/BUILD.gn
@@ -196,6 +196,7 @@
     "//components/policy:generated",
     "//components/policy/core/common",
     "//components/prefs",
+    "//components/safe_browsing:csd_proto",
     "//components/signin/core/common",
     "//components/strings",
     "//components/translate/content/common",
@@ -687,6 +688,7 @@
     "conflicts/module_event_sink_win.mojom",
     "field_trial_recorder.mojom",
     "file_patcher.mojom",
+    "image_context_menu_renderer.mojom",
     "insecure_content_renderer.mojom",
     "net_benchmarking.mojom",
     "network_diagnostics.mojom",
diff --git a/chrome/common/DEPS b/chrome/common/DEPS
index e6b1509..0d712737 100644
--- a/chrome/common/DEPS
+++ b/chrome/common/DEPS
@@ -23,6 +23,7 @@
   "+components/password_manager/core/common",
   "+components/policy/core/common",
   "+components/printing/common",
+  "+components/safe_browsing/csd.pb.h",
   "+components/signin/core/common",
   "+components/translate/core/common",
   "+components/url_formatter",
diff --git a/chrome/common/image_context_menu_renderer.mojom b/chrome/common/image_context_menu_renderer.mojom
new file mode 100644
index 0000000..bb25c0c
--- /dev/null
+++ b/chrome/common/image_context_menu_renderer.mojom
@@ -0,0 +1,12 @@
+// 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.
+
+module chrome.mojom;
+
+// Performs actions requested by right-clicking on an image.
+interface ImageContextMenuRenderer {
+  // Reloads the image selected by the most recently opened context menu
+  // (if there indeed is an image at that location).
+  RequestReloadImageForContextNode();
+};
diff --git a/chrome/common/render_messages.h b/chrome/common/render_messages.h
index 5eab22a..ccbbed63 100644
--- a/chrome/common/render_messages.h
+++ b/chrome/common/render_messages.h
@@ -135,6 +135,8 @@
 
 // Reloads the image selected by the most recently opened context menu
 // (if there indeed is an image at that location).
+//
+// TODO(nigeltao): delete this when tab_android.cc's use is converted to Mojo.
 IPC_MESSAGE_ROUTED0(ChromeViewMsg_RequestReloadImageForContextNode)
 
 // Asks the renderer for a thumbnail of the image selected by the most
diff --git a/chrome/common/safe_browsing/BUILD.gn b/chrome/common/safe_browsing/BUILD.gn
index 4f56f9f2..55ccf63 100644
--- a/chrome/common/safe_browsing/BUILD.gn
+++ b/chrome/common/safe_browsing/BUILD.gn
@@ -8,7 +8,6 @@
   sources = [
     "client_model.proto",
     "crx_info.proto",
-    "csd.proto",
     "download_file_types.proto",
     "permission_report.proto",
   ]
diff --git a/chrome/common/safe_browsing/binary_feature_extractor.cc b/chrome/common/safe_browsing/binary_feature_extractor.cc
index ed883db..538a81e 100644
--- a/chrome/common/safe_browsing/binary_feature_extractor.cc
+++ b/chrome/common/safe_browsing/binary_feature_extractor.cc
@@ -10,7 +10,7 @@
 #include "base/files/file.h"
 #include "base/files/file_path.h"
 #include "base/files/memory_mapped_file.h"
-#include "chrome/common/safe_browsing/csd.pb.h"
+#include "components/safe_browsing/csd.pb.h"
 #include "crypto/secure_hash.h"
 #include "crypto/sha2.h"
 
diff --git a/chrome/common/safe_browsing/binary_feature_extractor_mac.cc b/chrome/common/safe_browsing/binary_feature_extractor_mac.cc
index f0ab808c..7d64fae 100644
--- a/chrome/common/safe_browsing/binary_feature_extractor_mac.cc
+++ b/chrome/common/safe_browsing/binary_feature_extractor_mac.cc
@@ -7,8 +7,8 @@
 #include <stddef.h>
 #include <stdint.h>
 
-#include "chrome/common/safe_browsing/csd.pb.h"
 #include "chrome/common/safe_browsing/mach_o_image_reader_mac.h"
+#include "components/safe_browsing/csd.pb.h"
 
 namespace safe_browsing {
 
diff --git a/chrome/common/safe_browsing/binary_feature_extractor_mac_unittest.cc b/chrome/common/safe_browsing/binary_feature_extractor_mac_unittest.cc
index 88fe7b92..ec29387 100644
--- a/chrome/common/safe_browsing/binary_feature_extractor_mac_unittest.cc
+++ b/chrome/common/safe_browsing/binary_feature_extractor_mac_unittest.cc
@@ -7,7 +7,7 @@
 #include "base/files/file_path.h"
 #include "base/path_service.h"
 #include "chrome/common/chrome_paths.h"
-#include "chrome/common/safe_browsing/csd.pb.h"
+#include "components/safe_browsing/csd.pb.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace safe_browsing {
diff --git a/chrome/common/safe_browsing/binary_feature_extractor_unittest.cc b/chrome/common/safe_browsing/binary_feature_extractor_unittest.cc
index 9da2d48..48b1847 100644
--- a/chrome/common/safe_browsing/binary_feature_extractor_unittest.cc
+++ b/chrome/common/safe_browsing/binary_feature_extractor_unittest.cc
@@ -13,7 +13,7 @@
 #include "base/files/file.h"
 #include "base/files/scoped_temp_dir.h"
 #include "base/path_service.h"
-#include "chrome/common/safe_browsing/csd.pb.h"
+#include "components/safe_browsing/csd.pb.h"
 #include "crypto/sha2.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
diff --git a/chrome/common/safe_browsing/binary_feature_extractor_win.cc b/chrome/common/safe_browsing/binary_feature_extractor_win.cc
index a504631..ffdb9186 100644
--- a/chrome/common/safe_browsing/binary_feature_extractor_win.cc
+++ b/chrome/common/safe_browsing/binary_feature_extractor_win.cc
@@ -12,8 +12,8 @@
 
 #include "base/files/file_path.h"
 #include "base/logging.h"
-#include "chrome/common/safe_browsing/csd.pb.h"
 #include "chrome/common/safe_browsing/pe_image_reader_win.h"
+#include "components/safe_browsing/csd.pb.h"
 
 namespace safe_browsing {
 
diff --git a/chrome/common/safe_browsing/binary_feature_extractor_win_unittest.cc b/chrome/common/safe_browsing/binary_feature_extractor_win_unittest.cc
index 4b2f9df..c4a6d914 100644
--- a/chrome/common/safe_browsing/binary_feature_extractor_win_unittest.cc
+++ b/chrome/common/safe_browsing/binary_feature_extractor_win_unittest.cc
@@ -12,7 +12,7 @@
 #include "base/memory/ref_counted.h"
 #include "base/path_service.h"
 #include "chrome/common/chrome_paths.h"
-#include "chrome/common/safe_browsing/csd.pb.h"
+#include "components/safe_browsing/csd.pb.h"
 #include "net/cert/x509_cert_types.h"
 #include "net/cert/x509_certificate.h"
 #include "testing/gtest/include/gtest/gtest.h"
diff --git a/chrome/common/safe_browsing/download_protection_util.h b/chrome/common/safe_browsing/download_protection_util.h
index ff01cc5..dedc286 100644
--- a/chrome/common/safe_browsing/download_protection_util.h
+++ b/chrome/common/safe_browsing/download_protection_util.h
@@ -6,7 +6,7 @@
 #define CHROME_COMMON_SAFE_BROWSING_DOWNLOAD_PROTECTION_UTIL_H_
 
 #include "base/files/file_path.h"
-#include "chrome/common/safe_browsing/csd.pb.h"
+#include "components/safe_browsing/csd.pb.h"
 
 namespace safe_browsing {
 namespace download_protection_util {
diff --git a/chrome/common/safe_browsing/zip_analyzer.cc b/chrome/common/safe_browsing/zip_analyzer.cc
index b352ca3..ed3bde6 100644
--- a/chrome/common/safe_browsing/zip_analyzer.cc
+++ b/chrome/common/safe_browsing/zip_analyzer.cc
@@ -14,10 +14,10 @@
 #include "base/logging.h"
 #include "base/macros.h"
 #include "chrome/common/safe_browsing/binary_feature_extractor.h"
-#include "chrome/common/safe_browsing/csd.pb.h"
 #include "chrome/common/safe_browsing/download_protection_util.h"
 #include "chrome/common/safe_browsing/file_type_policies.h"
 #include "chrome/common/safe_browsing/zip_analyzer_results.h"
+#include "components/safe_browsing/csd.pb.h"
 #include "crypto/secure_hash.h"
 #include "crypto/sha2.h"
 #include "third_party/zlib/google/zip_reader.h"
diff --git a/chrome/common/safe_browsing/zip_analyzer.h b/chrome/common/safe_browsing/zip_analyzer.h
index 7c8c3730..b3109b9 100644
--- a/chrome/common/safe_browsing/zip_analyzer.h
+++ b/chrome/common/safe_browsing/zip_analyzer.h
@@ -9,7 +9,7 @@
 #define CHROME_COMMON_SAFE_BROWSING_ZIP_ANALYZER_H_
 
 #include "base/files/file.h"
-#include "chrome/common/safe_browsing/csd.pb.h"
+#include "components/safe_browsing/csd.pb.h"
 
 namespace safe_browsing {
 namespace zip_analyzer {
diff --git a/chrome/common/safe_browsing/zip_analyzer_results.h b/chrome/common/safe_browsing/zip_analyzer_results.h
index 7a506cf..efddbef 100644
--- a/chrome/common/safe_browsing/zip_analyzer_results.h
+++ b/chrome/common/safe_browsing/zip_analyzer_results.h
@@ -11,7 +11,7 @@
 #include <vector>
 
 #include "base/files/file_path.h"
-#include "chrome/common/safe_browsing/csd.pb.h"
+#include "components/safe_browsing/csd.pb.h"
 
 namespace safe_browsing {
 namespace zip_analyzer {
diff --git a/chrome/renderer/BUILD.gn b/chrome/renderer/BUILD.gn
index b0a28bfb..3ef7e5a 100644
--- a/chrome/renderer/BUILD.gn
+++ b/chrome/renderer/BUILD.gn
@@ -216,6 +216,7 @@
       ]
       deps += [
         "//chrome/common/safe_browsing:proto",
+        "//components/safe_browsing:csd_proto",
         "//third_party/smhasher:murmurhash3",
       ]
     }
diff --git a/chrome/renderer/chrome_render_frame_observer.cc b/chrome/renderer/chrome_render_frame_observer.cc
index 729e75b..84e6db4 100644
--- a/chrome/renderer/chrome_render_frame_observer.cc
+++ b/chrome/renderer/chrome_render_frame_observer.cc
@@ -28,6 +28,7 @@
 #include "content/public/renderer/render_view.h"
 #include "extensions/common/constants.h"
 #include "printing/features/features.h"
+#include "services/service_manager/public/cpp/interface_registry.h"
 #include "skia/ext/image_operations.h"
 #include "third_party/WebKit/public/platform/WebImage.h"
 #include "third_party/WebKit/public/platform/WebURLRequest.h"
@@ -112,7 +113,11 @@
     : content::RenderFrameObserver(render_frame),
       translate_helper_(nullptr),
       phishing_classifier_(nullptr) {
-  // Don't do anything for subframes.
+  render_frame->GetInterfaceRegistry()->AddInterface(
+      base::Bind(&ChromeRenderFrameObserver::OnImageContextMenuRendererRequest,
+                 base::Unretained(this)));
+
+  // Don't do anything else for subframes.
   if (!render_frame->IsMainFrame())
     return;
 
@@ -139,8 +144,11 @@
     return false;
 
   IPC_BEGIN_MESSAGE_MAP(ChromeRenderFrameObserver, message)
+    // TODO(nigeltao): delete the
+    // ChromeViewMsg_RequestReloadImageForContextNode handler when
+    // tab_android.cc's use is converted to Mojo.
     IPC_MESSAGE_HANDLER(ChromeViewMsg_RequestReloadImageForContextNode,
-                        OnRequestReloadImageForContextNode)
+                        RequestReloadImageForContextNode)
     IPC_MESSAGE_HANDLER(ChromeViewMsg_RequestThumbnailForContextNode,
                         OnRequestThumbnailForContextNode)
     IPC_MESSAGE_HANDLER(ChromeViewMsg_SetClientSidePhishingDetection,
@@ -171,7 +179,7 @@
   }
 }
 
-void ChromeRenderFrameObserver::OnRequestReloadImageForContextNode() {
+void ChromeRenderFrameObserver::RequestReloadImageForContextNode() {
   WebLocalFrame* frame = render_frame()->GetWebFrame();
   // TODO(dglazkov): This code is clearly in the wrong place. Need
   // to investigate what it is doing and fix (http://crbug.com/606164).
@@ -347,3 +355,8 @@
 void ChromeRenderFrameObserver::OnDestruct() {
   delete this;
 }
+
+void ChromeRenderFrameObserver::OnImageContextMenuRendererRequest(
+    chrome::mojom::ImageContextMenuRendererRequest request) {
+  image_context_menu_renderer_bindings_.AddBinding(this, std::move(request));
+}
diff --git a/chrome/renderer/chrome_render_frame_observer.h b/chrome/renderer/chrome_render_frame_observer.h
index a9dad7bb..a02f5d4 100644
--- a/chrome/renderer/chrome_render_frame_observer.h
+++ b/chrome/renderer/chrome_render_frame_observer.h
@@ -7,8 +7,10 @@
 
 #include "base/macros.h"
 #include "base/timer/timer.h"
+#include "chrome/common/image_context_menu_renderer.mojom.h"
 #include "chrome/common/prerender_types.h"
 #include "content/public/renderer/render_frame_observer.h"
+#include "mojo/public/cpp/bindings/binding_set.h"
 
 namespace gfx {
 class Size;
@@ -24,7 +26,9 @@
 
 // This class holds the Chrome specific parts of RenderFrame, and has the same
 // lifetime.
-class ChromeRenderFrameObserver : public content::RenderFrameObserver {
+class ChromeRenderFrameObserver
+    : public content::RenderFrameObserver,
+      public chrome::mojom::ImageContextMenuRenderer {
  public:
   explicit ChromeRenderFrameObserver(content::RenderFrame* render_frame);
   ~ChromeRenderFrameObserver() override;
@@ -41,9 +45,15 @@
   void DidMeaningfulLayout(blink::WebMeaningfulLayout layout_type) override;
   void OnDestruct() override;
 
+  // chrome::mojom::ImageContextMenuRenderer:
+  void RequestReloadImageForContextNode() override;
+
+  // Mojo handlers.
+  void OnImageContextMenuRendererRequest(
+      chrome::mojom::ImageContextMenuRendererRequest request);
+
   // IPC handlers
   void OnSetIsPrerendering(prerender::PrerenderMode mode);
-  void OnRequestReloadImageForContextNode();
   void OnRequestThumbnailForContextNode(
       int thumbnail_min_area_pixels,
       const gfx::Size& thumbnail_max_size_pixels,
@@ -65,6 +75,9 @@
   translate::TranslateHelper* translate_helper_;
   safe_browsing::PhishingClassifierDelegate* phishing_classifier_;
 
+  mojo::BindingSet<chrome::mojom::ImageContextMenuRenderer>
+      image_context_menu_renderer_bindings_;
+
   DISALLOW_COPY_AND_ASSIGN(ChromeRenderFrameObserver);
 };
 
diff --git a/chrome/renderer/safe_browsing/DEPS b/chrome/renderer/safe_browsing/DEPS
index 1a23f04..3e3e99f0 100644
--- a/chrome/renderer/safe_browsing/DEPS
+++ b/chrome/renderer/safe_browsing/DEPS
@@ -1,5 +1,6 @@
 include_rules = [
   "+components/safe_browsing/common",
+  "+components/safe_browsing/csd.pb.h",
   "+third_party/smhasher",
 ]
 
diff --git a/chrome/renderer/safe_browsing/phishing_classifier.cc b/chrome/renderer/safe_browsing/phishing_classifier.cc
index a2246d5..b6a1882 100644
--- a/chrome/renderer/safe_browsing/phishing_classifier.cc
+++ b/chrome/renderer/safe_browsing/phishing_classifier.cc
@@ -15,7 +15,6 @@
 #include "base/single_thread_task_runner.h"
 #include "base/strings/string_util.h"
 #include "base/threading/thread_task_runner_handle.h"
-#include "chrome/common/safe_browsing/csd.pb.h"
 #include "chrome/common/url_constants.h"
 #include "chrome/renderer/safe_browsing/feature_extractor_clock.h"
 #include "chrome/renderer/safe_browsing/features.h"
@@ -23,6 +22,7 @@
 #include "chrome/renderer/safe_browsing/phishing_term_feature_extractor.h"
 #include "chrome/renderer/safe_browsing/phishing_url_feature_extractor.h"
 #include "chrome/renderer/safe_browsing/scorer.h"
+#include "components/safe_browsing/csd.pb.h"
 #include "content/public/renderer/render_frame.h"
 #include "crypto/sha2.h"
 #include "third_party/WebKit/public/platform/WebURL.h"
diff --git a/chrome/renderer/safe_browsing/phishing_classifier_browsertest.cc b/chrome/renderer/safe_browsing/phishing_classifier_browsertest.cc
index c32f907..4ee3b99 100644
--- a/chrome/renderer/safe_browsing/phishing_classifier_browsertest.cc
+++ b/chrome/renderer/safe_browsing/phishing_classifier_browsertest.cc
@@ -17,13 +17,13 @@
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
 #include "chrome/common/chrome_switches.h"
 #include "chrome/common/safe_browsing/client_model.pb.h"
-#include "chrome/common/safe_browsing/csd.pb.h"
 #include "chrome/renderer/safe_browsing/features.h"
 #include "chrome/renderer/safe_browsing/mock_feature_extractor_clock.h"
 #include "chrome/renderer/safe_browsing/murmurhash3_util.h"
 #include "chrome/renderer/safe_browsing/scorer.h"
 #include "chrome/test/base/in_process_browser_test.h"
 #include "chrome/test/base/ui_test_utils.h"
+#include "components/safe_browsing/csd.pb.h"
 #include "content/public/browser/render_frame_host.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/common/content_switches.h"
diff --git a/chrome/renderer/safe_browsing/phishing_classifier_delegate.cc b/chrome/renderer/safe_browsing/phishing_classifier_delegate.cc
index 258b878..92043a0 100644
--- a/chrome/renderer/safe_browsing/phishing_classifier_delegate.cc
+++ b/chrome/renderer/safe_browsing/phishing_classifier_delegate.cc
@@ -12,11 +12,11 @@
 #include "base/lazy_instance.h"
 #include "base/logging.h"
 #include "base/metrics/histogram_macros.h"
-#include "chrome/common/safe_browsing/csd.pb.h"
 #include "chrome/renderer/safe_browsing/feature_extractor_clock.h"
 #include "chrome/renderer/safe_browsing/phishing_classifier.h"
 #include "chrome/renderer/safe_browsing/scorer.h"
 #include "components/safe_browsing/common/safebrowsing_messages.h"
+#include "components/safe_browsing/csd.pb.h"
 #include "content/public/renderer/document_state.h"
 #include "content/public/renderer/navigation_state.h"
 #include "content/public/renderer/render_frame.h"
diff --git a/chrome/renderer/safe_browsing/phishing_classifier_delegate_browsertest.cc b/chrome/renderer/safe_browsing/phishing_classifier_delegate_browsertest.cc
index 5e5b799..2412c84 100644
--- a/chrome/renderer/safe_browsing/phishing_classifier_delegate_browsertest.cc
+++ b/chrome/renderer/safe_browsing/phishing_classifier_delegate_browsertest.cc
@@ -7,13 +7,13 @@
 #include <memory>
 
 #include "base/strings/utf_string_conversions.h"
-#include "chrome/common/safe_browsing/csd.pb.h"
 #include "chrome/renderer/safe_browsing/features.h"
 #include "chrome/renderer/safe_browsing/phishing_classifier.h"
 #include "chrome/renderer/safe_browsing/scorer.h"
 #include "chrome/test/base/chrome_render_view_test.h"
 #include "chrome/test/base/chrome_unit_test_suite.h"
 #include "components/safe_browsing/common/safebrowsing_messages.h"
+#include "components/safe_browsing/csd.pb.h"
 #include "content/public/renderer/render_frame.h"
 #include "content/public/renderer/render_view.h"
 #include "testing/gmock/include/gmock/gmock.h"
diff --git a/chrome/service/service_utility_process_host.cc b/chrome/service/service_utility_process_host.cc
index f779bb2..b9b97c5 100644
--- a/chrome/service/service_utility_process_host.cc
+++ b/chrome/service/service_utility_process_host.cc
@@ -275,7 +275,9 @@
   }
 
   if (success)
-    process_connection_.Connect(process_.Handle(), std::move(parent_handle));
+    process_connection_.Connect(
+        process_.Handle(),
+        mojo::edk::ConnectionParams(std::move(parent_handle)));
 
   return success;
 }
diff --git a/chrome/test/base/mojo_test_connector.cc b/chrome/test/base/mojo_test_connector.cc
index 11260ef..fcbb998 100644
--- a/chrome/test/base/mojo_test_connector.cc
+++ b/chrome/test/base/mojo_test_connector.cc
@@ -87,7 +87,9 @@
   void ChildProcessLaunched(base::ProcessHandle handle,
                             base::ProcessId pid) override {
     platform_channel_->ChildProcessLaunched();
-    process_connection_.Connect(handle, platform_channel_->PassServerHandle());
+    process_connection_.Connect(
+        handle,
+        mojo::edk::ConnectionParams(platform_channel_->PassServerHandle()));
 
     main_task_runner_->PostTask(
         FROM_HERE,
diff --git a/chrome/test/chromedriver/chrome/version.cc b/chrome/test/chromedriver/chrome/version.cc
index 92f52ad..46bcbb6 100644
--- a/chrome/test/chromedriver/chrome/version.cc
+++ b/chrome/test/chromedriver/chrome/version.cc
@@ -9,7 +9,7 @@
 namespace {
 
 // This variable must be able to be found and parsed by the upload script.
-const int kMinimumSupportedChromeVersion[] = {55, 0, 2883, 0};
+const int kMinimumSupportedChromeVersion[] = {56, 0, 2884, 0};
 
 }  // namespace
 
diff --git a/chrome/test/chromedriver/test/run_all_tests.py b/chrome/test/chromedriver/test/run_all_tests.py
index b5b421437..74ae895c 100755
--- a/chrome/test/chromedriver/test/run_all_tests.py
+++ b/chrome/test/chromedriver/test/run_all_tests.py
@@ -193,15 +193,15 @@
       # Linux32 builds need to be special-cased, because 1) they are keyed by
       # git hash rather than commit position, and 2) come from a different
       # download site (so we can't just convert the commit position to a hash).
+      versions['58'] = '7613176285d46fbc5b4712e42bd135aae99cbba5'
       versions['57'] = '7da9cd89d4d18e171323ff7d0d2a93ede0c1d721'
       versions['56'] = '67002b0fdaa3123f10f96fa2f7965677d531db74'
-      versions['55'] = 'e9bc4e0245c9a1e570ed2cf8e12152b9122275f2'
       # TODO(samuong): speculative fix for crbug.com/611886
       os.environ['CHROME_DEVEL_SANDBOX'] = '/opt/chromium/chrome_sandbox'
     else:
+      versions['58'] = '454475'
       versions['57'] = '444890'
       versions['56'] = '433020'
-      versions['55'] = '423791'
     code = 0
     for version, revision in versions.iteritems():
       if options.chrome_version and version != options.chrome_version:
diff --git a/chrome/test/chromedriver/test/run_py_tests.py b/chrome/test/chromedriver/test/run_py_tests.py
index 49c0b2a..712e9072e 100755
--- a/chrome/test/chromedriver/test/run_py_tests.py
+++ b/chrome/test/chromedriver/test/run_py_tests.py
@@ -71,14 +71,19 @@
 
 _VERSION_SPECIFIC_FILTER = {}
 _VERSION_SPECIFIC_FILTER['HEAD'] = [
-    # https://code.google.com/p/chromedriver/issues/detail?id=992
+    # https://bugs.chromium.org/p/chromedriver/issues/detail?id=992
     'ChromeDownloadDirTest.testDownloadDirectoryOverridesExistingPreferences',
     # https://bugs.chromium.org/p/chromedriver/issues/detail?id=1673
     'ChromeDownloadDirTest.testFileDownloadWithGet',
     'ChromeDriverPageLoadTimeoutTest.*',
 ]
+_VERSION_SPECIFIC_FILTER['58'] = [
+    # https://bugs.chromium.org/p/chromedriver/issues/detail?id=1673
+    'ChromeDownloadDirTest.testFileDownloadWithGet',
+    'ChromeDriverPageLoadTimeoutTest.*',
+]
 _VERSION_SPECIFIC_FILTER['57'] = [
-    # https://code.google.com/p/chromedriver/issues/detail?id=992
+    # https://bugs.chromium.org/p/chromedriver/issues/detail?id=992
     'ChromeDownloadDirTest.testDownloadDirectoryOverridesExistingPreferences',
     # https://bugs.chromium.org/p/chromedriver/issues/detail?id=1625
     'ChromeDriverTest.testWindowMaximize',
@@ -91,7 +96,7 @@
 
 _OS_SPECIFIC_FILTER = {}
 _OS_SPECIFIC_FILTER['win'] = [
-    # https://code.google.com/p/chromedriver/issues/detail?id=299
+    # https://bugs.chromium.org/p/chromedriver/issues/detail?id=299
     'ChromeLogPathCapabilityTest.testChromeLogPath',
 ]
 _OS_SPECIFIC_FILTER['linux'] = [
@@ -134,7 +139,7 @@
         'ChromeDownloadDirTest.*',
         # https://crbug.com/274650
         'ChromeDriverTest.testCloseWindow',
-        # https://code.google.com/p/chromedriver/issues/detail?id=298
+        # https://bugs.chromium.org/p/chromedriver/issues/detail?id=298
         'ChromeDriverTest.testWindowPosition',
         'ChromeDriverTest.testWindowSize',
         'ChromeDriverTest.testWindowMaximize',
@@ -148,7 +153,7 @@
         'SessionHandlingTest.testGetSessions',
         # Android doesn't use the chrome://print dialog.
         'ChromeDriverTest.testCanSwitchToPrintPreviewDialog',
-        # https://code.google.com/p/chromedriver/issues/detail?id=1175
+        # https://bugs.chromium.org/p/chromedriver/issues/detail?id=1175
         'ChromeDriverTest.testChromeDriverSendLargeData',
         # Chrome 44+ for Android doesn't dispatch the dblclick event
         'ChromeDriverTest.testMouseDoubleClick',
@@ -821,7 +826,7 @@
     self.assertEquals(position, self._driver.GetWindowPosition())
 
     # Resize so the window isn't moved offscreen.
-    # See https://code.google.com/p/chromedriver/issues/detail?id=297.
+    # See https://bugs.chromium.org/p/chromedriver/issues/detail?id=297.
     self._driver.SetWindowSize(300, 300)
 
     self._driver.SetWindowPosition(100, 200)
@@ -843,7 +848,7 @@
     self.assertNotEqual([100, 200], self._driver.GetWindowPosition())
     self.assertNotEqual([600, 400], self._driver.GetWindowSize())
     # Set size first so that the window isn't moved offscreen.
-    # See https://code.google.com/p/chromedriver/issues/detail?id=297.
+    # See https://bugs.chromium.org/p/chromedriver/issues/detail?id=297.
     self._driver.SetWindowSize(600, 400)
     self._driver.SetWindowPosition(100, 200)
     self.assertEquals([100, 200], self._driver.GetWindowPosition())
@@ -928,7 +933,7 @@
   def testTabCrash(self):
     # If a tab is crashed, the session will be deleted.
     # When 31 is released, will reload the tab instead.
-    # https://code.google.com/p/chromedriver/issues/detail?id=547
+    # https://bugs.chromium.org/p/chromedriver/issues/detail?id=547
     self.assertRaises(chromedriver.UnknownError,
                       self._driver.Load, 'chrome://crash')
     self.assertRaises(chromedriver.NoSuchSession,
@@ -1170,7 +1175,7 @@
     self.assertEquals(0, self._driver.ExecuteScript(scroll_top))
     target = self._driver.FindElement('id', 'target')
     self._driver.TouchScroll(target, 47, 53)
-    # https://code.google.com/p/chromedriver/issues/detail?id=1179
+    # https://bugs.chromium.org/p/chromedriver/issues/detail?id=1179
     self.assertAlmostEqual(47, self._driver.ExecuteScript(scroll_left), delta=1)
     self.assertAlmostEqual(53, self._driver.ExecuteScript(scroll_top), delta=1)
 
diff --git a/chrome/test/chromedriver/test/test_expectations b/chrome/test/chromedriver/test/test_expectations
index 6b43d93..2ac40793 100644
--- a/chrome/test/chromedriver/test/test_expectations
+++ b/chrome/test/chromedriver/test/test_expectations
@@ -44,7 +44,7 @@
     'ExecutingJavascriptTest.testShouldThrowAnExceptionWithMessageAndStacktraceWhenTheJavascriptIsBad',
     'FormHandlingTest.testShouldNotBeAbleToSubmitAFormThatDoesNotExist',
     'FrameSwitchingTest.testShouldNotBeAbleToDoAnythingTheFrameIsDeletedFromUnderUs',
-    # https://code.google.com/p/chromedriver/issues/detail?id=1249
+    # https://bugs.chromium.org/p/chromedriver/issues/detail?id=1249
     'FrameSwitchingTest.testGetCurrentUrl',
     'I18nTest.testShouldBeAbleToActivateIMEEngine',
     # Broken because AddWebStorage.java is broken.
@@ -84,16 +84,16 @@
     'WindowTest.testCanMaximizeTheWindowFromIframe',
     'WindowTest.testSetsTheSizeOfTheCurrentWindow',
     'WindowTest.testCanMaximizeTheWindow',
-    # Flaky: https://code.google.com/p/chromedriver/issues/detail?id=528
+    # Flaky: https://bugs.chromium.org/p/chromedriver/issues/detail?id=528
     'PageLoadingTest.testShouldDoNothingIfThereIsNothingToGoBackTo',
-    # Flaky: https://code.google.com/p/chromedriver/issues/detail?id=653
+    # Flaky: https://bugs.chromium.org/p/chromedriver/issues/detail?id=653
     'PageLoadingTest.testShouldBeAbleToNavigateBackInTheBrowserHistoryInPresenceOfIframes',
     'PageLoadingTest.testShouldBeAbleToNavigateBackInTheBrowserHistory',
-    # https://code.google.com/p/chromedriver/issues/detail?id=1117
+    # https://bugs.chromium.org/p/chromedriver/issues/detail?id=1117
     'PageLoadingTest.testShouldBeAbleToAccessPagesWithAnInsecureSslCertificate',
-    # Flaky: https://code.google.com/p/chromedriver/issues/detail?id=1113
+    # Flaky: https://bugs.chromium.org/p/chromedriver/issues/detail?id=1113
     'FrameSwitchingTest.testShouldNotSwitchMagicallyToTheTopWindow',
-    # Flaky: https://code.google.com/p/chromedriver/issues/detail?id=1151
+    # Flaky: https://bugs.chromium.org/p/chromedriver/issues/detail?id=1151
     'DragAndDropTest.testDragAndDrop',
     'DragAndDropTest.testShouldAllowUsersToDragAndDropToElementsOffTheCurrentViewPort',
     'DragAndDropTest.testElementInDiv',
@@ -103,6 +103,11 @@
     # https://bugs.chromium.org/p/chromedriver/issues/detail?id=1674
     'WindowSwitchingTest.testShouldBeAbleToIterateOverAllOpenWindows',
 ]
+_REVISION_NEGATIVE_FILTER['58'] = (
+    _REVISION_NEGATIVE_FILTER['HEAD'] + [
+        # https://bugs.chromium.org/p/chromedriver/issues/detail?id=1674
+        'WindowSwitchingTest.testShouldBeAbleToIterateOverAllOpenWindows',
+]
 _REVISION_NEGATIVE_FILTER['57'] = (
     _REVISION_NEGATIVE_FILTER['HEAD'] + [
         # https://bugs.chromium.org/p/chromedriver/issues/detail?id=1625
@@ -113,33 +118,33 @@
 
 _OS_NEGATIVE_FILTER = {}
 _OS_NEGATIVE_FILTER['win'] = [
-    # Flaky: https://code.google.com/p/chromedriver/issues/detail?id=373
+    # Flaky: https://bugs.chromium.org/p/chromedriver/issues/detail?id=373
     'RenderedWebElementTest.testHoverPersists',
     'RenderedWebElementTest.canClickOnASuckerFishStyleMenu',
-    # Flaky: https://code.google.com/p/chromedriver/issues/detail?id=416
+    # Flaky: https://bugs.chromium.org/p/chromedriver/issues/detail?id=416
     'TakesScreenshotTest.testShouldCaptureScreenshotAtFramePageAfterSwitching',
     'TakesScreenshotTest.testShouldCaptureScreenshotAtFramePage',
 ]
 _OS_NEGATIVE_FILTER['linux'] = [
-    # Flaky: https://code.google.com/p/chromedriver/issues/detail?id=416
+    # Flaky: https://bugs.chromium.org/p/chromedriver/issues/detail?id=416
     'TakesScreenshotTest.testShouldCaptureScreenshotAtFramePage',
-    # Flaky: https://code.google.com/p/chromedriver/issues/detail?id=1148
+    # Flaky: https://bugs.chromium.org/p/chromedriver/issues/detail?id=1148
     'CombinedInputActionsTest.testCombiningShiftAndClickResultsInANewWindow',
-    # Flaky: https://code.google.com/p/chromedriver/issues/detail?id=1150
+    # Flaky: https://bugs.chromium.org/p/chromedriver/issues/detail?id=1150
     'BasicKeyboardInterfaceTest.testBasicKeyboardInputOnActiveElement',
 ]
 _OS_NEGATIVE_FILTER['mac'] = [
-    # https://code.google.com/p/chromedriver/issues/detail?id=26
+    # https://bugs.chromium.org/p/chromedriver/issues/detail?id=26
     'AlertsTest.testAlertShouldNotAllowAdditionalCommandsIfDismissed',
     'AlertsTest.testShouldAllowUsersToDismissAnAlertManually',
     'FormHandlingTest.handleFormWithJavascriptAction',
-    # Flaky: https://code.google.com/p/chromedriver/issues/detail?id=354
+    # Flaky: https://bugs.chromium.org/p/chromedriver/issues/detail?id=354
     'AlertsTest.testShouldAllowUsersToAcceptAnAlertInAFrame',
-    # Flaky: https://code.google.com/p/chromedriver/issues/detail?id=375
+    # Flaky: https://bugs.chromium.org/p/chromedriver/issues/detail?id=375
     'PageLoadingTest.testShouldBeAbleToNavigateBackInTheBrowserHistoryInPresenceOfIframes',
-    # Flaky: https://code.google.com/p/chromedriver/issues/detail?id=416
+    # Flaky: https://bugs.chromium.org/p/chromedriver/issues/detail?id=416
     'TakesScreenshotTest.testShouldCaptureScreenshotAtFramePage',
-    # Flaky: https://code.google.com/p/chromedriver/issues/detail?id=1149
+    # Flaky: https://bugs.chromium.org/p/chromedriver/issues/detail?id=1149
     'ChromeDriverTests.testShouldAllowTheUserToSwitchToAnIFrameAndRemainFocusedOnIt',
 ]
 
@@ -193,26 +198,26 @@
     # Test is written using local files; doesn't work on Android.
     'UploadTest.testFileUploading',
 
-    # Flaky: https://code.google.com/p/chromedriver/issues/detail?id=528
+    # Flaky: https://bugs.chromium.org/p/chromedriver/issues/detail?id=528
     'PageLoadingTest.testShouldDoNothingIfThereIsNothingToGoBackTo',
 
-    # https://code.google.com/p/chromedriver/issues/detail?id=604
+    # https://bugs.chromium.org/p/chromedriver/issues/detail?id=604
     'I18nTest.testShouldBeAbleToActivateIMEEngine',
 
-    # https://code.google.com/p/chromium/issues/detail?id=418590
+    # https://bugs.chromium.org/p/chromium/issues/detail?id=418590
     'ClearTest.testContentEditableAreaShouldClear',
     'ClickScrollingTest.testClickingOnAnchorScrollsPage',
     'JavascriptEnabledDriverTest.testChangeEventIsFiredAppropriatelyWhenFocusIsLost',
     'TypingTest.testShouldBeAbleToTypeIntoEmptyContentEditableElement',
 
-    # https://code.google.com/p/chromedriver/issues/detail?id=922
+    # https://bugs.chromium.org/p/chromedriver/issues/detail?id=922
     'CorrectEventFiringTest.testShouldEmitOnClickEventsWhenSelectingElements',
     'CorrectEventFiringTest.testSendingKeysToAnotherElementShouldCauseTheBlurEventToFire',
 
-    # https://code.google.com/p/chromedriver/issues/detail?id=1176
+    # https://bugs.chromium.org/p/chromedriver/issues/detail?id=1176
     'ChromeDriverTests.clientLogShouldBeEnabledByDefault',
 
-    # https://code.google.com/p/chromedriver/issues/detail?id=1119
+    # https://bugs.chromium.org/p/chromedriver/issues/detail?id=1119
     'CombinedInputActionsTest.testCanClickOnLinksWithAnOffset',
     'CombinedInputActionsTest.testMouseMovementWorksWhenNavigatingToAnotherPage',
     # https://bugs.chromium.org/p/chromedriver/issues/detail?id=1005
@@ -247,7 +252,7 @@
 )
 _OS_NEGATIVE_FILTER['android:chrome_beta'] = (
     _OS_NEGATIVE_FILTER['android:chrome'] + [
-         # https://code.google.com/p/chromedriver/issues/detail?id=1220
+         # https://bugs.chromium.org/p/chromedriver/issues/detail?id=1220
         'BasicMouseInterfaceTest.testDragAndDrop',
         'BasicMouseInterfaceTest.testDraggingElementWithMouseFiresEvents',
         'BasicMouseInterfaceTest.testDraggingElementWithMouseMovesItToAnotherList',
@@ -269,7 +274,7 @@
 )
 _OS_NEGATIVE_FILTER['android:chromedriver_webview_shell'] = (
     _OS_NEGATIVE_FILTER['android:chrome'] + [
-         # https://code.google.com/p/chromedriver/issues/detail?id=645
+         # https://bugs.chromium.org/p/chromedriver/issues/detail?id=645
         'ClickScrollingTest.testShouldBeAbleToClickElementInAFrameThatIsOutOfView',
         'ClickScrollingTest.testShouldBeAbleToClickElementThatIsOutOfViewInAFrameThatIsOutOfView',
         'FormHandlingTest.handleFormWithJavascriptAction',
diff --git a/chrome/test/data/extensions/api_test/developer/generated_output/behllobkkfkfnphdnhnkndlbkcpglgmj.json b/chrome/test/data/extensions/api_test/developer/generated_output/behllobkkfkfnphdnhnkndlbkcpglgmj.json
index adf92ff1..db9884b 100644
--- a/chrome/test/data/extensions/api_test/developer/generated_output/behllobkkfkfnphdnhnkndlbkcpglgmj.json
+++ b/chrome/test/data/extensions/api_test/developer/generated_output/behllobkkfkfnphdnhnkndlbkcpglgmj.json
@@ -25,8 +25,7 @@
       "isEnabled": true
    },
    "installWarnings": [  ],
-   "location": "UNKNOWN",
-   "locationText": "Not from Chrome Web Store.",
+   "location": "UNPACKED",
    "manifestErrors": [  ],
    "mustRemainInstalled": false,
    "name": "__MSG_chrome_extension_name__",
diff --git a/chrome/test/data/extensions/api_test/developer/generated_output/bjafgdebaacbbbecmhlhpofkepfkgcpa.json b/chrome/test/data/extensions/api_test/developer/generated_output/bjafgdebaacbbbecmhlhpofkepfkgcpa.json
index 7196363..4a59bef 100644
--- a/chrome/test/data/extensions/api_test/developer/generated_output/bjafgdebaacbbbecmhlhpofkepfkgcpa.json
+++ b/chrome/test/data/extensions/api_test/developer/generated_output/bjafgdebaacbbbecmhlhpofkepfkgcpa.json
@@ -25,8 +25,7 @@
       "isEnabled": true
    },
    "installWarnings": [  ],
-   "location": "UNKNOWN",
-   "locationText": "Not from Chrome Web Store.",
+   "location": "UNPACKED",
    "manifestErrors": [  ],
    "mustRemainInstalled": false,
    "name": "My extension 3",
diff --git a/chrome/test/data/extensions/api_test/developer/generated_output/hpiknbiabeeppbpihjehijgoemciehgk.json b/chrome/test/data/extensions/api_test/developer/generated_output/hpiknbiabeeppbpihjehijgoemciehgk.json
index 3bbb5cc..cd016d5 100644
--- a/chrome/test/data/extensions/api_test/developer/generated_output/hpiknbiabeeppbpihjehijgoemciehgk.json
+++ b/chrome/test/data/extensions/api_test/developer/generated_output/hpiknbiabeeppbpihjehijgoemciehgk.json
@@ -25,8 +25,7 @@
       "isEnabled": true
    },
    "installWarnings": [  ],
-   "location": "UNKNOWN",
-   "locationText": "Not from Chrome Web Store.",
+   "location": "UNPACKED",
    "manifestErrors": [  ],
    "mustRemainInstalled": false,
    "name": "My extension 2",
diff --git a/chrome/test/data/extensions/platform_apps/web_view/shim/main.js b/chrome/test/data/extensions/platform_apps/web_view/shim/main.js
index 2a4ccb3..dfedddc 100644
--- a/chrome/test/data/extensions/platform_apps/web_view/shim/main.js
+++ b/chrome/test/data/extensions/platform_apps/web_view/shim/main.js
@@ -2935,6 +2935,24 @@
   document.body.appendChild(webview);
 }
 
+function testNavigateToPDFInWebview() {
+  var webview = document.createElement('webview');
+  var pdfUrl = 'test.pdf';
+  // partition 'foobar' has access to local resource |pdfUrl|.
+  webview.partition = 'foobar';
+  webview.onloadabort = embedder.test.fail;
+
+  var loadstopHandler = function(e) {
+    webview.removeEventListener('loadstop', loadstopHandler);
+    webview.addEventListener('loadstop', embedder.test.succeed);
+    webview.setAttribute('src', pdfUrl);
+  };
+  webview.addEventListener('loadstop', loadstopHandler);
+
+  webview.setAttribute('src', 'about:blank');
+  document.body.appendChild(webview);
+}
+
 // This test verifies that mailto links are enabled.
 function testMailtoLink() {
   var webview = new WebView();
@@ -3135,6 +3153,7 @@
   'testCloseNewWindowCleanup': testCloseNewWindowCleanup,
   'testFocusWhileFocused': testFocusWhileFocused,
   'testPDFInWebview': testPDFInWebview,
+  'testNavigateToPDFInWebview': testNavigateToPDFInWebview,
   'testMailtoLink': testMailtoLink,
   'testRendererNavigationRedirectWhileUnattached':
        testRendererNavigationRedirectWhileUnattached,
diff --git a/chrome/test/data/webui/print_preview.js b/chrome/test/data/webui/print_preview.js
index 3282b34..0bd34708 100644
--- a/chrome/test/data/webui/print_preview.js
+++ b/chrome/test/data/webui/print_preview.js
@@ -375,6 +375,10 @@
   };
 }
 
+function isPrintAsImageEnabled() {
+  return !cr.isWindows && !cr.isMac;
+}
+
 // Test restore settings with one destination.
 TEST_F('PrintPreviewWebUITest', 'TestPrintPreviewRestoreLocalDestination',
     function() {
@@ -555,8 +559,8 @@
   var otherOptions = $('other-options-settings');
   // If rasterization is an option, other options should be visible. If not,
   // there should be no available other options.
-  checkSectionVisible(otherOptions, !cr.isWindows && !cr.isMac);
-  if (!cr.isWindows && !cr.isMac) {
+  checkSectionVisible(otherOptions, isPrintAsImageEnabled());
+  if (isPrintAsImageEnabled()) {
     checkElementDisplayed(
         otherOptions.querySelector('#fit-to-page-container'), false);
     checkElementDisplayed(
@@ -578,8 +582,7 @@
   var otherOptions = $('other-options-settings');
   var fitToPage = otherOptions.querySelector('#fit-to-page-container');
   var rasterize;
-  var rasterizeEnabled = !cr.isWindows && !cr.isMac;
-  if (rasterizeEnabled)
+  if (isPrintAsImageEnabled())
     rasterize = otherOptions.querySelector('#rasterize-container');
   var mediaSize = $('media-size-settings');
   var scalingSettings = $('scaling-settings');
@@ -588,7 +591,7 @@
   // available).
   checkSectionVisible(otherOptions, true);
   checkElementDisplayed(fitToPage, false);
-  if (rasterizeEnabled)
+  if (isPrintAsImageEnabled())
     checkElementDisplayed(rasterize, false);
   checkSectionVisible(mediaSize, false);
   checkSectionVisible(scalingSettings, false);
@@ -596,7 +599,7 @@
   this.expandMoreSettings();
 
   checkElementDisplayed(fitToPage, false);
-  if (rasterizeEnabled)
+  if (isPrintAsImageEnabled())
     checkElementDisplayed(rasterize, false);
   checkSectionVisible(mediaSize, true);
   checkSectionVisible(scalingSettings, true);
@@ -616,21 +619,20 @@
   var scalingSettings = $('scaling-settings');
   var fitToPageContainer =
       otherOptions.querySelector('#fit-to-page-container');
-  var rasterizeEnabled = !cr.isWindows && !cr.isMac;
   var rasterizeContainer;
-  if (rasterizeEnabled) {
+  if (isPrintAsImageEnabled()) {
     rasterizeContainer =
       otherOptions.querySelector('#rasterize-container');
   }
 
   checkSectionVisible(otherOptions, true);
   checkElementDisplayed(fitToPageContainer, true);
-  if (rasterizeEnabled)
+  if (isPrintAsImageEnabled())
     checkElementDisplayed(rasterizeContainer, false);
   expectTrue(
       fitToPageContainer.querySelector('.checkbox').checked);
   this.expandMoreSettings();
-  if (rasterizeEnabled) {
+  if (isPrintAsImageEnabled()) {
     checkElementDisplayed(rasterizeContainer, true);
     expectFalse(
         rasterizeContainer.querySelector('.checkbox').checked);
diff --git a/chrome/tools/safe_browsing/DEPS b/chrome/tools/safe_browsing/DEPS
new file mode 100644
index 0000000..322a686
--- /dev/null
+++ b/chrome/tools/safe_browsing/DEPS
@@ -0,0 +1,3 @@
+include_rules = [
+  "+components/safe_browsing/csd.pb.h",
+]
diff --git a/chrome/tools/safe_browsing/sb_sigutil.cc b/chrome/tools/safe_browsing/sb_sigutil.cc
index 88ca8d6ac..c33ffa7 100644
--- a/chrome/tools/safe_browsing/sb_sigutil.cc
+++ b/chrome/tools/safe_browsing/sb_sigutil.cc
@@ -16,7 +16,7 @@
 #include "base/logging.h"
 #include "base/memory/ref_counted.h"
 #include "chrome/common/safe_browsing/binary_feature_extractor.h"
-#include "chrome/common/safe_browsing/csd.pb.h"
+#include "components/safe_browsing/csd.pb.h"
 
 // Command-line switch for the executable to extract a signature from.
 static const char kExecutable[] = "executable";
diff --git a/chrome/utility/importer/firefox_importer_unittest_utils_mac.cc b/chrome/utility/importer/firefox_importer_unittest_utils_mac.cc
index e94d671..423e4f7 100644
--- a/chrome/utility/importer/firefox_importer_unittest_utils_mac.cc
+++ b/chrome/utility/importer/firefox_importer_unittest_utils_mac.cc
@@ -159,7 +159,9 @@
   child_process_ = LaunchNSSDecrypterChildProcess(
       nss_path, channel_pair.PassClientHandle(), token);
   if (child_process_.IsValid())
-    process.Connect(child_process_.Handle(), channel_pair.PassServerHandle());
+    process.Connect(
+        child_process_.Handle(),
+        mojo::edk::ConnectionParams(channel_pair.PassServerHandle()));
   return child_process_.IsValid();
 }
 
diff --git a/chrome/utility/safe_browsing/mac/BUILD.gn b/chrome/utility/safe_browsing/mac/BUILD.gn
index 90b9329..60d6979 100644
--- a/chrome/utility/safe_browsing/mac/BUILD.gn
+++ b/chrome/utility/safe_browsing/mac/BUILD.gn
@@ -16,7 +16,7 @@
     ":dmg_common",
     "//base",
     "//chrome/common",
-    "//chrome/common/safe_browsing:proto",
+    "//components/safe_browsing:csd_proto",
   ]
 }
 
diff --git a/chrome/utility/safe_browsing/mac/DEPS b/chrome/utility/safe_browsing/mac/DEPS
new file mode 100644
index 0000000..322a686
--- /dev/null
+++ b/chrome/utility/safe_browsing/mac/DEPS
@@ -0,0 +1,3 @@
+include_rules = [
+  "+components/safe_browsing/csd.pb.h",
+]
diff --git a/chrome/utility/safe_browsing/mac/dmg_analyzer.cc b/chrome/utility/safe_browsing/mac/dmg_analyzer.cc
index 94cb913..a93d19c 100644
--- a/chrome/utility/safe_browsing/mac/dmg_analyzer.cc
+++ b/chrome/utility/safe_browsing/mac/dmg_analyzer.cc
@@ -13,10 +13,10 @@
 #include "base/macros.h"
 #include "base/strings/utf_string_conversions.h"
 #include "chrome/common/safe_browsing/binary_feature_extractor.h"
-#include "chrome/common/safe_browsing/csd.pb.h"
 #include "chrome/common/safe_browsing/mach_o_image_reader_mac.h"
 #include "chrome/utility/safe_browsing/mac/dmg_iterator.h"
 #include "chrome/utility/safe_browsing/mac/read_stream.h"
+#include "components/safe_browsing/csd.pb.h"
 #include "crypto/secure_hash.h"
 #include "crypto/sha2.h"
 
diff --git a/chromeos/components/tether/BUILD.gn b/chromeos/components/tether/BUILD.gn
index 3a14c145..33ddb7c 100644
--- a/chromeos/components/tether/BUILD.gn
+++ b/chromeos/components/tether/BUILD.gn
@@ -6,6 +6,8 @@
 
 static_library("tether") {
   sources = [
+    "active_host.cc",
+    "active_host.h",
     "ble_advertisement_device_queue.cc",
     "ble_advertisement_device_queue.h",
     "ble_advertiser.cc",
@@ -28,6 +30,8 @@
     "host_scanner_operation.h",
     "initializer.cc",
     "initializer.h",
+    "keep_alive_operation.cc",
+    "keep_alive_operation.h",
     "local_device_data_provider.cc",
     "local_device_data_provider.h",
     "message_transfer_operation.cc",
@@ -83,6 +87,7 @@
   testonly = true
 
   sources = [
+    "active_host_unittest.cc",
     "ble_advertisement_device_queue_unittest.cc",
     "ble_advertiser_unittest.cc",
     "ble_connection_manager_unittest.cc",
@@ -92,6 +97,7 @@
     "host_scan_scheduler_unittest.cc",
     "host_scanner_operation_unittest.cc",
     "host_scanner_unittest.cc",
+    "keep_alive_operation_unittest.cc",
     "local_device_data_provider_unittest.cc",
     "message_transfer_operation_unittest.cc",
     "message_wrapper_unittest.cc",
diff --git a/chromeos/components/tether/active_host.cc b/chromeos/components/tether/active_host.cc
new file mode 100644
index 0000000..ddfa0fe8
--- /dev/null
+++ b/chromeos/components/tether/active_host.cc
@@ -0,0 +1,139 @@
+// 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/active_host.h"
+
+#include "base/bind.h"
+#include "base/values.h"
+#include "chromeos/components/tether/pref_names.h"
+#include "chromeos/components/tether/tether_host_fetcher.h"
+#include "components/cryptauth/remote_device.h"
+#include "components/prefs/pref_registry_simple.h"
+#include "components/prefs/pref_service.h"
+#include "components/proximity_auth/logging/logging.h"
+
+namespace chromeos {
+
+namespace tether {
+
+ActiveHost::ActiveHost(TetherHostFetcher* tether_host_fetcher,
+                       PrefService* pref_service)
+    : tether_host_fetcher_(tether_host_fetcher),
+      pref_service_(pref_service),
+      weak_ptr_factory_(this) {}
+
+ActiveHost::~ActiveHost() {}
+
+// static
+void ActiveHost::RegisterPrefs(PrefRegistrySimple* registry) {
+  registry->RegisterIntegerPref(
+      prefs::kActiveHostStatus,
+      static_cast<int>(ActiveHostStatus::DISCONNECTED));
+  registry->RegisterStringPref(prefs::kActiveHostDeviceId, "");
+  registry->RegisterStringPref(prefs::kWifiNetworkId, "");
+}
+
+void ActiveHost::SetActiveHostDisconnected() {
+  SetActiveHost(ActiveHostStatus::DISCONNECTED, "" /* active_host_device_id */,
+                "" /* wifi_network_id */);
+}
+
+void ActiveHost::SetActiveHostConnecting(
+    const std::string& active_host_device_id) {
+  DCHECK(!active_host_device_id.empty());
+
+  SetActiveHost(ActiveHostStatus::CONNECTING, active_host_device_id,
+                "" /* wifi_network_id */);
+}
+
+void ActiveHost::SetActiveHostConnected(
+    const std::string& active_host_device_id,
+    const std::string& wifi_network_id) {
+  DCHECK(!active_host_device_id.empty());
+  DCHECK(!wifi_network_id.empty());
+
+  SetActiveHost(ActiveHostStatus::CONNECTED, active_host_device_id,
+                wifi_network_id);
+}
+
+void ActiveHost::GetActiveHost(const ActiveHostCallback& active_host_callback) {
+  ActiveHostStatus status = GetActiveHostStatus();
+
+  if (status == ActiveHostStatus::DISCONNECTED) {
+    active_host_callback.Run(status, nullptr /* active_host */,
+                             "" /* wifi_network_id */);
+    return;
+  }
+
+  std::string active_host_device_id = GetActiveHostDeviceId();
+  DCHECK(!active_host_device_id.empty());
+
+  tether_host_fetcher_->FetchTetherHost(
+      active_host_device_id,
+      base::Bind(&ActiveHost::OnTetherHostFetched,
+                 weak_ptr_factory_.GetWeakPtr(), active_host_callback));
+}
+
+ActiveHost::ActiveHostStatus ActiveHost::GetActiveHostStatus() const {
+  return static_cast<ActiveHostStatus>(
+      pref_service_->GetInteger(prefs::kActiveHostStatus));
+}
+
+const std::string ActiveHost::GetActiveHostDeviceId() const {
+  return pref_service_->GetString(prefs::kActiveHostDeviceId);
+}
+
+const std::string ActiveHost::GetWifiNetworkId() const {
+  return pref_service_->GetString(prefs::kWifiNetworkId);
+}
+
+void ActiveHost::SetActiveHost(ActiveHostStatus active_host_status,
+                               const std::string& active_host_device_id,
+                               const std::string& wifi_network_id) {
+  pref_service_->Set(prefs::kActiveHostStatus,
+                     base::Value(static_cast<int>(active_host_status)));
+  pref_service_->Set(prefs::kActiveHostDeviceId,
+                     base::Value(active_host_device_id));
+  pref_service_->Set(prefs::kWifiNetworkId, base::Value(wifi_network_id));
+}
+
+void ActiveHost::OnTetherHostFetched(
+    const ActiveHostCallback& active_host_callback,
+    std::unique_ptr<cryptauth::RemoteDevice> remote_device) {
+  if (GetActiveHostDeviceId().empty() || !remote_device) {
+    DCHECK(GetActiveHostStatus() == ActiveHostStatus::DISCONNECTED);
+    DCHECK(GetWifiNetworkId().empty());
+
+    // If the active host became disconnected while the tether host was being
+    // fetched, forward this information to the callback.
+    active_host_callback.Run(ActiveHostStatus::DISCONNECTED,
+                             nullptr /* active_host */,
+                             "" /* wifi_network_id */);
+    return;
+  }
+
+  if (GetActiveHostDeviceId() != remote_device->GetDeviceId()) {
+    // If the active host has changed while the tether host was being fetched,
+    // perform the fetch again.
+    GetActiveHost(active_host_callback);
+    return;
+  }
+
+  if (GetActiveHostStatus() == ActiveHostStatus::CONNECTING) {
+    DCHECK(GetWifiNetworkId().empty());
+    active_host_callback.Run(ActiveHostStatus::CONNECTING,
+                             std::move(remote_device),
+                             "" /* wifi_network_id */);
+    return;
+  }
+
+  DCHECK(GetActiveHostStatus() == ActiveHostStatus::CONNECTED);
+  DCHECK(!GetWifiNetworkId().empty());
+  active_host_callback.Run(ActiveHostStatus::CONNECTED,
+                           std::move(remote_device), GetWifiNetworkId());
+}
+
+}  // namespace tether
+
+}  // namespace chromeos
diff --git a/chromeos/components/tether/active_host.h b/chromeos/components/tether/active_host.h
new file mode 100644
index 0000000..3e8e8cb
--- /dev/null
+++ b/chromeos/components/tether/active_host.h
@@ -0,0 +1,98 @@
+// 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_ACTIVE_HOST_H_
+#define CHROMEOS_COMPONENTS_TETHER_ACTIVE_HOST_H_
+
+#include <string>
+
+#include "base/callback.h"
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+
+class PrefRegistrySimple;
+class PrefService;
+
+namespace cryptauth {
+struct RemoteDevice;
+}  // namespace cryptauth
+
+namespace chromeos {
+
+namespace tether {
+
+class TetherHostFetcher;
+
+// ActiveHost tracks metadata about the current connection to a tether host.
+// This data is persisted to user preferences.
+class ActiveHost {
+ public:
+  // Enumeration used to describe the state of the active connection to a tether
+  // host.
+  enum class ActiveHostStatus {
+    DISCONNECTED = 0,
+    CONNECTING = 1,
+    CONNECTED = 2
+  };
+
+  ActiveHost(TetherHostFetcher* tether_host_fetcher, PrefService* pref_service);
+  virtual ~ActiveHost();
+
+  // Registers the prefs used by this class to the given |registry|.
+  static void RegisterPrefs(PrefRegistrySimple* registry);
+
+  // Sets the active host to be no host at all (i.e., the local device is not
+  // connecting or connected to a tether host).
+  void SetActiveHostDisconnected();
+
+  // Sets the active host to be the device with ID |active_host_device_id| and
+  // records that the there is an active attempt to connect to that host (i.e.,
+  // the host is not yet connected but it is in the process of connecting).
+  void SetActiveHostConnecting(const std::string& active_host_device_id);
+
+  // Sets the active host to be the device with ID |active_host_device_id| and
+  // that the local device is connected to that device on the mobile hotspot
+  // with Wi-Fi network ID |wifi_network_id|.
+  void SetActiveHostConnected(const std::string& active_host_device_id,
+                              const std::string& wifi_network_id);
+
+  // Gets the active host and associated metadata asynchronously. If
+  // the active host status is...
+  //     DISCONNECTED: The callback's |active_host| parameter will be nullptr
+  //                   and |wifi_network_id| parameter will be "".
+  //     CONNECTING: The callback's |wifi_network_id| will be "".
+  //     CONNECTED: All three parameter  will be present.
+  using ActiveHostCallback =
+      base::Callback<void(ActiveHostStatus active_host_status,
+                          std::unique_ptr<cryptauth::RemoteDevice> active_host,
+                          const std::string& wifi_network_id)>;
+  void GetActiveHost(const ActiveHostCallback& active_host_callback);
+
+  // Synchronous getter methods which do not return a full RemoteDevice object.
+  ActiveHostStatus GetActiveHostStatus() const;
+  const std::string GetActiveHostDeviceId() const;
+  const std::string GetWifiNetworkId() const;
+
+ private:
+  void SetActiveHost(ActiveHostStatus active_host_status,
+                     const std::string& active_host_device_id,
+                     const std::string& wifi_network_id);
+
+  void OnTetherHostFetched(
+      const ActiveHostCallback& active_host_callback,
+      std::unique_ptr<cryptauth::RemoteDevice> remote_device);
+
+  TetherHostFetcher* tether_host_fetcher_;
+  PrefService* pref_service_;
+
+  base::WeakPtrFactory<ActiveHost> weak_ptr_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(ActiveHost);
+};
+
+}  // namespace tether
+
+}  // namespace chromeos
+
+#endif  // CHROMEOS_COMPONENTS_TETHER_ACTIVE_HOST_H_
diff --git a/chromeos/components/tether/active_host_unittest.cc b/chromeos/components/tether/active_host_unittest.cc
new file mode 100644
index 0000000..c2981ff2
--- /dev/null
+++ b/chromeos/components/tether/active_host_unittest.cc
@@ -0,0 +1,177 @@
+// 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/active_host.h"
+
+#include <memory>
+
+#include "base/bind.h"
+#include "base/macros.h"
+#include "base/memory/ptr_util.h"
+#include "chromeos/components/tether/fake_tether_host_fetcher.h"
+#include "components/cryptauth/remote_device.h"
+#include "components/cryptauth/remote_device_test_util.h"
+#include "components/prefs/testing_pref_service.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace chromeos {
+
+namespace tether {
+
+namespace {
+
+struct GetActiveHostResult {
+  ActiveHost::ActiveHostStatus active_host_status;
+  std::shared_ptr<cryptauth::RemoteDevice> remote_device;
+  std::string wifi_network_id;
+
+  bool operator==(const GetActiveHostResult& other) const {
+    bool devices_equal;
+    if (remote_device) {
+      devices_equal =
+          other.remote_device && *remote_device == *other.remote_device;
+    } else {
+      devices_equal = !other.remote_device;
+    }
+
+    return active_host_status == other.active_host_status && devices_equal &&
+           wifi_network_id == other.wifi_network_id;
+  }
+};
+
+}  // namespace
+
+class ActiveHostTest : public testing::Test {
+ public:
+  ActiveHostTest() : test_devices_(cryptauth::GenerateTestRemoteDevices(4)) {}
+
+  void SetUp() override {
+    get_active_host_results_.clear();
+
+    test_pref_service_ = base::MakeUnique<TestingPrefServiceSimple>();
+    fake_tether_host_fetcher_ = base::MakeUnique<FakeTetherHostFetcher>(
+        test_devices_, false /* synchronously_reply_with_results */);
+
+    ActiveHost::RegisterPrefs(test_pref_service_->registry());
+    active_host_ = base::MakeUnique<ActiveHost>(fake_tether_host_fetcher_.get(),
+                                                test_pref_service_.get());
+  }
+
+  void OnActiveHostFetched(ActiveHost::ActiveHostStatus active_host_status,
+                           std::unique_ptr<cryptauth::RemoteDevice> active_host,
+                           const std::string& wifi_network_id) {
+    get_active_host_results_.push_back(GetActiveHostResult{
+        active_host_status, std::move(active_host), wifi_network_id});
+  }
+
+  void VerifyActiveHostDisconnected() {
+    EXPECT_EQ(ActiveHost::ActiveHostStatus::DISCONNECTED,
+              active_host_->GetActiveHostStatus());
+    EXPECT_TRUE(active_host_->GetActiveHostDeviceId().empty());
+    EXPECT_TRUE(active_host_->GetWifiNetworkId().empty());
+
+    active_host_->GetActiveHost(base::Bind(&ActiveHostTest::OnActiveHostFetched,
+                                           base::Unretained(this)));
+    fake_tether_host_fetcher_->InvokePendingCallbacks();
+    ASSERT_EQ(1u, get_active_host_results_.size());
+    EXPECT_EQ((GetActiveHostResult{ActiveHost::ActiveHostStatus::DISCONNECTED,
+                                   nullptr, std::string()}),
+              get_active_host_results_[0]);
+  }
+
+  const std::vector<cryptauth::RemoteDevice> test_devices_;
+
+  std::unique_ptr<TestingPrefServiceSimple> test_pref_service_;
+  std::unique_ptr<FakeTetherHostFetcher> fake_tether_host_fetcher_;
+
+  std::vector<GetActiveHostResult> get_active_host_results_;
+
+  std::unique_ptr<ActiveHost> active_host_;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(ActiveHostTest);
+};
+
+TEST_F(ActiveHostTest, TestDefaultValues) {
+  VerifyActiveHostDisconnected();
+}
+
+TEST_F(ActiveHostTest, TestDisconnected) {
+  active_host_->SetActiveHostDisconnected();
+  VerifyActiveHostDisconnected();
+}
+
+TEST_F(ActiveHostTest, TestConnecting) {
+  active_host_->SetActiveHostConnecting(test_devices_[0].GetDeviceId());
+
+  EXPECT_EQ(ActiveHost::ActiveHostStatus::CONNECTING,
+            active_host_->GetActiveHostStatus());
+  EXPECT_EQ(test_devices_[0].GetDeviceId(),
+            active_host_->GetActiveHostDeviceId());
+  EXPECT_TRUE(active_host_->GetWifiNetworkId().empty());
+
+  active_host_->GetActiveHost(
+      base::Bind(&ActiveHostTest::OnActiveHostFetched, base::Unretained(this)));
+  fake_tether_host_fetcher_->InvokePendingCallbacks();
+  ASSERT_EQ(1u, get_active_host_results_.size());
+  EXPECT_EQ(
+      (GetActiveHostResult{ActiveHost::ActiveHostStatus::CONNECTING,
+                           std::shared_ptr<cryptauth::RemoteDevice>(
+                               new cryptauth::RemoteDevice(test_devices_[0])),
+                           std::string()}),
+      get_active_host_results_[0]);
+}
+
+TEST_F(ActiveHostTest, TestConnected) {
+  active_host_->SetActiveHostConnected(test_devices_[0].GetDeviceId(),
+                                       "wifiNetworkId");
+
+  EXPECT_EQ(ActiveHost::ActiveHostStatus::CONNECTED,
+            active_host_->GetActiveHostStatus());
+  EXPECT_EQ(test_devices_[0].GetDeviceId(),
+            active_host_->GetActiveHostDeviceId());
+  EXPECT_EQ("wifiNetworkId", active_host_->GetWifiNetworkId());
+
+  active_host_->GetActiveHost(
+      base::Bind(&ActiveHostTest::OnActiveHostFetched, base::Unretained(this)));
+  fake_tether_host_fetcher_->InvokePendingCallbacks();
+  ASSERT_EQ(1u, get_active_host_results_.size());
+  EXPECT_EQ(
+      (GetActiveHostResult{ActiveHost::ActiveHostStatus::CONNECTED,
+                           std::shared_ptr<cryptauth::RemoteDevice>(
+                               new cryptauth::RemoteDevice(test_devices_[0])),
+                           "wifiNetworkId"}),
+      get_active_host_results_[0]);
+}
+
+TEST_F(ActiveHostTest, TestActiveHostChangesDuringFetch) {
+  active_host_->SetActiveHostConnected(test_devices_[0].GetDeviceId(),
+                                       "wifiNetworkId");
+
+  EXPECT_EQ(ActiveHost::ActiveHostStatus::CONNECTED,
+            active_host_->GetActiveHostStatus());
+  EXPECT_EQ(test_devices_[0].GetDeviceId(),
+            active_host_->GetActiveHostDeviceId());
+  EXPECT_EQ("wifiNetworkId", active_host_->GetWifiNetworkId());
+
+  // Start a fetch for the active host.
+  active_host_->GetActiveHost(
+      base::Bind(&ActiveHostTest::OnActiveHostFetched, base::Unretained(this)));
+
+  // While the fetch is in progress, simulate a disconnection occurring.
+  active_host_->SetActiveHostDisconnected();
+
+  // Now, finish the fech.
+  fake_tether_host_fetcher_->InvokePendingCallbacks();
+
+  // The resulting callback should indicate that there is no active host.
+  ASSERT_EQ(1u, get_active_host_results_.size());
+  EXPECT_EQ((GetActiveHostResult{ActiveHost::ActiveHostStatus::DISCONNECTED,
+                                 nullptr, ""}),
+            get_active_host_results_[0]);
+}
+
+}  // namespace tether
+
+}  // namespace cryptauth
diff --git a/chromeos/components/tether/keep_alive_operation.cc b/chromeos/components/tether/keep_alive_operation.cc
new file mode 100644
index 0000000..d1cf0db
--- /dev/null
+++ b/chromeos/components/tether/keep_alive_operation.cc
@@ -0,0 +1,65 @@
+// 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/keep_alive_operation.h"
+
+#include "chromeos/components/tether/message_wrapper.h"
+#include "chromeos/components/tether/proto/tether.pb.h"
+#include "components/proximity_auth/logging/logging.h"
+
+namespace chromeos {
+
+namespace tether {
+
+// static
+KeepAliveOperation::Factory* KeepAliveOperation::Factory::factory_instance_ =
+    nullptr;
+
+// static
+std::unique_ptr<KeepAliveOperation> KeepAliveOperation::Factory::NewInstance(
+    const cryptauth::RemoteDevice& device_to_connect,
+    BleConnectionManager* connection_manager) {
+  if (!factory_instance_) {
+    factory_instance_ = new Factory();
+  }
+  return factory_instance_->BuildInstance(device_to_connect,
+                                          connection_manager);
+}
+
+// static
+void KeepAliveOperation::Factory::SetInstanceForTesting(Factory* factory) {
+  factory_instance_ = factory;
+}
+
+std::unique_ptr<KeepAliveOperation> KeepAliveOperation::Factory::BuildInstance(
+    const cryptauth::RemoteDevice& device_to_connect,
+    BleConnectionManager* connection_manager) {
+  return base::MakeUnique<KeepAliveOperation>(device_to_connect,
+                                              connection_manager);
+}
+
+KeepAliveOperation::KeepAliveOperation(
+    const cryptauth::RemoteDevice& device_to_connect,
+    BleConnectionManager* connection_manager)
+    : MessageTransferOperation(
+          std::vector<cryptauth::RemoteDevice>{device_to_connect},
+          connection_manager) {}
+
+KeepAliveOperation::~KeepAliveOperation() {}
+
+void KeepAliveOperation::OnDeviceAuthenticated(
+    const cryptauth::RemoteDevice& remote_device) {
+  DCHECK(remote_devices().size() == 1u && remote_devices()[0] == remote_device);
+
+  SendMessageToDevice(remote_device,
+                      base::MakeUnique<MessageWrapper>(KeepAliveTickle()));
+}
+
+MessageType KeepAliveOperation::GetMessageTypeForConnection() {
+  return MessageType::KEEP_ALIVE_TICKLE;
+}
+
+}  // namespace tether
+
+}  // namespace chromeos
diff --git a/chromeos/components/tether/keep_alive_operation.h b/chromeos/components/tether/keep_alive_operation.h
new file mode 100644
index 0000000..c5099e9
--- /dev/null
+++ b/chromeos/components/tether/keep_alive_operation.h
@@ -0,0 +1,62 @@
+// 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_KEEP_ALIVE_OPERATION_H_
+#define CHROMEOS_COMPONENTS_TETHER_KEEP_ALIVE_OPERATION_H_
+
+#include "chromeos/components/tether/message_transfer_operation.h"
+
+namespace chromeos {
+
+namespace tether {
+
+class BleConnectionManager;
+
+// Operation which sends a keep-alive message to a tether host.
+// TODO(khorimoto/hansberry): Consider changing protocol to receive a
+//                            DeviceStatus update after sending the
+//                            KeepAliveTickle message.
+class KeepAliveOperation : public MessageTransferOperation {
+ public:
+  class Factory {
+   public:
+    static std::unique_ptr<KeepAliveOperation> NewInstance(
+        const cryptauth::RemoteDevice& device_to_connect,
+        BleConnectionManager* connection_manager);
+
+    static void SetInstanceForTesting(Factory* factory);
+
+   protected:
+    virtual std::unique_ptr<KeepAliveOperation> BuildInstance(
+        const cryptauth::RemoteDevice& devices_to_connect,
+        BleConnectionManager* connection_manager);
+
+   private:
+    static Factory* factory_instance_;
+  };
+
+  KeepAliveOperation(const cryptauth::RemoteDevice& device_to_connect,
+                     BleConnectionManager* connection_manager);
+  ~KeepAliveOperation() override;
+
+  void AddObserver(Observer* observer);
+  void RemoveObserver(Observer* observer);
+
+ protected:
+  // MessageTransferOperation:
+  void OnDeviceAuthenticated(
+      const cryptauth::RemoteDevice& remote_device) override;
+  MessageType GetMessageTypeForConnection() override;
+
+ private:
+  friend class KeepAliveOperationTest;
+
+  DISALLOW_COPY_AND_ASSIGN(KeepAliveOperation);
+};
+
+}  // namespace tether
+
+}  // namespace chromeos
+
+#endif  // CHROMEOS_COMPONENTS_TETHER_KEEP_ALIVE_OPERATION_H_
diff --git a/chromeos/components/tether/keep_alive_operation_unittest.cc b/chromeos/components/tether/keep_alive_operation_unittest.cc
new file mode 100644
index 0000000..f0b196d
--- /dev/null
+++ b/chromeos/components/tether/keep_alive_operation_unittest.cc
@@ -0,0 +1,70 @@
+// 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/keep_alive_operation.h"
+
+#include <memory>
+#include <vector>
+
+#include "chromeos/components/tether/fake_ble_connection_manager.h"
+#include "chromeos/components/tether/message_wrapper.h"
+#include "chromeos/components/tether/proto/tether.pb.h"
+#include "components/cryptauth/remote_device_test_util.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace chromeos {
+
+namespace tether {
+
+namespace {
+
+std::string CreateKeepAliveTickleString() {
+  KeepAliveTickle tickle;
+  return MessageWrapper(tickle).ToRawMessage();
+}
+
+}  // namespace
+
+class KeepAliveOperationTest : public testing::Test {
+ protected:
+  KeepAliveOperationTest()
+      : keep_alive_tickle_string_(CreateKeepAliveTickleString()),
+        test_device_(cryptauth::GenerateTestRemoteDevices(1)[0]) {}
+
+  void SetUp() override {
+    fake_ble_connection_manager_ = base::MakeUnique<FakeBleConnectionManager>();
+
+    operation_ = base::WrapUnique(new KeepAliveOperation(
+        test_device_, fake_ble_connection_manager_.get()));
+    operation_->Initialize();
+  }
+
+  void SimulateDeviceAuthenticationAndVerifyMessageSent() {
+    operation_->OnDeviceAuthenticated(test_device_);
+
+    // Verify that the message was sent successfully.
+    std::vector<FakeBleConnectionManager::SentMessage>& sent_messages =
+        fake_ble_connection_manager_->sent_messages();
+    ASSERT_EQ(1u, sent_messages.size());
+    EXPECT_EQ(test_device_, sent_messages[0].remote_device);
+    EXPECT_EQ(keep_alive_tickle_string_, sent_messages[0].message);
+  }
+
+  const std::string keep_alive_tickle_string_;
+  const cryptauth::RemoteDevice test_device_;
+
+  std::unique_ptr<FakeBleConnectionManager> fake_ble_connection_manager_;
+  std::unique_ptr<KeepAliveOperation> operation_;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(KeepAliveOperationTest);
+};
+
+TEST_F(KeepAliveOperationTest, TestSendsKeepAliveTickle) {
+  SimulateDeviceAuthenticationAndVerifyMessageSent();
+}
+
+}  // namespace tether
+
+}  // namespace cryptauth
diff --git a/chromeos/components/tether/pref_names.cc b/chromeos/components/tether/pref_names.cc
index 5ab3772..888dbd3c 100644
--- a/chromeos/components/tether/pref_names.cc
+++ b/chromeos/components/tether/pref_names.cc
@@ -16,6 +16,12 @@
 const char kMostRecentConnectTetheringResponderId[] =
     "tether.most_recent_connect_tethering_responder_ids";
 
+const char kActiveHostStatus[] = "tether.active_host_status";
+
+const char kActiveHostDeviceId[] = "tether.active_host_device_id";
+
+const char kWifiNetworkId[] = "tether.wifi_network_id";
+
 }  // namespace prefs
 
 }  // namespace tether
diff --git a/chromeos/components/tether/pref_names.h b/chromeos/components/tether/pref_names.h
index 7f5b923..d6285b3 100644
--- a/chromeos/components/tether/pref_names.h
+++ b/chromeos/components/tether/pref_names.h
@@ -22,6 +22,18 @@
 // a response code indicating that its hotspot has started up successfully.
 extern const char kMostRecentConnectTetheringResponderId[];
 
+// The status of the active host. The value stored for this key is the integer
+// version of an ActiveHost::ActiveHostStatus enumeration value.
+extern const char kActiveHostStatus[];
+
+// The device ID of the active host. If there is no active host, the value at
+// this key is "".
+extern const char kActiveHostDeviceId[];
+
+// The Wi-Fi network ID of the active host. If there is no active host, the
+// value at this key is "".
+extern const char kWifiNetworkId[];
+
 }  // namespace prefs
 
 }  // namespace tether
diff --git a/components/BUILD.gn b/components/BUILD.gn
index 882d2dac9..dd5ed4e 100644
--- a/components/BUILD.gn
+++ b/components/BUILD.gn
@@ -399,6 +399,7 @@
       "dom_distiller/content/browser/test/dom_distiller_js_browsertest.cc",
       "password_manager/content/renderer/credential_manager_client_browsertest.cc",
       "security_state/content/content_utils_browsertest.cc",
+      "tracing/child/child_trace_message_filter_browsertest.cc",
     ]
 
     data = [
diff --git a/components/arc/arc_session.cc b/components/arc/arc_session.cc
index c369d28..5738ed6 100644
--- a/components/arc/arc_session.cc
+++ b/components/arc/arc_session.cc
@@ -442,7 +442,8 @@
   const base::ProcessHandle kUnusedChildProcessHandle = 0;
   mojo::edk::PlatformChannelPair channel_pair;
   mojo::edk::PendingProcessConnection process;
-  process.Connect(kUnusedChildProcessHandle, channel_pair.PassServerHandle());
+  process.Connect(kUnusedChildProcessHandle,
+                  mojo::edk::ConnectionParams(channel_pair.PassServerHandle()));
 
   mojo::edk::ScopedPlatformHandleVectorPtr handles(
       new mojo::edk::PlatformHandleVector{
diff --git a/components/arc/intent_helper/arc_intent_helper_bridge.cc b/components/arc/intent_helper/arc_intent_helper_bridge.cc
index 953b249c..ef9e4cc 100644
--- a/components/arc/intent_helper/arc_intent_helper_bridge.cc
+++ b/components/arc/intent_helper/arc_intent_helper_bridge.cc
@@ -79,7 +79,7 @@
 
 void ArcIntentHelperBridge::OpenWallpaperPicker() {
   DCHECK(thread_checker_.CalledOnValidThread());
-  ash::WmShell::Get()->wallpaper_controller()->OpenSetWallpaperPage();
+  ash::Shell::GetInstance()->wallpaper_controller()->OpenSetWallpaperPage();
 }
 
 void ArcIntentHelperBridge::SetWallpaperDeprecated(
diff --git a/components/exo/shell_surface_unittest.cc b/components/exo/shell_surface_unittest.cc
index 45cc534..8ad2c3a1 100644
--- a/components/exo/shell_surface_unittest.cc
+++ b/components/exo/shell_surface_unittest.cc
@@ -2,18 +2,19 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include "components/exo/shell_surface.h"
 #include "ash/common/accessibility_delegate.h"
 #include "ash/common/wm/window_state.h"
 #include "ash/common/wm/wm_event.h"
 #include "ash/common/wm_shell.h"
 #include "ash/common/wm_window.h"
 #include "ash/public/cpp/shell_window_ids.h"
+#include "ash/shell.h"
 #include "ash/wm/window_state_aura.h"
 #include "base/message_loop/message_loop.h"
 #include "base/strings/utf_string_conversions.h"
 #include "components/exo/buffer.h"
 #include "components/exo/display.h"
-#include "components/exo/shell_surface.h"
 #include "components/exo/sub_surface.h"
 #include "components/exo/surface.h"
 #include "components/exo/test/exo_test_base.h"
@@ -901,7 +902,7 @@
   EXPECT_FALSE(targeter->SubtreeShouldBeExploredForEvent(shell_window, ev_out));
 
   // Enable spoken feedback.
-  ash::WmShell::Get()->accessibility_delegate()->ToggleSpokenFeedback(
+  ash::Shell::GetInstance()->accessibility_delegate()->ToggleSpokenFeedback(
       ash::A11Y_NOTIFICATION_NONE);
   shell_surface.OnAccessibilityModeChanged();
 
@@ -938,7 +939,7 @@
   EXPECT_EQ(shadow_bounds, shell_surface.shadow_underlay()->bounds());
 
   // Disable spoken feedback. Shadow underlay is restored.
-  ash::WmShell::Get()->accessibility_delegate()->ToggleSpokenFeedback(
+  ash::Shell::GetInstance()->accessibility_delegate()->ToggleSpokenFeedback(
       ash::A11Y_NOTIFICATION_NONE);
   shell_surface.OnAccessibilityModeChanged();
   shell_surface2.OnAccessibilityModeChanged();
diff --git a/components/exo/wm_helper_ash.cc b/components/exo/wm_helper_ash.cc
index a54f8ce..6c305117 100644
--- a/components/exo/wm_helper_ash.cc
+++ b/components/exo/wm_helper_ash.cc
@@ -98,13 +98,14 @@
 }
 
 bool WMHelperAsh::IsSpokenFeedbackEnabled() const {
-  return ash::WmShell::Get()
+  return ash::Shell::GetInstance()
       ->accessibility_delegate()
       ->IsSpokenFeedbackEnabled();
 }
 
 void WMHelperAsh::PlayEarcon(int sound_key) const {
-  return ash::WmShell::Get()->accessibility_delegate()->PlayEarcon(sound_key);
+  return ash::Shell::GetInstance()->accessibility_delegate()->PlayEarcon(
+      sound_key);
 }
 
 void WMHelperAsh::OnWindowActivated(
diff --git a/components/nacl/broker/nacl_broker_listener.cc b/components/nacl/broker/nacl_broker_listener.cc
index 734e5e5..abd72ae 100644
--- a/components/nacl/broker/nacl_broker_listener.cc
+++ b/components/nacl/broker/nacl_broker_listener.cc
@@ -149,8 +149,9 @@
         this, cmd_line, handles, &loader_process);
 
     if (result == sandbox::SBOX_ALL_OK) {
-      pending_process.Connect(loader_process.Handle(),
-                              std::move(parent_handle));
+      pending_process.Connect(
+          loader_process.Handle(),
+          mojo::edk::ConnectionParams(std::move(parent_handle)));
 
       // Note: PROCESS_DUP_HANDLE is necessary here, because:
       // 1) The current process is the broker, which is the loader's parent.
diff --git a/components/offline_pages/core/background/pick_request_task.cc b/components/offline_pages/core/background/pick_request_task.cc
index 006d08b4..516b099 100644
--- a/components/offline_pages/core/background/pick_request_task.cc
+++ b/components/offline_pages/core/background/pick_request_task.cc
@@ -4,6 +4,8 @@
 
 #include "components/offline_pages/core/background/pick_request_task.h"
 
+#include <unordered_set>
+
 #include "base/bind.h"
 #include "base/logging.h"
 #include "base/time/time.h"
@@ -35,13 +37,15 @@
                                  RequestNotPickedCallback not_picked_callback,
                                  RequestCountCallback request_count_callback,
                                  DeviceConditions& device_conditions,
-                                 const std::set<int64_t>& disabled_requests)
+                                 const std::set<int64_t>& disabled_requests,
+                                 std::deque<int64_t>& prioritized_requests)
     : store_(store),
       policy_(policy),
       picked_callback_(picked_callback),
       not_picked_callback_(not_picked_callback),
       request_count_callback_(request_count_callback),
       disabled_requests_(disabled_requests),
+      prioritized_requests_(prioritized_requests),
       weak_ptr_factory_(this) {
   device_conditions_.reset(new DeviceConditions(device_conditions));
 }
@@ -85,19 +89,22 @@
   bool cleanup_needed = false;
 
   size_t available_request_count = 0;
+  // Request ids which are available for picking.
+  std::unordered_set<int64_t> available_request_ids;
 
-  // Iterate once through the requests, keeping track of best candidate.
-  for (unsigned i = 0; i < requests.size(); ++i) {
+  // Iterate through the requests, filter out unavailable requests and get other
+  // information (if cleanup is needed and number of non-user-requested
+  // requests).
+  for (const auto& request : requests) {
     // If the request is expired or has exceeded the retry count, skip it.
-    if (OfflinerPolicyUtils::CheckRequestExpirationStatus(requests[i].get(),
+    if (OfflinerPolicyUtils::CheckRequestExpirationStatus(request.get(),
                                                           policy_) !=
         OfflinerPolicyUtils::RequestExpirationStatus::VALID) {
       cleanup_needed = true;
       continue;
     }
-
-    // If the  request is on the disabled list, skip it.
-    auto search = disabled_requests_.find(requests[i]->request_id());
+    // If the request is on the disabled list, skip it.
+    auto search = disabled_requests_.find(request->request_id());
     if (search != disabled_requests_.end())
       continue;
 
@@ -106,21 +113,50 @@
     // detect if any exist. If we don't find any user-requested tasks, we will
     // inform the "not_picked_callback_" that it needs to schedule a task for
     // non-user-requested items, which have different network and power needs.
-    if (!requests[i]->user_requested())
+    if (!request->user_requested())
       non_user_requested_tasks_remaining = true;
-    if (requests[i]->request_state() ==
-        SavePageRequest::RequestState::AVAILABLE) {
+    if (request->request_state() == SavePageRequest::RequestState::AVAILABLE) {
       available_request_count++;
     }
-    if (!RequestConditionsSatisfied(requests[i].get()))
+    if (!RequestConditionsSatisfied(request.get()))
       continue;
-    if (IsNewRequestBetter(picked_request, requests[i].get(), comparator))
-      picked_request = requests[i].get();
+    available_request_ids.insert(request->request_id());
   }
-
   // Report the request queue counts.
   request_count_callback_.Run(requests.size(), available_request_count);
 
+  // Search for and pick the prioritized request which is available for picking
+  // from |available_request_ids|, the closer to the end means higher priority.
+  // Also if a request in |prioritized_requests_| doesn't exist in |requests|
+  // we're going to remove it.
+  // For every ID in |available_request_ids|, there exists a corresponding
+  // request in |requests|, so this won't be an infinite loop: either we pick a
+  // request, or there's a request being poped from |prioritized_requests_|.
+  while (!picked_request && !prioritized_requests_.empty()) {
+    if (available_request_ids.count(prioritized_requests_.back()) > 0) {
+      for (const auto& request : requests) {
+        if (request->request_id() == prioritized_requests_.back()) {
+          picked_request = request.get();
+          break;
+        }
+      }
+      DCHECK(picked_request);
+    } else {
+      prioritized_requests_.pop_back();
+    }
+  }
+
+  // If no request was found from the priority list, find the best request
+  // according to current policies.
+  if (!picked_request) {
+    for (const auto& request : requests) {
+      if ((available_request_ids.count(request->request_id()) > 0) &&
+          (IsNewRequestBetter(picked_request, request.get(), comparator))) {
+        picked_request = request.get();
+      }
+    }
+  }
+
   // If we have a best request to try next, get the request coodinator to
   // start it.  Otherwise return that we have no candidates.
   if (picked_request != nullptr) {
diff --git a/components/offline_pages/core/background/pick_request_task.h b/components/offline_pages/core/background/pick_request_task.h
index ef04612..b6cad2c 100644
--- a/components/offline_pages/core/background/pick_request_task.h
+++ b/components/offline_pages/core/background/pick_request_task.h
@@ -5,6 +5,7 @@
 #ifndef COMPONENTS_OFFLINE_PAGES_CORE_BACKGROUND_PICK_REQUEST_TASK_H_
 #define COMPONENTS_OFFLINE_PAGES_CORE_BACKGROUND_PICK_REQUEST_TASK_H_
 
+#include <deque>
 #include <set>
 
 #include "base/memory/weak_ptr.h"
@@ -43,7 +44,8 @@
                   RequestNotPickedCallback not_picked_callback,
                   RequestCountCallback request_count_callback,
                   DeviceConditions& device_conditions,
-                  const std::set<int64_t>& disabled_requests);
+                  const std::set<int64_t>& disabled_requests,
+                  std::deque<int64_t>& prioritized_requests);
 
   ~PickRequestTask() override;
 
@@ -94,6 +96,7 @@
   RequestCountCallback request_count_callback_;
   std::unique_ptr<DeviceConditions> device_conditions_;
   const std::set<int64_t>& disabled_requests_;
+  std::deque<int64_t>& prioritized_requests_;
   // Allows us to pass a weak pointer to callbacks.
   base::WeakPtrFactory<PickRequestTask> weak_ptr_factory_;
 };
diff --git a/components/offline_pages/core/background/pick_request_task_unittest.cc b/components/offline_pages/core/background/pick_request_task_unittest.cc
index 1b3ca46..7f686246 100644
--- a/components/offline_pages/core/background/pick_request_task_unittest.cc
+++ b/components/offline_pages/core/background/pick_request_task_unittest.cc
@@ -4,6 +4,7 @@
 
 #include "components/offline_pages/core/background/pick_request_task.h"
 
+#include <deque>
 #include <memory>
 #include <set>
 
@@ -127,6 +128,7 @@
   std::unique_ptr<OfflinerPolicy> policy_;
   RequestCoordinatorEventLogger event_logger_;
   std::set<int64_t> disabled_requests_;
+  std::deque<int64_t> prioritized_requests_;
   std::unique_ptr<PickRequestTask> task_;
   bool request_queue_not_picked_called_;
   bool cleanup_needed_;
@@ -215,7 +217,7 @@
                  base::Unretained(this)),
       base::Bind(&PickRequestTaskTest::RequestCountCallback,
                  base::Unretained(this)),
-      conditions, disabled_requests_));
+      conditions, disabled_requests_, prioritized_requests_));
   task_->SetTaskCompletionCallbackForTesting(
       task_runner_.get(),
       base::Bind(&PickRequestTaskTest::TaskCompletionCallback,
@@ -493,4 +495,74 @@
   EXPECT_TRUE(task_complete_called_);
 }
 
+TEST_F(PickRequestTaskTest, ChoosePrioritizedRequests) {
+  prioritized_requests_.push_back(kRequestId2);
+  MakePickRequestTask();
+
+  base::Time creation_time = base::Time::Now();
+  SavePageRequest request1(kRequestId1, kUrl1, kClientId1, creation_time,
+                           kUserRequested);
+  SavePageRequest request2(kRequestId2, kUrl2, kClientId2, creation_time,
+                           kUserRequested);
+  // Since default policy prefer untried requests, make request1 the favorable
+  // pick if no prioritized requests. But request2 is prioritized so it should
+  // be picked.
+  request2.set_completed_attempt_count(kAttemptCount);
+
+  // Add test requests on the Queue.
+  QueueRequests(request1, request2);
+
+  task()->Run();
+  PumpLoop();
+
+  // Pump the loop again to give the async queue the opportunity to return
+  // results from the Get operation, and for the picker to call the "picked"
+  // callback.
+  PumpLoop();
+
+  EXPECT_EQ(kRequestId2, last_picked_->request_id());
+  EXPECT_FALSE(request_queue_not_picked_called_);
+  EXPECT_EQ(2UL, total_request_count_);
+  EXPECT_EQ(2UL, available_request_count_);
+  EXPECT_TRUE(task_complete_called_);
+  EXPECT_EQ(1UL, prioritized_requests_.size());
+}
+
+TEST_F(PickRequestTaskTest, ChooseFromTwoPrioritizedRequests) {
+  // Make two prioritized requests, the second one should be picked because
+  // higher priority requests are later on the list.
+  prioritized_requests_.push_back(kRequestId1);
+  prioritized_requests_.push_back(kRequestId2);
+  MakePickRequestTask();
+
+  // Making request 1 more attractive to be picked not considering the
+  // prioritizing issues with older creation time, fewer attempt count and it's
+  // earlier in the request queue.
+  base::Time creation_time = base::Time::Now();
+  base::Time older_creation_time =
+      creation_time - base::TimeDelta::FromMinutes(10);
+  SavePageRequest request1(kRequestId1, kUrl1, kClientId1, older_creation_time,
+                           kUserRequested);
+  SavePageRequest request2(kRequestId2, kUrl2, kClientId2, creation_time,
+                           kUserRequested);
+  request2.set_completed_attempt_count(kAttemptCount);
+
+  // Add test requests on the Queue.
+  QueueRequests(request1, request2);
+
+  task()->Run();
+  PumpLoop();
+
+  // Pump the loop again to give the async queue the opportunity to return
+  // results from the Get operation, and for the picker to call the "picked"
+  // callback.
+  PumpLoop();
+
+  EXPECT_EQ(kRequestId2, last_picked_->request_id());
+  EXPECT_FALSE(request_queue_not_picked_called_);
+  EXPECT_EQ(2UL, total_request_count_);
+  EXPECT_EQ(2UL, available_request_count_);
+  EXPECT_TRUE(task_complete_called_);
+  EXPECT_EQ(2UL, prioritized_requests_.size());
+}
 }  // namespace offline_pages
diff --git a/components/offline_pages/core/background/request_coordinator.cc b/components/offline_pages/core/background/request_coordinator.cc
index cc80a342..5370b1c 100644
--- a/components/offline_pages/core/background/request_coordinator.cc
+++ b/components/offline_pages/core/background/request_coordinator.cc
@@ -394,6 +394,15 @@
 void RequestCoordinator::PauseRequests(
     const std::vector<int64_t>& request_ids) {
   bool canceled = CancelActiveRequestIfItMatches(request_ids);
+
+  // Remove the paused requests from prioritized list.
+  for (int64_t id : request_ids) {
+    auto it = std::find(prioritized_requests_.begin(),
+                        prioritized_requests_.end(), id);
+    if (it != prioritized_requests_.end())
+      prioritized_requests_.erase(it);
+  }
+
   queue_->ChangeRequestsState(
       request_ids, SavePageRequest::RequestState::PAUSED,
       base::Bind(&RequestCoordinator::UpdateMultipleRequestsCallback,
@@ -413,6 +422,8 @@
 
 void RequestCoordinator::ResumeRequests(
     const std::vector<int64_t>& request_ids) {
+  prioritized_requests_.insert(prioritized_requests_.end(), request_ids.begin(),
+                               request_ids.end());
   queue_->ChangeRequestsState(
       request_ids, SavePageRequest::RequestState::AVAILABLE,
       base::Bind(&RequestCoordinator::UpdateMultipleRequestsCallback,
@@ -720,16 +731,17 @@
     return;
   }
 
-  // Ask request queue to make a new PickRequestTask object, then put it on the
-  // task queue.
+  // Ask request queue to make a new PickRequestTask object, then put it on
+  // the task queue.
   queue_->PickNextRequest(
-      policy_.get(), base::Bind(&RequestCoordinator::RequestPicked,
-                                weak_ptr_factory_.GetWeakPtr()),
+      policy_.get(),
+      base::Bind(&RequestCoordinator::RequestPicked,
+                 weak_ptr_factory_.GetWeakPtr()),
       base::Bind(&RequestCoordinator::RequestNotPicked,
                  weak_ptr_factory_.GetWeakPtr()),
       base::Bind(&RequestCoordinator::RequestCounts,
                  weak_ptr_factory_.GetWeakPtr(), is_start_of_processing),
-      *current_conditions_.get(), disabled_requests_);
+      *current_conditions_.get(), disabled_requests_, prioritized_requests_);
   // TODO(petewil): Verify current_conditions has a good value on all calling
   // paths.  It is really more of a "last known conditions" than "current
   // conditions".  Consider having a call to Java to check the current
diff --git a/components/offline_pages/core/background/request_coordinator.h b/components/offline_pages/core/background/request_coordinator.h
index d4b9100..30ac8eb2 100644
--- a/components/offline_pages/core/background/request_coordinator.h
+++ b/components/offline_pages/core/background/request_coordinator.h
@@ -461,6 +461,15 @@
   base::OneShotTimer watchdog_timer_;
   // Used for potential immediate processing when we get network connection.
   std::unique_ptr<ConnectionNotifier> connection_notifier_;
+  // Used to track prioritized requests.
+  // The requests can only be added by RC when they are resumed and there are
+  // two places where deletion from the |prioritized_requests_| would happen:
+  //   1. When request is paused RC will remove it.
+  //   2. When a task is not available to be picked by PickRequestTask (because
+  //   it was completed or cancelled), the task will remove it.
+  // Currently it's used as LIFO.
+  // TODO(romax): see if LIFO is a good idea, or change to FIFO.
+  std::deque<int64_t> prioritized_requests_;
   // Allows us to pass a weak pointer to callbacks.
   base::WeakPtrFactory<RequestCoordinator> weak_ptr_factory_;
 
diff --git a/components/offline_pages/core/background/request_coordinator_unittest.cc b/components/offline_pages/core/background/request_coordinator_unittest.cc
index 0850bef..0c5030cb5 100644
--- a/components/offline_pages/core/background/request_coordinator_unittest.cc
+++ b/components/offline_pages/core/background/request_coordinator_unittest.cc
@@ -310,6 +310,10 @@
     return coordinator()->disabled_requests_;
   }
 
+  const std::deque<int64_t>& prioritized_requests() {
+    return coordinator()->prioritized_requests_;
+  }
+
  private:
   GetRequestsResult last_get_requests_result_;
   MultipleItemStatuses last_remove_results_;
@@ -1457,10 +1461,12 @@
   coordinator()->ResumeRequests(request_ids);
   PumpLoop();
   EXPECT_FALSE(is_busy());
+  EXPECT_EQ(1UL, prioritized_requests().size());
 
   // Pause the request again.
   coordinator()->PauseRequests(request_ids);
   PumpLoop();
+  EXPECT_EQ(0UL, prioritized_requests().size());
 
   // Now simulate reasonable connection.
   SetNetworkConnected(true);
@@ -1469,6 +1475,7 @@
   coordinator()->ResumeRequests(request_ids);
   EXPECT_FALSE(is_busy());
   PumpLoop();
+  EXPECT_EQ(1UL, prioritized_requests().size());
 
   EXPECT_TRUE(is_busy());
 }
diff --git a/components/offline_pages/core/background/request_queue.cc b/components/offline_pages/core/background/request_queue.cc
index 406f0ae..bdf2054 100644
--- a/components/offline_pages/core/background/request_queue.cc
+++ b/components/offline_pages/core/background/request_queue.cc
@@ -128,11 +128,13 @@
     PickRequestTask::RequestNotPickedCallback not_picked_callback,
     PickRequestTask::RequestCountCallback request_count_callback,
     DeviceConditions& conditions,
-    std::set<int64_t>& disabled_requests) {
+    std::set<int64_t>& disabled_requests,
+    std::deque<int64_t>& prioritized_requests) {
   // Using the PickerContext, create a picker task.
-  std::unique_ptr<Task> task(new PickRequestTask(
-      store_.get(), policy, picked_callback, not_picked_callback,
-      request_count_callback, conditions, disabled_requests));
+  std::unique_ptr<Task> task(
+      new PickRequestTask(store_.get(), policy, picked_callback,
+                          not_picked_callback, request_count_callback,
+                          conditions, disabled_requests, prioritized_requests));
 
   // Queue up the picking task, it will call one of the callbacks when it
   // completes.
diff --git a/components/offline_pages/core/background/request_queue.h b/components/offline_pages/core/background/request_queue.h
index 8325dd0..d0c66a7 100644
--- a/components/offline_pages/core/background/request_queue.h
+++ b/components/offline_pages/core/background/request_queue.h
@@ -7,6 +7,7 @@
 
 #include <stdint.h>
 
+#include <deque>
 #include <memory>
 #include <set>
 #include <string>
@@ -95,7 +96,8 @@
       PickRequestTask::RequestNotPickedCallback not_picked_callback,
       PickRequestTask::RequestCountCallback request_count_callback,
       DeviceConditions& conditions,
-      std::set<int64_t>& disabled_requests);
+      std::set<int64_t>& disabled_requests,
+      std::deque<int64_t>& prioritized_requests);
 
   // Reconcile any requests that were active the last time chrome exited.
   void ReconcileRequests(const UpdateCallback& callback);
diff --git a/components/precache/content/precache_manager.cc b/components/precache/content/precache_manager.cc
index 7732807e..c28cb53 100644
--- a/components/precache/content/precache_manager.cc
+++ b/components/precache/content/precache_manager.cc
@@ -352,7 +352,6 @@
 void PrecacheManager::UpdatePrecacheMetricsAndState(
     const GURL& url,
     const GURL& referrer,
-    const base::TimeDelta& latency,
     const base::Time& fetch_time,
     const net::HttpResponseInfo& info,
     int64_t size,
@@ -365,8 +364,7 @@
       base::Bind(&PrecacheDatabase::GetLastPrecacheTimestamp,
                  base::Unretained(precache_database_.get())),
       base::Bind(&PrecacheManager::RecordStatsForFetch, AsWeakPtr(), url,
-                 referrer, latency, fetch_time, info, size,
-                 register_synthetic_trial));
+                 referrer, fetch_time, info, size, register_synthetic_trial));
 
   if (is_user_traffic && IsPrecaching())
     CancelPrecaching();
@@ -375,7 +373,6 @@
 void PrecacheManager::RecordStatsForFetch(
     const GURL& url,
     const GURL& referrer,
-    const base::TimeDelta& latency,
     const base::Time& fetch_time,
     const net::HttpResponseInfo& info,
     int64_t size,
@@ -396,13 +393,12 @@
   history_service_->HostRankIfAvailable(
       referrer,
       base::Bind(&PrecacheManager::RecordStatsForFetchInternal, AsWeakPtr(),
-                 url, referrer.host(), latency, fetch_time, info, size));
+                 url, referrer.host(), fetch_time, info, size));
 }
 
 void PrecacheManager::RecordStatsForFetchInternal(
     const GURL& url,
     const std::string& referrer_host,
-    const base::TimeDelta& latency,
     const base::Time& fetch_time,
     const net::HttpResponseInfo& info,
     int64_t size,
@@ -416,7 +412,7 @@
     BrowserThread::PostTask(
         BrowserThread::DB, FROM_HERE,
         base::Bind(&PrecacheDatabase::RecordURLPrefetchMetrics,
-                   base::Unretained(precache_database_.get()), info, latency));
+                   base::Unretained(precache_database_.get()), info));
   } else {
     bool is_connection_cellular =
         net::NetworkChangeNotifier::IsConnectionCellular(
@@ -425,8 +421,8 @@
     BrowserThread::PostTask(
         BrowserThread::DB, FROM_HERE,
         base::Bind(&PrecacheDatabase::RecordURLNonPrefetch,
-                   base::Unretained(precache_database_.get()), url, latency,
-                   fetch_time, info, size, host_rank, is_connection_cellular));
+                   base::Unretained(precache_database_.get()), url, fetch_time,
+                   info, size, host_rank, is_connection_cellular));
   }
 }
 
diff --git a/components/precache/content/precache_manager.h b/components/precache/content/precache_manager.h
index d4bff9e9..afd5e6c 100644
--- a/components/precache/content/precache_manager.h
+++ b/components/precache/content/precache_manager.h
@@ -29,7 +29,6 @@
 namespace base {
 class FilePath;
 class Time;
-class TimeDelta;
 }
 
 namespace content {
@@ -123,7 +122,6 @@
   void UpdatePrecacheMetricsAndState(
       const GURL& url,
       const GURL& referrer,
-      const base::TimeDelta& latency,
       const base::Time& fetch_time,
       const net::HttpResponseInfo& info,
       int64_t size,
@@ -190,7 +188,6 @@
   void RecordStatsForFetch(
       const GURL& url,
       const GURL& referrer,
-      const base::TimeDelta& latency,
       const base::Time& fetch_time,
       const net::HttpResponseInfo& info,
       int64_t size,
@@ -201,7 +198,6 @@
   // by RecordStatsForFetch() by way of an asynchronous HistoryService callback.
   void RecordStatsForFetchInternal(const GURL& url,
                                    const std::string& referrer_host,
-                                   const base::TimeDelta& latency,
                                    const base::Time& fetch_time,
                                    const net::HttpResponseInfo& info,
                                    int64_t size,
diff --git a/components/precache/content/precache_manager_unittest.cc b/components/precache/content/precache_manager_unittest.cc
index 8fdbe6b..67ef094 100644
--- a/components/precache/content/precache_manager_unittest.cc
+++ b/components/precache/content/precache_manager_unittest.cc
@@ -210,13 +210,12 @@
 
   void RecordStatsForFetch(const GURL& url,
                            const std::string& referrer_host,
-                           const base::TimeDelta& latency,
                            const base::Time& fetch_time,
                            const net::HttpResponseInfo& info,
                            int64_t size,
                            base::Time last_precache_time) {
     precache_manager_->RecordStatsForFetch(
-        url, GURL(referrer_host), latency, fetch_time, info, size,
+        url, GURL(referrer_host), fetch_time, info, size,
         base::Bind(&PrecacheManagerTest::RegisterSyntheticFieldTrial,
                    base::Unretained(this)),
         last_precache_time);
@@ -224,12 +223,11 @@
 
   void RecordStatsForPrecacheFetch(const GURL& url,
                                    const std::string& referrer_host,
-                                   const base::TimeDelta& latency,
                                    const base::Time& fetch_time,
                                    const net::HttpResponseInfo& info,
                                    int64_t size,
                                    base::Time last_precache_time) {
-    RecordStatsForFetch(url, referrer_host, latency, fetch_time, info, size,
+    RecordStatsForFetch(url, referrer_host, fetch_time, info, size,
                         last_precache_time);
     precache_database_->RecordURLPrefetch(url, referrer_host, fetch_time,
                                           info.was_cached, size);
@@ -477,28 +475,25 @@
 // PrecacheUtil::UpdatePrecacheMetricsAndState() for more test coverage.
 TEST_F(PrecacheManagerTest, RecordStatsForFetchWithSizeZero) {
   // Fetches with size 0 should be ignored.
-  RecordStatsForPrecacheFetch(GURL("http://url.com"), "", base::TimeDelta(),
-                              base::Time(), info_, 0, base::Time());
+  RecordStatsForPrecacheFetch(GURL("http://url.com"), "", base::Time(), info_,
+                              0, base::Time());
   base::RunLoop().RunUntilIdle();
-  histograms_.ExpectTotalCount("Precache.Latency.Prefetch", 0);
   histograms_.ExpectTotalCount("Precache.Freshness.Prefetch", 0);
 }
 
 TEST_F(PrecacheManagerTest, RecordStatsForFetchWithNonHTTP) {
   // Fetches for URLs with schemes other than HTTP or HTTPS should be ignored.
-  RecordStatsForPrecacheFetch(GURL("ftp://ftp.com"), "", base::TimeDelta(),
-                              base::Time(), info_, 1000, base::Time());
+  RecordStatsForPrecacheFetch(GURL("ftp://ftp.com"), "", base::Time(), info_,
+                              1000, base::Time());
   base::RunLoop().RunUntilIdle();
-  histograms_.ExpectTotalCount("Precache.Latency.Prefetch", 0);
   histograms_.ExpectTotalCount("Precache.Freshness.Prefetch", 0);
 }
 
 TEST_F(PrecacheManagerTest, RecordStatsForFetchWithEmptyURL) {
   // Fetches for empty URLs should be ignored.
-  RecordStatsForPrecacheFetch(GURL(), "", base::TimeDelta(), base::Time(),
-                              info_, 1000, base::Time());
+  RecordStatsForPrecacheFetch(GURL(), "", base::Time(), info_, 1000,
+                              base::Time());
   base::RunLoop().RunUntilIdle();
-  histograms_.ExpectTotalCount("Precache.Latency.Prefetch", 0);
   histograms_.ExpectTotalCount("Precache.Freshness.Prefetch", 0);
 }
 
@@ -510,8 +505,7 @@
 
   EXPECT_TRUE(precache_manager_->IsPrecaching());
   RecordStatsForPrecacheFetch(GURL("http://url.com"), std::string(),
-                              base::TimeDelta(), base::Time(), info_, 1000,
-                              base::Time());
+                              base::Time(), info_, 1000, base::Time());
   base::RunLoop().RunUntilIdle();
   precache_manager_->CancelPrecaching();
 
@@ -526,7 +520,6 @@
                            Pair("Precache.Fetch.ResponseBytes.Network", 1),
                            Pair("Precache.Fetch.ResponseBytes.Total", 1),
                            Pair("Precache.Fetch.TimeToComplete", 1),
-                           Pair("Precache.Latency.Prefetch", 1),
                            Pair("Precache.Freshness.Prefetch", 1)));
 }
 
@@ -541,36 +534,34 @@
 
   EXPECT_TRUE(precache_manager_->IsPrecaching());
   RecordStatsForPrecacheFetch(GURL("http://url.com"), std::string(),
-                              base::TimeDelta(), base::Time(), info_, 1000,
+                              base::Time(), info_, 1000,
                               now /* last_precache_time */);
   base::RunLoop().RunUntilIdle();
   precache_manager_->CancelPrecaching();
 }
 
 TEST_F(PrecacheManagerTest, RecordStatsForFetchHTTP) {
-  RecordStatsForFetch(GURL("http://http-url.com"), "", base::TimeDelta(),
-                      base::Time(), info_, 1000, base::Time());
+  RecordStatsForFetch(GURL("http://http-url.com"), "", base::Time(), info_,
+                      1000, base::Time());
   base::RunLoop().RunUntilIdle();
 
   EXPECT_THAT(histograms_.GetTotalCountsForPrefix("Precache."),
               UnorderedElementsAre(
                   Pair("Precache.DownloadedNonPrecache", 1),
                   Pair("Precache.CacheStatus.NonPrefetch", 1),
-                  Pair("Precache.Latency.NonPrefetch", 1),
-                  Pair("Precache.Latency.NonPrefetch.NonTopHosts", 1)));
+                  Pair("Precache.CacheStatus.NonPrefetch.NonTopHosts", 1)));
 }
 
 TEST_F(PrecacheManagerTest, RecordStatsForFetchHTTPS) {
-  RecordStatsForFetch(GURL("https://https-url.com"), "", base::TimeDelta(),
-                      base::Time(), info_, 1000, base::Time());
+  RecordStatsForFetch(GURL("https://https-url.com"), "", base::Time(), info_,
+                      1000, base::Time());
   base::RunLoop().RunUntilIdle();
 
   EXPECT_THAT(histograms_.GetTotalCountsForPrefix("Precache."),
               UnorderedElementsAre(
                   Pair("Precache.DownloadedNonPrecache", 1),
                   Pair("Precache.CacheStatus.NonPrefetch", 1),
-                  Pair("Precache.Latency.NonPrefetch", 1),
-                  Pair("Precache.Latency.NonPrefetch.NonTopHosts", 1)));
+                  Pair("Precache.CacheStatus.NonPrefetch.NonTopHosts", 1)));
 }
 
 TEST_F(PrecacheManagerTest, RecordStatsForFetchInTopHosts) {
@@ -581,16 +572,14 @@
             callback.Run(0);
           }));
   RecordStatsForFetch(GURL("http://http-url.com"), "http://referrer.com",
-                      base::TimeDelta(), base::Time(), info_, 1000,
-                      base::Time());
+                      base::Time(), info_, 1000, base::Time());
   base::RunLoop().RunUntilIdle();
 
-  EXPECT_THAT(
-      histograms_.GetTotalCountsForPrefix("Precache."),
-      UnorderedElementsAre(Pair("Precache.DownloadedNonPrecache", 1),
-                           Pair("Precache.CacheStatus.NonPrefetch", 1),
-                           Pair("Precache.Latency.NonPrefetch", 1),
-                           Pair("Precache.Latency.NonPrefetch.TopHosts", 1)));
+  EXPECT_THAT(histograms_.GetTotalCountsForPrefix("Precache."),
+              UnorderedElementsAre(
+                  Pair("Precache.DownloadedNonPrecache", 1),
+                  Pair("Precache.CacheStatus.NonPrefetch", 1),
+                  Pair("Precache.CacheStatus.NonPrefetch.TopHosts", 1)));
 }
 
 TEST_F(PrecacheManagerTest, DeleteExpiredPrecacheHistory) {
@@ -607,15 +596,15 @@
   EXPECT_TRUE(precache_manager_->IsPrecaching());
 
   // Precache a bunch of URLs, with different fetch times.
-  RecordStatsForPrecacheFetch(
-      GURL("http://old-fetch.com"), std::string(), base::TimeDelta(),
-      kCurrentTime - base::TimeDelta::FromDays(61), info_, 1000, base::Time());
-  RecordStatsForPrecacheFetch(
-      GURL("http://recent-fetch.com"), std::string(), base::TimeDelta(),
-      kCurrentTime - base::TimeDelta::FromDays(59), info_, 1000, base::Time());
-  RecordStatsForPrecacheFetch(
-      GURL("http://yesterday-fetch.com"), std::string(), base::TimeDelta(),
-      kCurrentTime - base::TimeDelta::FromDays(1), info_, 1000, base::Time());
+  RecordStatsForPrecacheFetch(GURL("http://old-fetch.com"), std::string(),
+                              kCurrentTime - base::TimeDelta::FromDays(61),
+                              info_, 1000, base::Time());
+  RecordStatsForPrecacheFetch(GURL("http://recent-fetch.com"), std::string(),
+                              kCurrentTime - base::TimeDelta::FromDays(59),
+                              info_, 1000, base::Time());
+  RecordStatsForPrecacheFetch(GURL("http://yesterday-fetch.com"), std::string(),
+                              kCurrentTime - base::TimeDelta::FromDays(1),
+                              info_, 1000, base::Time());
   expected_histogram_count_map["Precache.CacheStatus.Prefetch"] += 3;
   expected_histogram_count_map["Precache.CacheSize.AllEntries"]++;
   expected_histogram_count_map["Precache.DownloadedPrecacheMotivated"] += 3;
@@ -623,7 +612,6 @@
   expected_histogram_count_map["Precache.Fetch.ResponseBytes.Network"]++;
   expected_histogram_count_map["Precache.Fetch.ResponseBytes.Total"]++;
   expected_histogram_count_map["Precache.Fetch.TimeToComplete"]++;
-  expected_histogram_count_map["Precache.Latency.Prefetch"] += 3;
   expected_histogram_count_map["Precache.Freshness.Prefetch"] += 3;
   base::RunLoop().RunUntilIdle();
 
@@ -659,12 +647,12 @@
   // but it isn't reported as saved bytes because it had expired in the precache
   // history.
   info_.was_cached = true;  // From now on all fetches are cached.
-  RecordStatsForFetch(GURL("http://old-fetch.com"), "", base::TimeDelta(),
-                      kCurrentTime, info_, 1000, base::Time());
+  RecordStatsForFetch(GURL("http://old-fetch.com"), "", kCurrentTime, info_,
+                      1000, base::Time());
   expected_histogram_count_map["Precache.Fetch.TimeToComplete"]++;
   expected_histogram_count_map["Precache.CacheStatus.NonPrefetch"]++;
-  expected_histogram_count_map["Precache.Latency.NonPrefetch"]++;
-  expected_histogram_count_map["Precache.Latency.NonPrefetch.NonTopHosts"]++;
+  expected_histogram_count_map
+      ["Precache.CacheStatus.NonPrefetch.NonTopHosts"]++;
   expected_histogram_count_map["Precache.TimeSinceLastPrecache"] += 1;
 
   base::RunLoop().RunUntilIdle();
@@ -673,15 +661,15 @@
 
   // The other precaches should not have expired, so the following fetches from
   // the cache should count as saved bytes.
-  RecordStatsForFetch(GURL("http://recent-fetch.com"), "", base::TimeDelta(),
-                      kCurrentTime, info_, 1000, base::Time());
-  RecordStatsForFetch(GURL("http://yesterday-fetch.com"), "", base::TimeDelta(),
-                      kCurrentTime, info_, 1000, base::Time());
+  RecordStatsForFetch(GURL("http://recent-fetch.com"), "", kCurrentTime, info_,
+                      1000, base::Time());
+  RecordStatsForFetch(GURL("http://yesterday-fetch.com"), "", kCurrentTime,
+                      info_, 1000, base::Time());
   expected_histogram_count_map["Precache.CacheStatus.NonPrefetch"] += 2;
   expected_histogram_count_map
       ["Precache.CacheStatus.NonPrefetch.FromPrecache"] += 2;
-  expected_histogram_count_map["Precache.Latency.NonPrefetch"] += 2;
-  expected_histogram_count_map["Precache.Latency.NonPrefetch.NonTopHosts"] += 2;
+  expected_histogram_count_map
+      ["Precache.CacheStatus.NonPrefetch.NonTopHosts"] += 2;
   expected_histogram_count_map["Precache.Saved"] += 2;
   expected_histogram_count_map["Precache.TimeSinceLastPrecache"] += 2;
   expected_histogram_count_map["Precache.Saved.Freshness"] = 2;
diff --git a/components/precache/core/precache_database.cc b/components/precache/core/precache_database.cc
index c10c183d..f30d8cb 100644
--- a/components/precache/core/precache_database.cc
+++ b/components/precache/core/precache_database.cc
@@ -148,11 +148,9 @@
 }
 
 void PrecacheDatabase::RecordURLPrefetchMetrics(
-    const net::HttpResponseInfo& info,
-    const base::TimeDelta& latency) {
+    const net::HttpResponseInfo& info) {
   DCHECK(thread_checker_.CalledOnValidThread());
 
-  UMA_HISTOGRAM_TIMES("Precache.Latency.Prefetch", latency);
   UMA_HISTOGRAM_ENUMERATION("Precache.CacheStatus.Prefetch",
                             info.cache_entry_status,
                             net::HttpResponseInfo::CacheEntryStatus::ENTRY_MAX);
@@ -233,13 +231,11 @@
 }
 
 void PrecacheDatabase::RecordURLNonPrefetch(const GURL& url,
-                                            const base::TimeDelta& latency,
                                             const base::Time& fetch_time,
                                             const net::HttpResponseInfo& info,
                                             int64_t size,
                                             int host_rank,
                                             bool is_connection_cellular) {
-  UMA_HISTOGRAM_TIMES("Precache.Latency.NonPrefetch", latency);
   UMA_HISTOGRAM_ENUMERATION("Precache.CacheStatus.NonPrefetch",
                             info.cache_entry_status,
                             net::HttpResponseInfo::CacheEntryStatus::ENTRY_MAX);
@@ -247,11 +243,15 @@
   if (host_rank != history::kMaxTopHosts) {
     // The resource was loaded on a page that could have been affected by
     // precaching.
-    UMA_HISTOGRAM_TIMES("Precache.Latency.NonPrefetch.TopHosts", latency);
+    UMA_HISTOGRAM_ENUMERATION(
+        "Precache.CacheStatus.NonPrefetch.TopHosts", info.cache_entry_status,
+        net::HttpResponseInfo::CacheEntryStatus::ENTRY_MAX);
   } else {
     // The resource was loaded on a page that could NOT have been affected by
     // precaching.
-    UMA_HISTOGRAM_TIMES("Precache.Latency.NonPrefetch.NonTopHosts", latency);
+    UMA_HISTOGRAM_ENUMERATION(
+        "Precache.CacheStatus.NonPrefetch.NonTopHosts", info.cache_entry_status,
+        net::HttpResponseInfo::CacheEntryStatus::ENTRY_MAX);
   }
 
   if (!IsDatabaseAccessible()) {
diff --git a/components/precache/core/precache_database.h b/components/precache/core/precache_database.h
index 984a375..0597516 100644
--- a/components/precache/core/precache_database.h
+++ b/components/precache/core/precache_database.h
@@ -69,8 +69,7 @@
   // Report precache-related metrics in response to a URL being fetched, where
   // the fetch was motivated by precaching. This is called from the network
   // delegate, via precache_util.
-  void RecordURLPrefetchMetrics(const net::HttpResponseInfo& info,
-                                const base::TimeDelta& latency);
+  void RecordURLPrefetchMetrics(const net::HttpResponseInfo& info);
 
   // Records the precache of an url |url| for top host |referrer_host|. This is
   // called from PrecacheFetcher.
@@ -85,7 +84,6 @@
   // indicates whether the current network connection is a cellular network.
   // This is called from the network delegate, via precache_util.
   void RecordURLNonPrefetch(const GURL& url,
-                            const base::TimeDelta& latency,
                             const base::Time& fetch_time,
                             const net::HttpResponseInfo& info,
                             int64_t size,
diff --git a/components/precache/core/precache_database_unittest.cc b/components/precache/core/precache_database_unittest.cc
index 98564d3..233c2d0 100644
--- a/components/precache/core/precache_database_unittest.cc
+++ b/components/precache/core/precache_database_unittest.cc
@@ -35,7 +35,6 @@
 
 const GURL kURL("http://url.com");
 const int kReferrerID = 1;
-const base::TimeDelta kLatency = base::TimeDelta::FromMilliseconds(5);
 const base::Time kFetchTime = base::Time() + base::TimeDelta::FromHours(1000);
 const base::Time kOldFetchTime = kFetchTime - base::TimeDelta::FromDays(1);
 const base::Time kNewFetchTime =
@@ -123,23 +122,19 @@
   // Convenience methods for recording different types of URL fetches. These
   // exist to improve the readability of the tests.
   void RecordPrecacheFromNetwork(const GURL& url,
-                                 base::TimeDelta latency,
                                  const base::Time& fetch_time,
                                  int64_t size);
   void RecordPrecacheFromCache(const GURL& url,
                                const base::Time& fetch_time,
                                int64_t size);
   void RecordFetchFromNetwork(const GURL& url,
-                              base::TimeDelta latency,
                               const base::Time& fetch_time,
                               int64_t size);
   void RecordFetchFromNetwork(const GURL& url,
-                              base::TimeDelta latency,
                               const base::Time& fetch_time,
                               int64_t size,
                               int host_rank);
   void RecordFetchFromNetworkCellular(const GURL& url,
-                                      base::TimeDelta latency,
                                       const base::Time& fetch_time,
                                       int64_t size);
   void RecordFetchFromCache(const GURL& url,
@@ -174,12 +169,11 @@
 
 void PrecacheDatabaseTest::RecordPrecacheFromNetwork(
     const GURL& url,
-    base::TimeDelta latency,
     const base::Time& fetch_time,
     int64_t size) {
   const HttpResponseInfo info = CreateHttpResponseInfo(
       false /* was_cached */, false /* network_accessed */);
-  precache_database_->RecordURLPrefetchMetrics(info, latency);
+  precache_database_->RecordURLPrefetchMetrics(info);
   precache_database_->RecordURLPrefetch(url, std::string(), fetch_time,
                                         info.was_cached, size);
 }
@@ -189,43 +183,39 @@
                                                    int64_t size) {
   const HttpResponseInfo info = CreateHttpResponseInfo(
       true /* was_cached */, false /* network_accessed */);
-  precache_database_->RecordURLPrefetchMetrics(info,
-                                               base::TimeDelta() /* latency */);
+  precache_database_->RecordURLPrefetchMetrics(info);
   precache_database_->RecordURLPrefetch(url, std::string(), fetch_time,
                                         info.was_cached, size);
 }
 
 void PrecacheDatabaseTest::RecordFetchFromNetwork(const GURL& url,
-                                                  base::TimeDelta latency,
                                                   const base::Time& fetch_time,
                                                   int64_t size) {
   const HttpResponseInfo info = CreateHttpResponseInfo(
       false /* was_cached */, false /* network_accessed */);
-  precache_database_->RecordURLNonPrefetch(url, latency, fetch_time, info, size,
+  precache_database_->RecordURLNonPrefetch(url, fetch_time, info, size,
                                            history::kMaxTopHosts,
                                            false /* is_connection_cellular */);
 }
 
 void PrecacheDatabaseTest::RecordFetchFromNetwork(const GURL& url,
-                                                  base::TimeDelta latency,
                                                   const base::Time& fetch_time,
                                                   int64_t size,
                                                   int host_rank) {
   const HttpResponseInfo info = CreateHttpResponseInfo(
       false /* was_cached */, false /* network_accessed */);
-  precache_database_->RecordURLNonPrefetch(url, latency, fetch_time, info, size,
+  precache_database_->RecordURLNonPrefetch(url, fetch_time, info, size,
                                            host_rank,
                                            false /* is_connection_cellular */);
 }
 
 void PrecacheDatabaseTest::RecordFetchFromNetworkCellular(
     const GURL& url,
-    base::TimeDelta latency,
     const base::Time& fetch_time,
     int64_t size) {
   const HttpResponseInfo info = CreateHttpResponseInfo(
       false /* was_cached */, false /* network_accessed */);
-  precache_database_->RecordURLNonPrefetch(url, latency, fetch_time, info, size,
+  precache_database_->RecordURLNonPrefetch(url, fetch_time, info, size,
                                            history::kMaxTopHosts,
                                            true /* is_connection_cellular */);
 }
@@ -235,9 +225,9 @@
                                                 int64_t size) {
   const HttpResponseInfo info = CreateHttpResponseInfo(
       true /* was_cached */, false /* network_accessed */);
-  precache_database_->RecordURLNonPrefetch(
-      url, base::TimeDelta() /* latency */, fetch_time, info, size,
-      history::kMaxTopHosts, false /* is_connection_cellular */);
+  precache_database_->RecordURLNonPrefetch(url, fetch_time, info, size,
+                                           history::kMaxTopHosts,
+                                           false /* is_connection_cellular */);
 }
 
 void PrecacheDatabaseTest::RecordFetchFromCacheCellular(
@@ -246,20 +236,19 @@
     int64_t size) {
   const HttpResponseInfo info = CreateHttpResponseInfo(
       true /* was_cached */, false /* network_accessed */);
-  precache_database_->RecordURLNonPrefetch(
-      url, base::TimeDelta() /* latency */, fetch_time, info, size,
-      history::kMaxTopHosts, true /* is_connection_cellular */);
+  precache_database_->RecordURLNonPrefetch(url, fetch_time, info, size,
+                                           history::kMaxTopHosts,
+                                           true /* is_connection_cellular */);
 }
 
 namespace {
 
 TEST_F(PrecacheDatabaseTest, PrecacheOverNetwork) {
-  RecordPrecacheFromNetwork(kURL, kLatency, kFetchTime, kSize);
+  RecordPrecacheFromNetwork(kURL, kFetchTime, kSize);
 
   EXPECT_EQ(BuildURLTableMap(kURL, kFetchTime), GetActualURLTableMap());
 
   ExpectNewSample("Precache.DownloadedPrecacheMotivated", kSize);
-  ExpectNewSample("Precache.Latency.Prefetch", kLatency.InMilliseconds());
   ExpectNewSample("Precache.CacheStatus.Prefetch", kFromNetwork);
   ExpectNewSample("Precache.Freshness.Prefetch", kFreshnessBucket10K);
   ExpectNoOtherSamples();
@@ -273,7 +262,6 @@
   // timestamp.
   EXPECT_EQ(BuildURLTableMap(kURL, kFetchTime), GetActualURLTableMap());
 
-  ExpectNewSample("Precache.Latency.Prefetch", 0);
   ExpectNewSample("Precache.CacheStatus.Prefetch",
                   net::HttpResponseInfo::ENTRY_USED);
   ExpectNewSample("Precache.Freshness.Prefetch", kFreshnessBucket10K);
@@ -285,7 +273,6 @@
 
   EXPECT_TRUE(GetActualURLTableMap().empty());
 
-  ExpectNewSample("Precache.Latency.Prefetch", 0);
   ExpectNewSample("Precache.CacheStatus.Prefetch",
                   net::HttpResponseInfo::ENTRY_USED);
   ExpectNewSample("Precache.Freshness.Prefetch", kFreshnessBucket10K);
@@ -293,59 +280,51 @@
 }
 
 TEST_F(PrecacheDatabaseTest, FetchOverNetwork_NonCellular) {
-  RecordFetchFromNetwork(kURL, kLatency, kFetchTime, kSize);
+  RecordFetchFromNetwork(kURL, kFetchTime, kSize);
 
   EXPECT_TRUE(GetActualURLTableMap().empty());
 
   ExpectNewSample("Precache.DownloadedNonPrecache", kSize);
   ExpectNewSample("Precache.CacheStatus.NonPrefetch", kFromNetwork);
-  ExpectNewSample("Precache.Latency.NonPrefetch", kLatency.InMilliseconds());
-  ExpectNewSample("Precache.Latency.NonPrefetch.NonTopHosts",
-                  kLatency.InMilliseconds());
+  ExpectNewSample("Precache.CacheStatus.NonPrefetch.NonTopHosts", kFromNetwork);
   ExpectNoOtherSamples();
 }
 
 TEST_F(PrecacheDatabaseTest, FetchOverNetwork_NonCellular_TopHosts) {
-  RecordFetchFromNetwork(kURL, kLatency, kFetchTime, kSize, 0 /* host_rank */);
+  RecordFetchFromNetwork(kURL, kFetchTime, kSize, 0 /* host_rank */);
 
   EXPECT_TRUE(GetActualURLTableMap().empty());
 
   ExpectNewSample("Precache.DownloadedNonPrecache", kSize);
   ExpectNewSample("Precache.CacheStatus.NonPrefetch", kFromNetwork);
-  ExpectNewSample("Precache.Latency.NonPrefetch", kLatency.InMilliseconds());
-  ExpectNewSample("Precache.Latency.NonPrefetch.TopHosts",
-                  kLatency.InMilliseconds());
+  ExpectNewSample("Precache.CacheStatus.NonPrefetch.TopHosts", kFromNetwork);
   ExpectNoOtherSamples();
 }
 
 TEST_F(PrecacheDatabaseTest, FetchOverNetwork_Cellular) {
-  RecordFetchFromNetworkCellular(kURL, kLatency, kFetchTime, kSize);
+  RecordFetchFromNetworkCellular(kURL, kFetchTime, kSize);
 
   EXPECT_TRUE(GetActualURLTableMap().empty());
 
   ExpectNewSample("Precache.DownloadedNonPrecache", kSize);
   ExpectNewSample("Precache.DownloadedNonPrecache.Cellular", kSize);
   ExpectNewSample("Precache.CacheStatus.NonPrefetch", kFromNetwork);
-  ExpectNewSample("Precache.Latency.NonPrefetch", kLatency.InMilliseconds());
-  ExpectNewSample("Precache.Latency.NonPrefetch.NonTopHosts",
-                  kLatency.InMilliseconds());
+  ExpectNewSample("Precache.CacheStatus.NonPrefetch.NonTopHosts", kFromNetwork);
   ExpectNoOtherSamples();
 }
 
 TEST_F(PrecacheDatabaseTest, FetchOverNetworkWithURLTableEntry) {
   precache_url_table()->AddURL(kURL, kReferrerID, true, kOldFetchTime, false);
-  RecordFetchFromNetwork(kURL, kLatency, kFetchTime, kSize);
+  RecordFetchFromNetwork(kURL, kFetchTime, kSize);
 
   // The URL table entry should have been deleted.
   EXPECT_TRUE(GetActualURLTableMap().empty());
 
   ExpectNewSample("Precache.DownloadedNonPrecache", kSize);
-  ExpectNewSample("Precache.Latency.NonPrefetch", kLatency.InMilliseconds());
-  ExpectNewSample("Precache.Latency.NonPrefetch.NonTopHosts",
-                  kLatency.InMilliseconds());
   ExpectNewSample("Precache.CacheStatus.NonPrefetch", kFromNetwork);
   ExpectNewSample("Precache.CacheStatus.NonPrefetch.FromPrecache",
                   kFromNetwork);
+  ExpectNewSample("Precache.CacheStatus.NonPrefetch.NonTopHosts", kFromNetwork);
   ExpectNoOtherSamples();
 }
 
@@ -356,12 +335,12 @@
   // The URL table entry should have been deleted.
   EXPECT_TRUE(GetActualURLTableMap().empty());
 
-  ExpectNewSample("Precache.Latency.NonPrefetch", 0);
-  ExpectNewSample("Precache.Latency.NonPrefetch.NonTopHosts", 0);
   ExpectNewSample("Precache.CacheStatus.NonPrefetch",
                   HttpResponseInfo::CacheEntryStatus::ENTRY_USED);
   ExpectNewSample("Precache.CacheStatus.NonPrefetch.FromPrecache",
                   HttpResponseInfo::CacheEntryStatus::ENTRY_USED);
+  ExpectNewSample("Precache.CacheStatus.NonPrefetch.NonTopHosts",
+                  HttpResponseInfo::CacheEntryStatus::ENTRY_USED);
   ExpectNewSample("Precache.Saved", kSize);
   ExpectNewSample("Precache.Saved.Freshness", kFreshnessBucket10K);
   ExpectNoOtherSamples();
@@ -374,12 +353,12 @@
   // The URL table entry should have been deleted.
   EXPECT_TRUE(GetActualURLTableMap().empty());
 
-  ExpectNewSample("Precache.Latency.NonPrefetch", 0);
-  ExpectNewSample("Precache.Latency.NonPrefetch.NonTopHosts", 0);
   ExpectNewSample("Precache.CacheStatus.NonPrefetch",
                   HttpResponseInfo::CacheEntryStatus::ENTRY_USED);
   ExpectNewSample("Precache.CacheStatus.NonPrefetch.FromPrecache",
                   HttpResponseInfo::CacheEntryStatus::ENTRY_USED);
+  ExpectNewSample("Precache.CacheStatus.NonPrefetch.NonTopHosts",
+                  HttpResponseInfo::CacheEntryStatus::ENTRY_USED);
   ExpectNewSample("Precache.Saved", kSize);
   ExpectNewSample("Precache.Saved.Cellular", kSize);
   ExpectNewSample("Precache.Saved.Freshness", kFreshnessBucket10K);
@@ -391,10 +370,10 @@
 
   EXPECT_TRUE(GetActualURLTableMap().empty());
 
-  ExpectNewSample("Precache.Latency.NonPrefetch", 0);
-  ExpectNewSample("Precache.Latency.NonPrefetch.NonTopHosts", 0);
   ExpectNewSample("Precache.CacheStatus.NonPrefetch",
                   HttpResponseInfo::CacheEntryStatus::ENTRY_USED);
+  ExpectNewSample("Precache.CacheStatus.NonPrefetch.NonTopHosts",
+                  HttpResponseInfo::CacheEntryStatus::ENTRY_USED);
   ExpectNoOtherSamples();
 }
 
@@ -426,24 +405,24 @@
   const GURL kURL5("http://url5.com");
   const int64_t kSize5 = 5;
 
-  RecordPrecacheFromNetwork(kURL1, kLatency, kFetchTime, kSize1);
-  RecordPrecacheFromNetwork(kURL2, kLatency, kFetchTime, kSize2);
-  RecordPrecacheFromNetwork(kURL3, kLatency, kFetchTime, kSize3);
-  RecordPrecacheFromNetwork(kURL4, kLatency, kFetchTime, kSize4);
+  RecordPrecacheFromNetwork(kURL1, kFetchTime, kSize1);
+  RecordPrecacheFromNetwork(kURL2, kFetchTime, kSize2);
+  RecordPrecacheFromNetwork(kURL3, kFetchTime, kSize3);
+  RecordPrecacheFromNetwork(kURL4, kFetchTime, kSize4);
 
   RecordFetchFromCacheCellular(kURL1, kFetchTime, kSize1);
   RecordFetchFromCacheCellular(kURL1, kFetchTime, kSize1);
-  RecordFetchFromNetworkCellular(kURL2, kLatency, kFetchTime, kSize2);
-  RecordFetchFromNetworkCellular(kURL5, kLatency, kFetchTime, kSize5);
+  RecordFetchFromNetworkCellular(kURL2, kFetchTime, kSize2);
+  RecordFetchFromNetworkCellular(kURL5, kFetchTime, kSize5);
   RecordFetchFromCacheCellular(kURL5, kFetchTime, kSize5);
 
   RecordPrecacheFromCache(kURL1, kFetchTime, kSize1);
-  RecordPrecacheFromNetwork(kURL2, kLatency, kFetchTime, kSize2);
+  RecordPrecacheFromNetwork(kURL2, kFetchTime, kSize2);
   RecordPrecacheFromCache(kURL3, kFetchTime, kSize3);
   RecordPrecacheFromCache(kURL4, kFetchTime, kSize4);
 
   RecordFetchFromCache(kURL1, kFetchTime, kSize1);
-  RecordFetchFromNetwork(kURL2, kLatency, kFetchTime, kSize2);
+  RecordFetchFromNetwork(kURL2, kFetchTime, kSize2);
   RecordFetchFromCache(kURL3, kFetchTime, kSize3);
   RecordFetchFromCache(kURL5, kFetchTime, kSize5);
 
@@ -458,11 +437,17 @@
       histograms_.GetAllSamples("Precache.DownloadedNonPrecache.Cellular"),
       ElementsAre(Bucket(kSize2, 1), Bucket(kSize5, 1)));
 
-  EXPECT_THAT(histograms_.GetAllSamples("Precache.Latency.Prefetch"),
-              ElementsAre(Bucket(0, 3), Bucket(kLatency.InMilliseconds(), 5)));
+  EXPECT_THAT(
+      histograms_.GetAllSamples("Precache.CacheStatus.Prefetch"),
+      ElementsAre(
+          Bucket(HttpResponseInfo::CacheEntryStatus::ENTRY_USED, 3),
+          Bucket(HttpResponseInfo::CacheEntryStatus::ENTRY_UPDATED, 5)));
 
-  EXPECT_THAT(histograms_.GetAllSamples("Precache.Latency.NonPrefetch"),
-              ElementsAre(Bucket(0, 6), Bucket(kLatency.InMilliseconds(), 3)));
+  EXPECT_THAT(
+      histograms_.GetAllSamples("Precache.CacheStatus.NonPrefetch"),
+      ElementsAre(
+          Bucket(HttpResponseInfo::CacheEntryStatus::ENTRY_USED, 6),
+          Bucket(HttpResponseInfo::CacheEntryStatus::ENTRY_UPDATED, 3)));
 
   EXPECT_THAT(histograms_.GetAllSamples("Precache.Saved"),
               ElementsAre(Bucket(kSize1, 1), Bucket(kSize3, 1)));
@@ -477,10 +462,10 @@
       base::Time() + base::TimeDelta::FromSeconds(100);
   precache_database_->SetLastPrecacheTimestamp(kStartTime);
 
-  RecordPrecacheFromNetwork(kURL, kLatency, kStartTime, kSize);
-  RecordPrecacheFromNetwork(kURL, kLatency, kStartTime, kSize);
-  RecordPrecacheFromNetwork(kURL, kLatency, kStartTime, kSize);
-  RecordPrecacheFromNetwork(kURL, kLatency, kStartTime, kSize);
+  RecordPrecacheFromNetwork(kURL, kStartTime, kSize);
+  RecordPrecacheFromNetwork(kURL, kStartTime, kSize);
+  RecordPrecacheFromNetwork(kURL, kStartTime, kSize);
+  RecordPrecacheFromNetwork(kURL, kStartTime, kSize);
 
   EXPECT_THAT(histograms_.GetAllSamples("Precache.TimeSinceLastPrecache"),
               ElementsAre());
@@ -491,8 +476,8 @@
 
   RecordFetchFromCacheCellular(kURL, kTimeA, kSize);
   RecordFetchFromCacheCellular(kURL, kTimeA, kSize);
-  RecordFetchFromNetworkCellular(kURL, kLatency, kTimeB, kSize);
-  RecordFetchFromNetworkCellular(kURL, kLatency, kTimeB, kSize);
+  RecordFetchFromNetworkCellular(kURL, kTimeB, kSize);
+  RecordFetchFromNetworkCellular(kURL, kTimeB, kSize);
   RecordFetchFromCacheCellular(kURL, kTimeB, kSize);
   RecordFetchFromCacheCellular(kURL, kTimeC, kSize);
 
@@ -503,7 +488,7 @@
 TEST_F(PrecacheDatabaseTest, PrecacheFreshnessPrefetch) {
   auto info = CreateHttpResponseInfo(false /* was_cached */,
                                      false /* network_accessed */);
-  RecordPrecacheFromNetwork(kURL, kLatency, kFetchTime, kSize);
+  RecordPrecacheFromNetwork(kURL, kFetchTime, kSize);
 
   EXPECT_THAT(histograms_.GetAllSamples("Precache.Freshness.Prefetch"),
               ElementsAre(Bucket(kFreshnessBucket10K, 1)));
@@ -599,10 +584,9 @@
       } else if (!resource.is_network_fetched && resource.is_cellular_fetched) {
         RecordFetchFromCacheCellular(GURL(resource.url), kFetchTime, kSize);
       } else if (resource.is_network_fetched && !resource.is_cellular_fetched) {
-        RecordFetchFromNetwork(GURL(resource.url), kLatency, kFetchTime, kSize);
+        RecordFetchFromNetwork(GURL(resource.url), kFetchTime, kSize);
       } else if (resource.is_network_fetched && resource.is_cellular_fetched) {
-        RecordFetchFromNetworkCellular(GURL(resource.url), kLatency, kFetchTime,
-                                       kSize);
+        RecordFetchFromNetworkCellular(GURL(resource.url), kFetchTime, kSize);
       }
     }
   }
diff --git a/components/precache/core/precache_fetcher_unittest.cc b/components/precache/core/precache_fetcher_unittest.cc
index 51ea0c17..4dda6bf 100644
--- a/components/precache/core/precache_fetcher_unittest.cc
+++ b/components/precache/core/precache_fetcher_unittest.cc
@@ -440,9 +440,9 @@
     net::HttpResponseInfo info;
     info.was_cached = true;
     info.headers = new net::HttpResponseHeaders(std::string());
-    precache_database_.RecordURLNonPrefetch(
-        url, base::TimeDelta(), base::Time::Now(), info, 1000 /* size */,
-        0 /* host_rank */, false /* is_connection_cellular */);
+    precache_database_.RecordURLNonPrefetch(url, base::Time::Now(), info,
+                                            1000 /* size */, 0 /* host_rank */,
+                                            false /* is_connection_cellular */);
   }
 
  protected:
diff --git a/components/prefs/pref_value_store.h b/components/prefs/pref_value_store.h
index 39b6bf1..f6c9c37 100644
--- a/components/prefs/pref_value_store.h
+++ b/components/prefs/pref_value_store.h
@@ -30,6 +30,30 @@
  public:
   typedef base::Callback<void(const std::string&)> PrefChangedCallback;
 
+  // PrefStores must be listed here in order from highest to lowest priority.
+  //   MANAGED contains all managed preference values that are provided by
+  //      mandatory policies (e.g. Windows Group Policy or cloud policy).
+  //   SUPERVISED_USER contains preferences that are valid for supervised users.
+  //   EXTENSION contains preference values set by extensions.
+  //   COMMAND_LINE contains preference values set by command-line switches.
+  //   USER contains all user-set preference values.
+  //   RECOMMENDED contains all preferences that are provided by recommended
+  //      policies.
+  //   DEFAULT contains all application default preference values.
+  enum PrefStoreType {
+    // INVALID_STORE is not associated with an actual PrefStore but used as
+    // an invalid marker, e.g. as a return value.
+    INVALID_STORE = -1,
+    MANAGED_STORE = 0,
+    SUPERVISED_USER_STORE,
+    EXTENSION_STORE,
+    COMMAND_LINE_STORE,
+    USER_STORE,
+    RECOMMENDED_STORE,
+    DEFAULT_STORE,
+    PREF_STORE_TYPE_MAX = DEFAULT_STORE
+  };
+
   // In decreasing order of precedence:
   //   |managed_prefs| contains all preferences from mandatory policies.
   //   |supervised_user_prefs| contains all preferences from supervised user
@@ -118,30 +142,6 @@
   void UpdateCommandLinePrefStore(PrefStore* command_line_prefs);
 
  private:
-  // PrefStores must be listed here in order from highest to lowest priority.
-  //   MANAGED contains all managed preference values that are provided by
-  //      mandatory policies (e.g. Windows Group Policy or cloud policy).
-  //   SUPERVISED_USER contains preferences that are valid for supervised users.
-  //   EXTENSION contains preference values set by extensions.
-  //   COMMAND_LINE contains preference values set by command-line switches.
-  //   USER contains all user-set preference values.
-  //   RECOMMENDED contains all preferences that are provided by recommended
-  //      policies.
-  //   DEFAULT contains all application default preference values.
-  enum PrefStoreType {
-    // INVALID_STORE is not associated with an actual PrefStore but used as
-    // an invalid marker, e.g. as a return value.
-    INVALID_STORE = -1,
-    MANAGED_STORE = 0,
-    SUPERVISED_USER_STORE,
-    EXTENSION_STORE,
-    COMMAND_LINE_STORE,
-    USER_STORE,
-    RECOMMENDED_STORE,
-    DEFAULT_STORE,
-    PREF_STORE_TYPE_MAX = DEFAULT_STORE
-  };
-
   // Keeps a PrefStore reference on behalf of the PrefValueStore and monitors
   // the PrefStore for changes, forwarding notifications to PrefValueStore. This
   // indirection is here for the sake of disambiguating notifications from the
@@ -257,4 +257,16 @@
   DISALLOW_COPY_AND_ASSIGN(PrefValueStore);
 };
 
+namespace std {
+
+template <>
+struct hash<PrefValueStore::PrefStoreType> {
+  size_t operator()(PrefValueStore::PrefStoreType type) const {
+    return std::hash<
+        base::underlying_type<PrefValueStore::PrefStoreType>::type>()(type);
+  }
+};
+
+}  // namespace std
+
 #endif  // COMPONENTS_PREFS_PREF_VALUE_STORE_H_
diff --git a/components/printing/common/print_messages.h b/components/printing/common/print_messages.h
index 9a90bc11..14c8d64 100644
--- a/components/printing/common/print_messages.h
+++ b/components/printing/common/print_messages.h
@@ -130,9 +130,6 @@
   // Desired apparent dpi on paper.
   IPC_STRUCT_TRAITS_MEMBER(desired_dpi)
 
-  // Whether to rasterize a PDF for printing
-  IPC_STRUCT_TRAITS_MEMBER(rasterize_pdf)
-
   // Cookie for the document to ensure correctness.
   IPC_STRUCT_TRAITS_MEMBER(document_cookie)
 
@@ -168,6 +165,9 @@
   // URL string to be printed as footer if requested by the user.
   IPC_STRUCT_TRAITS_MEMBER(url)
 
+  // Whether to rasterize a PDF for printing
+  IPC_STRUCT_TRAITS_MEMBER(rasterize_pdf)
+
   // True if print backgrounds is requested by the user.
   IPC_STRUCT_TRAITS_MEMBER(should_print_backgrounds)
 IPC_STRUCT_TRAITS_END()
diff --git a/components/safe_browsing/BUILD.gn b/components/safe_browsing/BUILD.gn
index ca81666..ff80235e 100644
--- a/components/safe_browsing/BUILD.gn
+++ b/components/safe_browsing/BUILD.gn
@@ -2,6 +2,14 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
+import("//third_party/protobuf/proto_library.gni")
+
+proto_library("csd_proto") {
+  sources = [
+    "csd.proto",
+  ]
+}
+
 source_set("safe_browsing") {
   sources = [
     "base_blocking_page.cc",
diff --git a/components/safe_browsing/DEPS b/components/safe_browsing/DEPS
index ac3d8bb7..36b6e0f8 100644
--- a/components/safe_browsing/DEPS
+++ b/components/safe_browsing/DEPS
@@ -11,4 +11,5 @@
   "+net/log",
   "+net/url_request",
   "+testing/gtest",
+  "+third_party/protobuf",
 ]
diff --git a/chrome/common/safe_browsing/csd.proto b/components/safe_browsing/csd.proto
similarity index 99%
rename from chrome/common/safe_browsing/csd.proto
rename to components/safe_browsing/csd.proto
index 92b07a2..3da6cd68 100644
--- a/chrome/common/safe_browsing/csd.proto
+++ b/components/safe_browsing/csd.proto
@@ -36,7 +36,6 @@
   optional UserPopulation user_population = 1;
 }
 
-
 message ClientPhishingRequest {
   // URL that the client visited.  The CGI parameters are stripped by the
   // client.
@@ -471,7 +470,7 @@
   // True if the .zip or DMG, etc, was 100% successfully unpacked.
   optional bool archive_valid = 26;
 
-    // True if this ClientDownloadRequest is from a whitelisted domain.
+  // True if this ClientDownloadRequest is from a whitelisted domain.
   optional bool skipped_url_whitelist = 28;
 
   // True if this ClientDownloadRequest contains a whitelisted certificate.
@@ -578,9 +577,7 @@
 message ClientDownloadReport {
   // The information of user who provided the feedback.
   // This is going to be useful for handling appeals.
-  message UserInformation {
-    optional string email = 1;
-  }
+  message UserInformation { optional string email = 1; }
 
   enum Reason {
     SHARE = 0;
diff --git a/components/tracing/BUILD.gn b/components/tracing/BUILD.gn
index 6608413..deab4a6 100644
--- a/components/tracing/BUILD.gn
+++ b/components/tracing/BUILD.gn
@@ -7,6 +7,8 @@
 
 component("tracing") {
   sources = [
+    "child/child_memory_dump_manager_delegate_impl.cc",
+    "child/child_memory_dump_manager_delegate_impl.h",
     "child/child_trace_message_filter.cc",
     "child/child_trace_message_filter.h",
     "common/graphics_memory_dump_provider_android.cc",
diff --git a/components/tracing/child/child_memory_dump_manager_delegate_impl.cc b/components/tracing/child/child_memory_dump_manager_delegate_impl.cc
new file mode 100644
index 0000000..f0c4517b
--- /dev/null
+++ b/components/tracing/child/child_memory_dump_manager_delegate_impl.cc
@@ -0,0 +1,114 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/tracing/child/child_memory_dump_manager_delegate_impl.h"
+
+#include "base/single_thread_task_runner.h"
+#include "build/build_config.h"
+#include "components/tracing/child/child_trace_message_filter.h"
+#include "components/tracing/common/process_metrics_memory_dump_provider.h"
+
+namespace tracing {
+
+namespace {
+void AbortDumpRequest(const base::trace_event::MemoryDumpRequestArgs& args,
+                      const base::trace_event::MemoryDumpCallback& callback) {
+  if (!callback.is_null())
+    callback.Run(args.dump_guid, false /* success */);
+}
+}  // namespace
+
+// static
+ChildMemoryDumpManagerDelegateImpl*
+ChildMemoryDumpManagerDelegateImpl::GetInstance() {
+  return base::Singleton<
+      ChildMemoryDumpManagerDelegateImpl,
+      base::LeakySingletonTraits<ChildMemoryDumpManagerDelegateImpl>>::get();
+}
+
+ChildMemoryDumpManagerDelegateImpl::ChildMemoryDumpManagerDelegateImpl()
+    : ctmf_(nullptr),
+      tracing_process_id_(
+          base::trace_event::MemoryDumpManager::kInvalidTracingProcessId) {
+}
+
+ChildMemoryDumpManagerDelegateImpl::~ChildMemoryDumpManagerDelegateImpl() {}
+
+void ChildMemoryDumpManagerDelegateImpl::SetChildTraceMessageFilter(
+    ChildTraceMessageFilter* ctmf) {
+  auto* task_runner = ctmf ? (ctmf->ipc_task_runner()) : nullptr;
+  // Check that we are either registering the CTMF or tearing it down, but not
+  // replacing a valid instance with another one (should never happen).
+  DCHECK(!ctmf_ || (!ctmf && ctmf_task_runner_));
+  ctmf_ = ctmf;
+
+  {
+    base::AutoLock lock(lock_);
+    ctmf_task_runner_ = task_runner;
+  }
+
+  if (ctmf) {
+    base::trace_event::MemoryDumpManager::GetInstance()->Initialize(
+      this /* delegate */, false /* is_coordinator */);
+
+#if !defined(OS_LINUX) && !defined(OS_NACL)
+    // On linux the browser process takes care of dumping process metrics.
+    // The child process is not allowed to do so due to BPF sandbox.
+    tracing::ProcessMetricsMemoryDumpProvider::RegisterForProcess(
+        base::kNullProcessId);
+#endif
+  }
+}
+
+// Invoked in child processes by the MemoryDumpManager.
+void ChildMemoryDumpManagerDelegateImpl::RequestGlobalMemoryDump(
+    const base::trace_event::MemoryDumpRequestArgs& args,
+    const base::trace_event::MemoryDumpCallback& callback) {
+  // RequestGlobalMemoryDump can be called on any thread, cannot access
+  // ctmf_task_runner_ as it could be racy.
+  scoped_refptr<base::SingleThreadTaskRunner> ctmf_task_runner;
+  {
+    base::AutoLock lock(lock_);
+    ctmf_task_runner = ctmf_task_runner_;
+  }
+
+  // Bail out if we receive a dump request from the manager before the
+  // ChildTraceMessageFilter has been initialized.
+  if (!ctmf_task_runner) {
+    VLOG(1) << base::trace_event::MemoryDumpManager::kLogPrefix
+            << " failed because child trace message filter hasn't been"
+            << " initialized";
+    return AbortDumpRequest(args, callback);
+  }
+
+  // Make sure we access |ctmf_| only on the thread where it lives to avoid
+  // races on shutdown.
+  if (!ctmf_task_runner->BelongsToCurrentThread()) {
+    const bool did_post_task = ctmf_task_runner->PostTask(
+        FROM_HERE,
+        base::Bind(&ChildMemoryDumpManagerDelegateImpl::RequestGlobalMemoryDump,
+                   base::Unretained(this), args, callback));
+    if (!did_post_task)
+      return AbortDumpRequest(args, callback);
+    return;
+  }
+
+  // The ChildTraceMessageFilter could have been destroyed while hopping on the
+  // right thread. If this is the case, bail out.
+  if (!ctmf_) {
+    VLOG(1) << base::trace_event::MemoryDumpManager::kLogPrefix
+            << " failed because child trace message filter was"
+            << " destroyed while switching threads";
+    return AbortDumpRequest(args, callback);
+  }
+
+  // Send the request up to the browser process' MessageDumpmanager.
+  ctmf_->SendGlobalMemoryDumpRequest(args, callback);
+}
+
+uint64_t ChildMemoryDumpManagerDelegateImpl::GetTracingProcessId() const {
+  return tracing_process_id_;
+}
+
+}  // namespace tracing
diff --git a/components/tracing/child/child_memory_dump_manager_delegate_impl.h b/components/tracing/child/child_memory_dump_manager_delegate_impl.h
new file mode 100644
index 0000000..fc27d96
--- /dev/null
+++ b/components/tracing/child/child_memory_dump_manager_delegate_impl.h
@@ -0,0 +1,85 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_TRACING_CHILD_CHILD_MEMORY_DUMP_MANAGER_DELEGATE_IMPL_H_
+#define COMPONENTS_TRACING_CHILD_CHILD_MEMORY_DUMP_MANAGER_DELEGATE_IMPL_H_
+
+#include "base/trace_event/memory_dump_manager.h"
+
+#include <stdint.h>
+
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/singleton.h"
+#include "base/synchronization/lock.h"
+#include "components/tracing/tracing_export.h"
+
+namespace base {
+class SingleThreadTaskRunner;
+}  // namespace base
+
+namespace tracing {
+
+class ChildTraceMessageFilter;
+
+// This class is a simple proxy class between the MemoryDumpManager and the
+// ChildTraceMessageFilter. It's only purpose is to adapt the lifetime of
+// CTMF to the demands of MDM, which expects the delegate to be thread-safe
+// and long lived. CTMF, instead, can be torn down during browser shutdown.
+// This class is registered as MDM delegate in child processes and handles
+// gracefully (and thread-safely) failures in the case of a lack of the CTMF.
+class TRACING_EXPORT ChildMemoryDumpManagerDelegateImpl
+    : public base::trace_event::MemoryDumpManagerDelegate {
+ public:
+  static ChildMemoryDumpManagerDelegateImpl* GetInstance();
+
+  // base::trace_event::MemoryDumpManagerDelegate implementation.
+  void RequestGlobalMemoryDump(
+      const base::trace_event::MemoryDumpRequestArgs& args,
+      const base::trace_event::MemoryDumpCallback& callback) override;
+  uint64_t GetTracingProcessId() const override;
+
+  void SetChildTraceMessageFilter(ChildTraceMessageFilter* ctmf);
+
+  // Pass kInvalidTracingProcessId to invalidate the id.
+  void set_tracing_process_id(uint64_t id) {
+    DCHECK(tracing_process_id_ ==
+               base::trace_event::MemoryDumpManager::kInvalidTracingProcessId ||
+           id ==
+               base::trace_event::MemoryDumpManager::kInvalidTracingProcessId ||
+           id == tracing_process_id_);
+    tracing_process_id_ = id;
+  }
+
+ protected:
+  // Make CreateProcessDump() visible to ChildTraceMessageFilter.
+  friend class ChildTraceMessageFilter;
+
+ private:
+  friend struct base::DefaultSingletonTraits<
+      ChildMemoryDumpManagerDelegateImpl>;
+
+  ChildMemoryDumpManagerDelegateImpl();
+  ~ChildMemoryDumpManagerDelegateImpl() override;
+
+  ChildTraceMessageFilter* ctmf_;  // Not owned.
+
+  // The SingleThreadTaskRunner where the |ctmf_| lives.
+  // It is NULL iff |cmtf_| is NULL.
+  scoped_refptr<base::SingleThreadTaskRunner> ctmf_task_runner_;
+
+  // Protects from concurrent access to |ctmf_task_runner_| to allow
+  // RequestGlobalMemoryDump to be called from arbitrary threads.
+  base::Lock lock_;
+
+  // The unique id of the child process, created for tracing and is expected to
+  // be valid only when tracing is enabled.
+  uint64_t tracing_process_id_;
+
+  DISALLOW_COPY_AND_ASSIGN(ChildMemoryDumpManagerDelegateImpl);
+};
+
+}  // namespace tracing
+
+#endif  // COMPONENTS_TRACING_CHILD_CHILD_MEMORY_DUMP_MANAGER_DELEGATE_IMPL_H_
diff --git a/components/tracing/child/child_trace_message_filter.cc b/components/tracing/child/child_trace_message_filter.cc
index 6012a96a..ed2be875 100644
--- a/components/tracing/child/child_trace_message_filter.cc
+++ b/components/tracing/child/child_trace_message_filter.cc
@@ -8,13 +8,11 @@
 
 #include "base/memory/ref_counted_memory.h"
 #include "base/metrics/statistics_recorder.h"
-#include "base/trace_event/memory_dump_manager.h"
 #include "base/trace_event/trace_event.h"
-#include "components/tracing/common/process_metrics_memory_dump_provider.h"
+#include "components/tracing/child/child_memory_dump_manager_delegate_impl.h"
 #include "components/tracing/common/tracing_messages.h"
 #include "ipc/ipc_channel.h"
 
-using base::trace_event::MemoryDumpManager;
 using base::trace_event::TraceLog;
 
 namespace tracing {
@@ -27,18 +25,16 @@
 
 ChildTraceMessageFilter::ChildTraceMessageFilter(
     base::SingleThreadTaskRunner* ipc_task_runner)
-    : sender_(NULL), ipc_task_runner_(ipc_task_runner) {}
+    : sender_(NULL),
+      ipc_task_runner_(ipc_task_runner),
+      pending_memory_dump_guid_(0) {
+}
 
 void ChildTraceMessageFilter::OnFilterAdded(IPC::Channel* channel) {
   sender_ = channel;
   sender_->Send(new TracingHostMsg_ChildSupportsTracing());
-
-#if !defined(OS_LINUX) && !defined(OS_NACL)
-  // On linux the browser process takes care of dumping process metrics.
-  // The child process is not allowed to do so due to BPF sandbox.
-  tracing::ProcessMetricsMemoryDumpProvider::RegisterForProcess(
-      base::kNullProcessId);
-#endif
+  ChildMemoryDumpManagerDelegateImpl::GetInstance()->SetChildTraceMessageFilter(
+      this);
 }
 
 void ChildTraceMessageFilter::SetSenderForTesting(IPC::Sender* sender) {
@@ -46,6 +42,8 @@
 }
 
 void ChildTraceMessageFilter::OnFilterRemoved() {
+  ChildMemoryDumpManagerDelegateImpl::GetInstance()->SetChildTraceMessageFilter(
+      nullptr);
   sender_ = NULL;
 }
 
@@ -56,6 +54,10 @@
     IPC_MESSAGE_HANDLER(TracingMsg_EndTracing, OnEndTracing)
     IPC_MESSAGE_HANDLER(TracingMsg_CancelTracing, OnCancelTracing)
     IPC_MESSAGE_HANDLER(TracingMsg_GetTraceLogStatus, OnGetTraceLogStatus)
+    IPC_MESSAGE_HANDLER(TracingMsg_ProcessMemoryDumpRequest,
+                        OnProcessMemoryDumpRequest)
+    IPC_MESSAGE_HANDLER(TracingMsg_GlobalMemoryDumpResponse,
+                        OnGlobalMemoryDumpResponse)
     IPC_MESSAGE_HANDLER(TracingMsg_SetUMACallback, OnSetUMACallback)
     IPC_MESSAGE_HANDLER(TracingMsg_ClearUMACallback, OnClearUMACallback)
     IPC_MESSAGE_UNHANDLED(handled = false)
@@ -76,7 +78,8 @@
   base::TimeDelta time_offset = base::TimeTicks::Now() - browser_time;
   TraceLog::GetInstance()->SetTimeOffset(time_offset);
 #endif
-  MemoryDumpManager::GetInstance()->set_tracing_process_id(tracing_process_id);
+  ChildMemoryDumpManagerDelegateImpl::GetInstance()->set_tracing_process_id(
+      tracing_process_id);
   TraceLog::GetInstance()->SetEnabled(
       base::trace_event::TraceConfig(trace_config_str),
       base::trace_event::TraceLog::RECORDING_MODE);
@@ -92,8 +95,8 @@
   TraceLog::GetInstance()->Flush(
       base::Bind(&ChildTraceMessageFilter::OnTraceDataCollected, this));
 
-  MemoryDumpManager::GetInstance()->set_tracing_process_id(
-      MemoryDumpManager::kInvalidTracingProcessId);
+  ChildMemoryDumpManagerDelegateImpl::GetInstance()->set_tracing_process_id(
+      base::trace_event::MemoryDumpManager::kInvalidTracingProcessId);
 }
 
 void ChildTraceMessageFilter::OnCancelTracing() {
@@ -126,6 +129,52 @@
   }
 }
 
+// Sent by the Browser's MemoryDumpManager when coordinating a global dump.
+void ChildTraceMessageFilter::OnProcessMemoryDumpRequest(
+    const base::trace_event::MemoryDumpRequestArgs& args) {
+  ChildMemoryDumpManagerDelegateImpl::GetInstance()->CreateProcessDump(
+      args,
+      base::Bind(&ChildTraceMessageFilter::OnProcessMemoryDumpDone, this));
+}
+
+void ChildTraceMessageFilter::OnProcessMemoryDumpDone(uint64_t dump_guid,
+                                                      bool success) {
+  sender_->Send(
+      new TracingHostMsg_ProcessMemoryDumpResponse(dump_guid, success));
+}
+
+// Initiates a dump request, asking the Browser's MemoryDumpManager to
+// coordinate a global memory dump. The Browser's MDM will answer back with a
+// MemoryDumpResponse when all the child processes (including this one) have
+// dumped, or with a NACK (|success| == false) if the dump failed (e.g., due to
+// a collision with a concurrent request from another child process).
+void ChildTraceMessageFilter::SendGlobalMemoryDumpRequest(
+    const base::trace_event::MemoryDumpRequestArgs& args,
+    const base::trace_event::MemoryDumpCallback& callback) {
+  // If there is already another dump request pending from this child process,
+  // there is no point bothering the Browser's MemoryDumpManager.
+  if (pending_memory_dump_guid_) {
+    if (!callback.is_null())
+      callback.Run(args.dump_guid, false);
+    return;
+  }
+
+  pending_memory_dump_guid_ = args.dump_guid;
+  pending_memory_dump_callback_ = callback;
+  sender_->Send(new TracingHostMsg_GlobalMemoryDumpRequest(args));
+}
+
+// Sent by the Browser's MemoryDumpManager in response of a dump request
+// initiated by this child process.
+void ChildTraceMessageFilter::OnGlobalMemoryDumpResponse(uint64_t dump_guid,
+                                                         bool success) {
+  DCHECK_NE(0U, pending_memory_dump_guid_);
+  pending_memory_dump_guid_ = 0;
+  if (pending_memory_dump_callback_.is_null())
+    return;
+  pending_memory_dump_callback_.Run(dump_guid, success);
+}
+
 void ChildTraceMessageFilter::OnHistogramChanged(
     const std::string& histogram_name,
     base::Histogram::Sample reference_lower_value,
diff --git a/components/tracing/child/child_trace_message_filter.h b/components/tracing/child/child_trace_message_filter.h
index 619de78..287abd3 100644
--- a/components/tracing/child/child_trace_message_filter.h
+++ b/components/tracing/child/child_trace_message_filter.h
@@ -12,6 +12,7 @@
 #include "base/memory/ref_counted_memory.h"
 #include "base/metrics/histogram.h"
 #include "base/time/time.h"
+#include "base/trace_event/memory_dump_request_args.h"
 #include "components/tracing/tracing_export.h"
 #include "ipc/message_filter.h"
 
@@ -32,6 +33,10 @@
   void OnFilterRemoved() override;
   bool OnMessageReceived(const IPC::Message& message) override;
 
+  void SendGlobalMemoryDumpRequest(
+      const base::trace_event::MemoryDumpRequestArgs& args,
+      const base::trace_event::MemoryDumpCallback& callback);
+
   base::SingleThreadTaskRunner* ipc_task_runner() const {
     return ipc_task_runner_;
   }
@@ -53,6 +58,9 @@
                        const std::string& event_name);
   void OnCancelWatchEvent();
   void OnWatchEventMatched();
+  void OnProcessMemoryDumpRequest(
+      const base::trace_event::MemoryDumpRequestArgs& args);
+  void OnGlobalMemoryDumpResponse(uint64_t dump_guid, bool success);
   void OnSetUMACallback(const std::string& histogram_name,
                         int histogram_lower_value,
                         int histogram_upper_value,
@@ -71,11 +79,20 @@
       const scoped_refptr<base::RefCountedString>& events_str_ptr,
       bool has_more_events);
 
+  void OnProcessMemoryDumpDone(uint64_t dump_guid, bool success);
+
   void SetSenderForTesting(IPC::Sender* sender);
 
   IPC::Sender* sender_;
   base::SingleThreadTaskRunner* ipc_task_runner_;
 
+  // guid of the outstanding request (to the Browser's MemoryDumpManager), if
+  // any. 0 if there is no request pending.
+  uint64_t pending_memory_dump_guid_;
+
+  // callback of the outstanding memory dump request, if any.
+  base::trace_event::MemoryDumpCallback pending_memory_dump_callback_;
+
   base::Time histogram_last_changed_;
 
   DISALLOW_COPY_AND_ASSIGN(ChildTraceMessageFilter);
diff --git a/components/tracing/child/child_trace_message_filter_browsertest.cc b/components/tracing/child/child_trace_message_filter_browsertest.cc
new file mode 100644
index 0000000..d0d2d020
--- /dev/null
+++ b/components/tracing/child/child_trace_message_filter_browsertest.cc
@@ -0,0 +1,326 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <stdint.h>
+
+#include <memory>
+#include <tuple>
+
+#include "base/callback.h"
+#include "base/run_loop.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "base/trace_event/memory_dump_manager.h"
+#include "base/trace_event/memory_dump_provider.h"
+#include "base/trace_event/trace_event.h"
+#include "components/tracing/child/child_memory_dump_manager_delegate_impl.h"
+#include "components/tracing/child/child_trace_message_filter.h"
+#include "components/tracing/common/tracing_messages.h"
+#include "content/public/test/render_view_test.h"
+#include "testing/gmock/include/gmock/gmock.h"
+
+using base::trace_event::MemoryDumpManager;
+using base::trace_event::MemoryDumpRequestArgs;
+using base::trace_event::MemoryDumpArgs;
+using base::trace_event::MemoryDumpLevelOfDetail;
+using base::trace_event::MemoryDumpType;
+using testing::_;
+using testing::Return;
+
+namespace tracing {
+
+// A mock dump provider, used to check that dump requests actually end up
+// creating memory dumps.
+class MockDumpProvider : public base::trace_event::MemoryDumpProvider {
+ public:
+  MOCK_METHOD2(OnMemoryDump,
+               bool(const MemoryDumpArgs& args,
+                    base::trace_event::ProcessMemoryDump* pmd));
+};
+
+class ChildTracingTest : public content::RenderViewTest, public IPC::Listener {
+ public:
+  // Used as callback argument for MemoryDumpManager::RequestGlobalDump():
+  void OnMemoryDumpCallback(uint64_t dump_guid, bool status) {
+    last_callback_dump_guid_ = dump_guid;
+    last_callback_status_ = status;
+    ++callback_call_count_;
+  }
+
+ protected:
+  void SetUp() override {
+    // RenderViewTest::SetUp causes additional registrations, so we first
+    // register the mock dump provider and ignore registrations from then on.
+    // In addition to the mock dump provider, the TraceLog has already
+    // registered itself by now; this cannot be prevented easily.
+    mock_dump_provider_.reset(new MockDumpProvider());
+    MemoryDumpManager::GetInstance()->RegisterDumpProvider(
+        mock_dump_provider_.get(), "MockDumpProvider",
+        base::ThreadTaskRunnerHandle::Get());
+    MemoryDumpManager::GetInstance()
+        ->set_dumper_registrations_ignored_for_testing(true);
+
+    RenderViewTest::SetUp();
+
+    callback_call_count_ = 0;
+    last_callback_dump_guid_ = 0;
+    last_callback_status_ = false;
+    wait_for_ipc_message_type_ = 0;
+    callback_ = base::Bind(&ChildTracingTest::OnMemoryDumpCallback,
+                           base::Unretained(this));
+    task_runner_ = base::ThreadTaskRunnerHandle::Get();
+    ctmf_ = make_scoped_refptr(new ChildTraceMessageFilter(task_runner_.get()));
+    render_thread_->AddFilter(ctmf_.get());
+
+    // Add a filter to the TestSink which allows to WaitForIPCMessage() by
+    // posting a nested RunLoop closure when a given IPC Message is seen.
+    render_thread_->sink().AddFilter(this);
+
+    // Getting an instance of |ChildMemoryDumpManagerDelegateImpl| calls
+    // |MemoryDumpManager::Initialize| with the correct delegate.
+    ChildMemoryDumpManagerDelegateImpl::GetInstance();
+  }
+
+  void TearDown() override {
+    render_thread_->sink().RemoveFilter(this);
+    RenderViewTest::TearDown();
+    MemoryDumpManager::GetInstance()->UnregisterDumpProvider(
+        mock_dump_provider_.get());
+    mock_dump_provider_.reset();
+    ctmf_ = nullptr;
+    task_runner_ = nullptr;
+  }
+
+  // IPC::Filter implementation.
+  bool OnMessageReceived(const IPC::Message& message) override {
+    if (message.type() == wait_for_ipc_message_type_) {
+      DCHECK(!wait_for_ipc_closure_.is_null());
+      task_runner_->PostTask(FROM_HERE, wait_for_ipc_closure_);
+    }
+    // Always propagate messages to the sink, never consume them here.
+    return false;
+  }
+
+  const IPC::Message* WaitForIPCMessage(uint32_t message_type) {
+    base::RunLoop run_loop;
+    wait_for_ipc_message_type_ = message_type;
+    wait_for_ipc_closure_ = run_loop.QuitClosure();
+    run_loop.Run();
+    wait_for_ipc_message_type_ = 0;
+    wait_for_ipc_closure_.Reset();
+    return render_thread_->sink().GetUniqueMessageMatching(message_type);
+  }
+
+  // Simulates a synthetic browser -> child (this process) IPC message.
+  void SimulateSyntheticMessageFromBrowser(const IPC::Message& msg) {
+    ctmf_->OnMessageReceived(msg);
+  }
+
+  void EnableTracingWithMemoryDumps() {
+    // Re-enabling tracing could crash these tests https://crbug.com/656729 .
+    if (base::trace_event::TraceLog::GetInstance()->IsEnabled()) {
+      FAIL() << "Tracing seems to be already enabled. "
+                "Very likely this is because the startup tracing file "
+                "has been leaked from a previous test.";
+    }
+
+    std::string category_filter = "-*,";  // Disable all other trace categories.
+    category_filter += MemoryDumpManager::kTraceCategory;
+    base::trace_event::TraceConfig trace_config(category_filter, "");
+    TracingMsg_BeginTracing msg(trace_config.ToString(), base::TimeTicks(), 0);
+    SimulateSyntheticMessageFromBrowser(msg);
+  }
+
+  void DisableTracing() {
+    SimulateSyntheticMessageFromBrowser(TracingMsg_EndTracing());
+  }
+
+  // Simulates a synthetic browser -> child process memory dump request and
+  // checks that the child actually sends a response to that.
+  void RequestProcessMemoryDumpAndCheckResponse(uint64_t dump_guid) {
+    SimulateSyntheticMessageFromBrowser(TracingMsg_ProcessMemoryDumpRequest(
+        {dump_guid, MemoryDumpType::EXPLICITLY_TRIGGERED,
+         MemoryDumpLevelOfDetail::DETAILED}));
+
+    // Check that a child -> browser response to the local dump request is sent.
+    const IPC::Message* msg =
+        WaitForIPCMessage(TracingHostMsg_ProcessMemoryDumpResponse::ID);
+    EXPECT_NE(nullptr, msg);
+
+    // Check that the |dump_guid| and the |success| fields are properly set.
+    TracingHostMsg_ProcessMemoryDumpResponse::Param params;
+    TracingHostMsg_ProcessMemoryDumpResponse::Read(msg, &params);
+    const uint64_t resp_guid = std::get<0>(params);
+    const bool resp_success = std::get<1>(params);
+    EXPECT_EQ(dump_guid, resp_guid);
+    EXPECT_TRUE(resp_success);
+  }
+
+  // Retrieves the MemoryDumpRequestArgs of the global memory dump request that
+  // this child process tried to send to the browser. Fails if either none or
+  // multiple requests were sent.
+  MemoryDumpRequestArgs GetInterceptedGlobalMemoryDumpRequest() {
+    const IPC::Message* msg = render_thread_->sink().GetUniqueMessageMatching(
+        TracingHostMsg_GlobalMemoryDumpRequest::ID);
+    EXPECT_NE(nullptr, msg);
+    TracingHostMsg_GlobalMemoryDumpRequest::Param params;
+    TracingHostMsg_GlobalMemoryDumpRequest::Read(msg, &params);
+    MemoryDumpRequestArgs args = std::get<0>(params);
+    EXPECT_NE(0U, args.dump_guid);
+    return args;
+  }
+
+  scoped_refptr<ChildTraceMessageFilter> ctmf_;
+  std::unique_ptr<MockDumpProvider> mock_dump_provider_;
+  scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
+  base::trace_event::MemoryDumpCallback callback_;
+  uint32_t wait_for_ipc_message_type_;
+  base::Closure wait_for_ipc_closure_;
+  uint32_t callback_call_count_;
+  uint64_t last_callback_dump_guid_;
+  bool last_callback_status_;
+};
+
+// Covers the case of some browser-initiated memory dumps.
+#if defined(OS_ANDROID)
+// Flaky on Android. http://crbug.com/620734.
+#define MAYBE_BrowserInitiatedMemoryDumps DISABLED_BrowserInitiatedMemoryDumps
+#else
+#define MAYBE_BrowserInitiatedMemoryDumps BrowserInitiatedMemoryDumps
+#endif
+TEST_F(ChildTracingTest, MAYBE_BrowserInitiatedMemoryDumps) {
+  const uint32_t kNumDumps = 3;
+
+  EnableTracingWithMemoryDumps();
+  EXPECT_CALL(*mock_dump_provider_, OnMemoryDump(_, _))
+      .Times(kNumDumps)
+      .WillRepeatedly(Return(true));
+
+  for (uint32_t i = 0; i < kNumDumps; ++i) {
+    render_thread_->sink().ClearMessages();
+    RequestProcessMemoryDumpAndCheckResponse(i + 1);
+  }
+
+  DisableTracing();
+}
+
+// Covers the case of one simple child-initiated memory dump without callback,
+// simulating a global memory dump request to the browser (+ response).
+TEST_F(ChildTracingTest, SingleChildInitiatedMemoryDump) {
+  EnableTracingWithMemoryDumps();
+
+  // Expect that our mock dump provider is called when the emulated memory dump
+  // request (browser -> child) is sent.
+  EXPECT_CALL(*mock_dump_provider_, OnMemoryDump(_, _))
+      .Times(1)
+      .WillRepeatedly(Return(true));
+
+  // Send the global memory dump request to the browser.
+  render_thread_->sink().ClearMessages();
+  MemoryDumpManager::GetInstance()->RequestGlobalDump(
+      MemoryDumpType::EXPLICITLY_TRIGGERED, MemoryDumpLevelOfDetail::DETAILED);
+  base::RunLoop().RunUntilIdle();
+
+  // Check that the child -> browser global dump request IPC is actually sent.
+  MemoryDumpRequestArgs args = GetInterceptedGlobalMemoryDumpRequest();
+  EXPECT_EQ(MemoryDumpType::EXPLICITLY_TRIGGERED, args.dump_type);
+
+  // Emulate a browser -> child process dump request and corresponding response.
+  RequestProcessMemoryDumpAndCheckResponse(args.dump_guid);
+
+  // Send a synthetic browser -> child global memory dump response.
+  SimulateSyntheticMessageFromBrowser(
+      TracingMsg_GlobalMemoryDumpResponse(args.dump_guid, true));
+
+  DisableTracing();
+}
+
+// Covers the case of a global memory dump being requested while another one is
+// in progress and has not been acknowledged by the browser. The second request
+// is expected to fail immediately, while the first one is expected to suceed.
+TEST_F(ChildTracingTest, OverlappingChildInitiatedMemoryDumps) {
+  EnableTracingWithMemoryDumps();
+
+  // Expect that our mock dump provider is called only once.
+  EXPECT_CALL(*mock_dump_provider_, OnMemoryDump(_, _))
+      .Times(1)
+      .WillRepeatedly(Return(true));
+
+  // Send the global memory dump request to the browser.
+  render_thread_->sink().ClearMessages();
+  MemoryDumpManager::GetInstance()->RequestGlobalDump(
+      MemoryDumpType::EXPLICITLY_TRIGGERED, MemoryDumpLevelOfDetail::DETAILED,
+      callback_);
+  base::RunLoop().RunUntilIdle();
+
+  // Check that the child -> browser global dump request IPC is actually sent.
+  MemoryDumpRequestArgs args = GetInterceptedGlobalMemoryDumpRequest();
+  EXPECT_EQ(MemoryDumpType::EXPLICITLY_TRIGGERED, args.dump_type);
+
+  // Emulate a browser -> child process dump request and corresponding response.
+  RequestProcessMemoryDumpAndCheckResponse(args.dump_guid);
+
+  // Before the response for the first global dump is sent, send another one,
+  // and expect that to fail.
+  render_thread_->sink().ClearMessages();
+  MemoryDumpManager::GetInstance()->RequestGlobalDump(
+      MemoryDumpType::EXPLICITLY_TRIGGERED, MemoryDumpLevelOfDetail::DETAILED,
+      callback_);
+  base::RunLoop().RunUntilIdle();
+
+  EXPECT_EQ(1u, callback_call_count_);
+  EXPECT_FALSE(last_callback_status_);
+  // Whatever the guid of the second failing request is, it cannot possibly be
+  // equal to the guid of the first request.
+  EXPECT_NE(args.dump_guid, last_callback_dump_guid_);
+
+  // Also, check that no request has been forwarded to the browser (because the
+  // first request is still outstanding and has not received any response yet).
+  EXPECT_EQ(nullptr, render_thread_->sink().GetUniqueMessageMatching(
+                         TracingHostMsg_GlobalMemoryDumpRequest::ID));
+
+  // Now send a synthetic browser -> child response to the first request.
+  SimulateSyntheticMessageFromBrowser(
+      TracingMsg_GlobalMemoryDumpResponse(args.dump_guid, true));
+
+  // Verify that the the callback for the first request is finally called.
+  EXPECT_EQ(2u, callback_call_count_);
+  EXPECT_EQ(args.dump_guid, last_callback_dump_guid_);
+  EXPECT_TRUE(last_callback_status_);
+
+  DisableTracing();
+}
+
+// Covers the case of five child-initiated global memory dumps. Each global dump
+// request has a callback, which is expected to fail for 3 out of 5 cases.
+TEST_F(ChildTracingTest, MultipleChildInitiatedMemoryDumpWithFailures) {
+  const uint32_t kNumRequests = 5;
+  MemoryDumpType kDumpType = MemoryDumpType::EXPLICITLY_TRIGGERED;
+
+  EnableTracingWithMemoryDumps();
+  EXPECT_CALL(*mock_dump_provider_, OnMemoryDump(_, _))
+      .Times(kNumRequests)
+      .WillRepeatedly(Return(true));
+
+  for (uint32_t i = 0; i < kNumRequests; ++i) {
+    render_thread_->sink().ClearMessages();
+    MemoryDumpManager::GetInstance()->RequestGlobalDump(
+        kDumpType, MemoryDumpLevelOfDetail::DETAILED, callback_);
+    base::RunLoop().RunUntilIdle();
+
+    MemoryDumpRequestArgs args = GetInterceptedGlobalMemoryDumpRequest();
+    EXPECT_EQ(kDumpType, args.dump_type);
+    RequestProcessMemoryDumpAndCheckResponse(args.dump_guid);
+
+    const bool success = (i & 1) ? true : false;
+    SimulateSyntheticMessageFromBrowser(
+        TracingMsg_GlobalMemoryDumpResponse(args.dump_guid, success));
+    EXPECT_EQ(i + 1, callback_call_count_);
+    EXPECT_EQ(args.dump_guid, last_callback_dump_guid_);
+    EXPECT_EQ(success, last_callback_status_);
+  }
+
+  DisableTracing();
+}
+
+}  // namespace tracing
diff --git a/components/variations/OWNERS b/components/variations/OWNERS
index b19eb09..1b1cf9d9 100644
--- a/components/variations/OWNERS
+++ b/components/variations/OWNERS
@@ -1,3 +1,5 @@
 asvitkine@chromium.org
 jwd@chromium.org
 rkaplow@chromium.org
+
+# COMPONENT: Internals>Metrics
diff --git a/content/browser/BUILD.gn b/content/browser/BUILD.gn
index 6848a589..8db0dd2 100644
--- a/content/browser/BUILD.gn
+++ b/content/browser/BUILD.gn
@@ -107,7 +107,6 @@
     "//services/device/public/interfaces:constants",
     "//services/file:lib",
     "//services/file/public/interfaces",
-    "//services/resource_coordinator:lib",
     "//services/service_manager",
     "//services/service_manager/public/cpp",
     "//services/service_manager/public/interfaces",
@@ -1796,8 +1795,6 @@
       "android/tracing_controller_android.h",
       "android/web_contents_observer_proxy.cc",
       "android/web_contents_observer_proxy.h",
-      "frame_host/render_frame_host_android.cc",
-      "frame_host/render_frame_host_android.h",
       "media/capture/screen_capture_device_android.cc",
       "media/capture/screen_capture_device_android.h",
       "renderer_host/compositor_impl_android.cc",
diff --git a/content/browser/android/browser_jni_registrar.cc b/content/browser/android/browser_jni_registrar.cc
index 08bff79..502643c 100644
--- a/content/browser/android/browser_jni_registrar.cc
+++ b/content/browser/android/browser_jni_registrar.cc
@@ -24,7 +24,6 @@
 #include "content/browser/android/tracing_controller_android.h"
 #include "content/browser/android/web_contents_observer_proxy.h"
 #include "content/browser/frame_host/navigation_controller_android.h"
-#include "content/browser/frame_host/render_frame_host_android.h"
 #include "content/browser/media/session/audio_focus_delegate_android.h"
 #include "content/browser/media/session/media_session_android.h"
 #include "content/browser/memory/memory_monitor_android.h"
@@ -61,7 +60,6 @@
     {"NavigationControllerAndroid",
      content::NavigationControllerAndroid::Register},
     {"RegisterImeAdapter", content::RegisterImeAdapter},
-    {"RenderFrameHostAndroid", content::RenderFrameHostAndroid::Register},
     {"SpeechRecognizerImplAndroid",
      content::SpeechRecognizerImplAndroid::RegisterSpeechRecognizer},
     {"TracingControllerAndroid", content::RegisterTracingControllerAndroid},
diff --git a/content/browser/android/content_view_core_impl.cc b/content/browser/android/content_view_core_impl.cc
index e036fb8..cd96d20 100644
--- a/content/browser/android/content_view_core_impl.cc
+++ b/content/browser/android/content_view_core_impl.cc
@@ -162,17 +162,6 @@
   }
 }
 
-void RecordToolTypeForActionDown(MotionEventAndroid& event) {
-  MotionEventAndroid::Action action = event.GetAction();
-  if (action == MotionEventAndroid::ACTION_DOWN ||
-      action == MotionEventAndroid::ACTION_POINTER_DOWN ||
-      action == MotionEventAndroid::ACTION_BUTTON_PRESS) {
-    UMA_HISTOGRAM_ENUMERATION("Event.AndroidActionDown.ToolType",
-                              event.GetToolType(0),
-                              MotionEventAndroid::TOOL_TYPE_LAST + 1);
-  }
-}
-
 }  // namespace
 
 // Enables a callback when the underlying WebContents is destroyed, to enable
@@ -876,127 +865,6 @@
   }
 }
 
-jboolean ContentViewCoreImpl::OnTouchEvent(
-    JNIEnv* env,
-    const JavaParamRef<jobject>& obj,
-    const JavaParamRef<jobject>& motion_event,
-    jlong time_ms,
-    jint android_action,
-    jint pointer_count,
-    jint history_size,
-    jint action_index,
-    jfloat pos_x_0,
-    jfloat pos_y_0,
-    jfloat pos_x_1,
-    jfloat pos_y_1,
-    jint pointer_id_0,
-    jint pointer_id_1,
-    jfloat touch_major_0,
-    jfloat touch_major_1,
-    jfloat touch_minor_0,
-    jfloat touch_minor_1,
-    jfloat orientation_0,
-    jfloat orientation_1,
-    jfloat tilt_0,
-    jfloat tilt_1,
-    jfloat raw_pos_x,
-    jfloat raw_pos_y,
-    jint android_tool_type_0,
-    jint android_tool_type_1,
-    jint android_button_state,
-    jint android_meta_state,
-    jboolean is_touch_handle_event) {
-  RenderWidgetHostViewAndroid* rwhv = GetRenderWidgetHostViewAndroid();
-  // Avoid synthesizing a touch event if it cannot be forwarded.
-  if (!rwhv)
-    return false;
-
-  MotionEventAndroid::Pointer pointer0(pointer_id_0,
-                                       pos_x_0,
-                                       pos_y_0,
-                                       touch_major_0,
-                                       touch_minor_0,
-                                       orientation_0,
-                                       tilt_0,
-                                       android_tool_type_0);
-  MotionEventAndroid::Pointer pointer1(pointer_id_1,
-                                       pos_x_1,
-                                       pos_y_1,
-                                       touch_major_1,
-                                       touch_minor_1,
-                                       orientation_1,
-                                       tilt_1,
-                                       android_tool_type_1);
-  MotionEventAndroid event(1.f / dpi_scale(),
-                           env,
-                           motion_event,
-                           time_ms,
-                           android_action,
-                           pointer_count,
-                           history_size,
-                           action_index,
-                           android_button_state,
-                           android_meta_state,
-                           raw_pos_x - pos_x_0,
-                           raw_pos_y - pos_y_0,
-                           &pointer0,
-                           &pointer1);
-
-  RecordToolTypeForActionDown(event);
-
-  return is_touch_handle_event ? rwhv->OnTouchHandleEvent(event)
-                               : rwhv->OnTouchEvent(event);
-}
-
-jboolean ContentViewCoreImpl::SendMouseEvent(JNIEnv* env,
-                                             const JavaParamRef<jobject>& obj,
-                                             jlong time_ms,
-                                             jint android_action,
-                                             jfloat x,
-                                             jfloat y,
-                                             jint pointer_id,
-                                             jfloat pressure,
-                                             jfloat orientation,
-                                             jfloat tilt,
-                                             jint android_action_button,
-                                             jint android_button_state,
-                                             jint android_meta_state,
-                                             jint android_tool_type) {
-  RenderWidgetHostViewAndroid* rwhv = GetRenderWidgetHostViewAndroid();
-  if (!rwhv)
-    return false;
-
-  // Construct a motion_event object minimally, only to convert the raw
-  // parameters to ui::MotionEvent values. Since we used only the cached values
-  // at index=0, it is okay to even pass a null event to the constructor.
-  MotionEventAndroid::Pointer pointer0(
-      pointer_id, x, y, 0.0f /* touch_major */, 0.0f /* touch_minor */,
-      orientation, tilt, android_tool_type);
-
-  MotionEventAndroid motion_event(1.f / dpi_scale(),
-      env,
-      nullptr /* event */,
-      time_ms,
-      android_action,
-      1 /* pointer_count */,
-      0 /* history_size */,
-      0 /* action_index */,
-      android_button_state,
-      android_meta_state,
-      0 /* raw_offset_x_pixels */,
-      0 /* raw_offset_y_pixels */,
-      &pointer0,
-      nullptr);
-
-  RecordToolTypeForActionDown(motion_event);
-
-  // Note: This relies on identical button enum values in MotionEvent and
-  // MotionEventAndroid.
-  rwhv->SendMouseEvent(motion_event, android_action_button);
-
-  return true;
-}
-
 jboolean ContentViewCoreImpl::SendMouseWheelEvent(
     JNIEnv* env,
     const JavaParamRef<jobject>& obj,
@@ -1220,6 +1088,15 @@
     rwhv->SetMultiTouchZoomSupportEnabled(enabled);
 }
 
+void ContentViewCoreImpl::OnTouchDown(
+    const base::android::ScopedJavaLocalRef<jobject>& event) {
+  JNIEnv* env = AttachCurrentThread();
+  ScopedJavaLocalRef<jobject> obj = java_ref_.get(env);
+  if (obj.is_null())
+    return;
+  Java_ContentViewCore_onTouchDown(env, obj, event);
+}
+
 void ContentViewCoreImpl::SetAllowJavascriptInterfacesInspection(
     JNIEnv* env,
     const JavaParamRef<jobject>& obj,
@@ -1506,6 +1383,7 @@
       "A ContentViewCoreImpl should be created with a valid WebContents.";
   ui::ViewAndroid* view_android = web_contents->GetView()->GetNativeView();
   view_android->SetDelegate(jview_android_delegate);
+  view_android->SetLayout(ui::ViewAndroid::LayoutParams::MatchParent());
 
   ui::WindowAndroid* window_android =
         reinterpret_cast<ui::WindowAndroid*>(jwindow_android);
diff --git a/content/browser/android/content_view_core_impl.h b/content/browser/android/content_view_core_impl.h
index 58b280f..867f969 100644
--- a/content/browser/android/content_view_core_impl.h
+++ b/content/browser/android/content_view_core_impl.h
@@ -91,50 +91,6 @@
       JNIEnv* env,
       const base::android::JavaParamRef<jobject>& obj,
       jint orientation);
-  jboolean OnTouchEvent(
-      JNIEnv* env,
-      const base::android::JavaParamRef<jobject>& obj,
-      const base::android::JavaParamRef<jobject>& motion_event,
-      jlong time_ms,
-      jint android_action,
-      jint pointer_count,
-      jint history_size,
-      jint action_index,
-      jfloat pos_x_0,
-      jfloat pos_y_0,
-      jfloat pos_x_1,
-      jfloat pos_y_1,
-      jint pointer_id_0,
-      jint pointer_id_1,
-      jfloat touch_major_0,
-      jfloat touch_major_1,
-      jfloat touch_minor_0,
-      jfloat touch_minor_1,
-      jfloat orientation_0,
-      jfloat orientation_1,
-      jfloat tilt_0,
-      jfloat tilt_1,
-      jfloat raw_pos_x,
-      jfloat raw_pos_y,
-      jint android_tool_type_0,
-      jint android_tool_type_1,
-      jint android_button_state,
-      jint android_meta_state,
-      jboolean is_touch_handle_event);
-  jboolean SendMouseEvent(JNIEnv* env,
-                          const base::android::JavaParamRef<jobject>& obj,
-                          jlong time_ms,
-                          jint android_action,
-                          jfloat x,
-                          jfloat y,
-                          jint pointer_id,
-                          jfloat pressure,
-                          jfloat orientation,
-                          jfloat tilt,
-                          jint android_action_button,
-                          jint android_button_state,
-                          jint android_meta_state,
-                          jint tool_type);
   jboolean SendMouseWheelEvent(JNIEnv* env,
                                const base::android::JavaParamRef<jobject>& obj,
                                jlong time_ms,
@@ -375,6 +331,7 @@
                                 const gfx::PointF& extent);
 
   void OnShowUnhandledTapUIIfNeeded(int x_dip, int y_dip);
+  void OnTouchDown(const base::android::ScopedJavaLocalRef<jobject>& event);
 
   ui::ViewAndroid* GetViewAndroid() const;
 
diff --git a/content/browser/browser_main.cc b/content/browser/browser_main.cc
index e669060..772010c 100644
--- a/content/browser/browser_main.cc
+++ b/content/browser/browser_main.cc
@@ -36,6 +36,7 @@
   base::trace_event::TraceLog::GetInstance()->SetProcessName("Browser");
   base::trace_event::TraceLog::GetInstance()->SetProcessSortIndex(
       kTraceEventBrowserProcessSortIndex);
+
   std::unique_ptr<BrowserMainRunner> main_runner(BrowserMainRunner::Create());
 
   int exit_code = main_runner->Initialize(parameters);
diff --git a/content/browser/browser_main_loop.cc b/content/browser/browser_main_loop.cc
index e1d779c..211afb4 100644
--- a/content/browser/browser_main_loop.cc
+++ b/content/browser/browser_main_loop.cc
@@ -103,7 +103,6 @@
 #include "net/socket/client_socket_factory.h"
 #include "net/ssl/ssl_config_service.h"
 #include "ppapi/features/features.h"
-#include "services/resource_coordinator/memory/coordinator/coordinator_impl.h"
 #include "services/service_manager/runner/common/client_util.h"
 #include "skia/ext/event_tracer_impl.h"
 #include "skia/ext/skia_memory_dump_provider.h"
@@ -828,10 +827,6 @@
     LevelDBWrapperImpl::EnableAggressiveCommitDelay();
   }
 
-  // Create the memory instrumentation service. It will initialize the memory
-  // dump manager, too.
-  memory_instrumentation::CoordinatorImpl::GetInstance();
-
   // Enable memory-infra dump providers.
   InitSkiaEventTracer();
   tracing::ProcessMetricsMemoryDumpProvider::RegisterForProcess(
diff --git a/content/browser/child_process_launcher.cc b/content/browser/child_process_launcher.cc
index cf807ba3..f57f26b 100644
--- a/content/browser/child_process_launcher.cc
+++ b/content/browser/child_process_launcher.cc
@@ -85,9 +85,10 @@
   if (process_.process.IsValid()) {
     // Set up Mojo IPC to the new process.
     DCHECK(pending_connection);
-    pending_connection->Connect(process_.process.Handle(),
-                                std::move(server_handle),
-                                process_error_callback_);
+    pending_connection->Connect(
+        process_.process.Handle(),
+        mojo::edk::ConnectionParams(std::move(server_handle)),
+        process_error_callback_);
     client_->OnProcessLaunched();
   } else {
     termination_status_ = base::TERMINATION_STATUS_LAUNCH_FAILED;
diff --git a/content/browser/frame_host/frame_tree_node.cc b/content/browser/frame_host/frame_tree_node.cc
index 78bbf40..be14c709 100644
--- a/content/browser/frame_host/frame_tree_node.cc
+++ b/content/browser/frame_host/frame_tree_node.cc
@@ -498,8 +498,13 @@
 }
 
 bool FrameTreeNode::StopLoading() {
-  if (IsBrowserSideNavigationEnabled())
+  if (IsBrowserSideNavigationEnabled()) {
+    if (navigation_request_) {
+      navigation_request_->navigation_handle()->set_net_error_code(
+          net::ERR_ABORTED);
+    }
     ResetNavigationRequest(false);
+  }
 
   // TODO(nasko): see if child frames should send IPCs in site-per-process
   // mode.
diff --git a/content/browser/frame_host/render_frame_host_android.cc b/content/browser/frame_host/render_frame_host_android.cc
deleted file mode 100644
index aa84f81..0000000
--- a/content/browser/frame_host/render_frame_host_android.cc
+++ /dev/null
@@ -1,58 +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 "content/browser/frame_host/render_frame_host_android.h"
-
-#include "base/android/jni_string.h"
-#include "base/logging.h"
-#include "content/browser/frame_host/render_frame_host_delegate.h"
-#include "content/browser/frame_host/render_frame_host_impl.h"
-#include "jni/RenderFrameHostImpl_jni.h"
-
-using base::android::AttachCurrentThread;
-using base::android::ConvertUTF8ToJavaString;
-using base::android::JavaParamRef;
-using base::android::JavaRef;
-using base::android::ScopedJavaLocalRef;
-
-namespace content {
-
-// static
-bool RenderFrameHostAndroid::Register(JNIEnv* env) {
-  return RegisterNativesImpl(env);
-}
-
-RenderFrameHostAndroid::RenderFrameHostAndroid(
-    RenderFrameHostImpl* render_frame_host)
-    : render_frame_host_(render_frame_host) {}
-
-RenderFrameHostAndroid::~RenderFrameHostAndroid() {
-  ScopedJavaLocalRef<jobject> jobj = GetJavaObject();
-  if (!jobj.is_null()) {
-    Java_RenderFrameHostImpl_clearNativePtr(AttachCurrentThread(), jobj);
-    obj_.reset();
-  }
-}
-
-base::android::ScopedJavaLocalRef<jobject>
-RenderFrameHostAndroid::GetJavaObject() {
-  JNIEnv* env = base::android::AttachCurrentThread();
-  if (obj_.is_uninitialized()) {
-    ScopedJavaLocalRef<jobject> local_ref = Java_RenderFrameHostImpl_create(
-        env, reinterpret_cast<intptr_t>(this),
-        render_frame_host_->delegate()->GetJavaRenderFrameHostDelegate());
-    obj_ = JavaObjectWeakGlobalRef(env, local_ref);
-    return local_ref;
-  }
-  return obj_.get(env);
-}
-
-ScopedJavaLocalRef<jstring> RenderFrameHostAndroid::GetLastCommittedURL(
-    JNIEnv* env,
-    const JavaParamRef<jobject>& obj) const {
-  return ConvertUTF8ToJavaString(
-      env, render_frame_host_->GetLastCommittedURL().spec());
-}
-
-}  // namespace content
diff --git a/content/browser/frame_host/render_frame_host_android.h b/content/browser/frame_host/render_frame_host_android.h
deleted file mode 100644
index 89377d3..0000000
--- a/content/browser/frame_host/render_frame_host_android.h
+++ /dev/null
@@ -1,50 +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 CONTENT_BROWSER_FRAME_HOST_RENDER_FRAME_HOST_ANDROID_H_
-#define CONTENT_BROWSER_FRAME_HOST_RENDER_FRAME_HOST_ANDROID_H_
-
-#include <jni.h>
-
-#include <memory>
-
-#include "base/android/jni_android.h"
-#include "base/android/jni_weak_ref.h"
-#include "base/android/scoped_java_ref.h"
-#include "base/compiler_specific.h"
-#include "base/macros.h"
-#include "base/supports_user_data.h"
-#include "content/common/content_export.h"
-
-namespace content {
-
-class RenderFrameHostImpl;
-
-// Android wrapper around RenderFrameHost that provides safer passage from java
-// and back to native and provides java with a means of communicating with its
-// native counterpart.
-class RenderFrameHostAndroid : public base::SupportsUserData::Data {
- public:
-  static bool Register(JNIEnv* env);
-
-  explicit RenderFrameHostAndroid(RenderFrameHostImpl* render_frame_host);
-  ~RenderFrameHostAndroid() override;
-
-  base::android::ScopedJavaLocalRef<jobject> GetJavaObject();
-
-  // Methods called from Java
-  base::android::ScopedJavaLocalRef<jstring> GetLastCommittedURL(
-      JNIEnv* env,
-      const base::android::JavaParamRef<jobject>&) const;
-
- private:
-  RenderFrameHostImpl* const render_frame_host_;
-  JavaObjectWeakGlobalRef obj_;
-
-  DISALLOW_COPY_AND_ASSIGN(RenderFrameHostAndroid);
-};
-
-}  // namespace content
-
-#endif  // CONTENT_BROWSER_FRAME_HOST_RENDER_FRAME_HOST_ANDROID_H_
diff --git a/content/browser/frame_host/render_frame_host_delegate.cc b/content/browser/frame_host/render_frame_host_delegate.cc
index bab7c82d..e260c8e 100644
--- a/content/browser/frame_host/render_frame_host_delegate.cc
+++ b/content/browser/frame_host/render_frame_host_delegate.cc
@@ -101,11 +101,4 @@
   return false;
 }
 
-#if defined(OS_ANDROID)
-base::android::ScopedJavaLocalRef<jobject>
-RenderFrameHostDelegate::GetJavaRenderFrameHostDelegate() {
-  return nullptr;
-}
-#endif
-
 }  // namespace content
diff --git a/content/browser/frame_host/render_frame_host_delegate.h b/content/browser/frame_host/render_frame_host_delegate.h
index 148c5ed..c863b00 100644
--- a/content/browser/frame_host/render_frame_host_delegate.h
+++ b/content/browser/frame_host/render_frame_host_delegate.h
@@ -26,10 +26,6 @@
 #include "ui/gfx/native_widget_types.h"
 #endif
 
-#if defined(OS_ANDROID)
-#include "base/android/scoped_java_ref.h"
-#endif
-
 class GURL;
 
 namespace IPC {
@@ -294,11 +290,6 @@
                                                  const url::Origin& origin,
                                                  const GURL& resource_url);
 
-#if defined(OS_ANDROID)
-  virtual base::android::ScopedJavaLocalRef<jobject>
-  GetJavaRenderFrameHostDelegate();
-#endif
-
  protected:
   virtual ~RenderFrameHostDelegate() {}
 };
diff --git a/content/browser/frame_host/render_frame_host_impl.cc b/content/browser/frame_host/render_frame_host_impl.cc
index b6086a0..944d444 100644
--- a/content/browser/frame_host/render_frame_host_impl.cc
+++ b/content/browser/frame_host/render_frame_host_impl.cc
@@ -112,9 +112,8 @@
 #include "url/gurl.h"
 
 #if defined(OS_ANDROID)
-#include "content/browser/frame_host/render_frame_host_android.h"
-#include "content/browser/media/android/media_player_renderer.h"
 #include "content/public/browser/android/java_interfaces.h"
+#include "content/browser/media/android/media_player_renderer.h"
 #include "media/base/audio_renderer_sink.h"
 #include "media/base/video_renderer_sink.h"
 #include "media/mojo/services/mojo_renderer_service.h"  // nogncheck
@@ -136,10 +135,6 @@
 
 namespace {
 
-#if defined(OS_ANDROID)
-const void* const kRenderFrameHostAndroidKey = &kRenderFrameHostAndroidKey;
-#endif  // OS_ANDROID
-
 // The next value to use for the accessibility reset token.
 int g_next_accessibility_reset_token = 1;
 
@@ -3467,18 +3462,4 @@
       entry_id_for_data_nav, false);  // started_from_context_menu
 }
 
-#if defined(OS_ANDROID)
-base::android::ScopedJavaLocalRef<jobject>
-RenderFrameHostImpl::GetJavaRenderFrameHost() {
-  RenderFrameHostAndroid* render_frame_host_android =
-      static_cast<RenderFrameHostAndroid*>(
-          GetUserData(kRenderFrameHostAndroidKey));
-  if (!render_frame_host_android) {
-    render_frame_host_android = new RenderFrameHostAndroid(this);
-    SetUserData(kRenderFrameHostAndroidKey, render_frame_host_android);
-  }
-  return render_frame_host_android->GetJavaObject();
-}
-#endif
-
 }  // namespace content
diff --git a/content/browser/frame_host/render_frame_host_impl.h b/content/browser/frame_host/render_frame_host_impl.h
index fb510b8..70173f9 100644
--- a/content/browser/frame_host/render_frame_host_impl.h
+++ b/content/browser/frame_host/render_frame_host_impl.h
@@ -20,7 +20,6 @@
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
 #include "base/strings/string16.h"
-#include "base/supports_user_data.h"
 #include "base/time/time.h"
 #include "build/build_config.h"
 #include "content/browser/accessibility/browser_accessibility_manager.h"
@@ -112,7 +111,6 @@
 
 class CONTENT_EXPORT RenderFrameHostImpl
     : public RenderFrameHost,
-      public base::SupportsUserData,
       NON_EXPORTED_BASE(public mojom::FrameHost),
       public BrowserAccessibilityDelegate,
       public SiteInstanceImpl::Observer,
@@ -617,10 +615,6 @@
     return has_focused_editable_element_;
   }
 
-#if defined(OS_ANDROID)
-  base::android::ScopedJavaLocalRef<jobject> GetJavaRenderFrameHost();
-#endif
-
  protected:
   friend class RenderFrameHostFactory;
 
diff --git a/content/browser/gpu/browser_gpu_channel_host_factory.cc b/content/browser/gpu/browser_gpu_channel_host_factory.cc
index e5cace20..b4ad576 100644
--- a/content/browser/gpu/browser_gpu_channel_host_factory.cc
+++ b/content/browser/gpu/browser_gpu_channel_host_factory.cc
@@ -28,7 +28,6 @@
 #include "gpu/ipc/common/gpu_messages.h"
 #include "ipc/ipc_channel_handle.h"
 #include "ipc/message_filter.h"
-#include "services/resource_coordinator/public/interfaces/memory/constants.mojom.h"
 #include "services/service_manager/runner/common/client_util.h"
 
 namespace content {
@@ -227,8 +226,7 @@
 
 BrowserGpuChannelHostFactory::BrowserGpuChannelHostFactory()
     : gpu_client_id_(ChildProcessHostImpl::GenerateChildProcessUniqueId()),
-      gpu_client_tracing_id_(
-          memory_instrumentation::mojom::kServiceTracingProcessId),
+      gpu_client_tracing_id_(ChildProcessHost::kBrowserTracingProcessId),
       shutdown_event_(new base::WaitableEvent(
           base::WaitableEvent::ResetPolicy::MANUAL,
           base::WaitableEvent::InitialState::NOT_SIGNALED)),
diff --git a/content/browser/gpu/gpu_process_host.cc b/content/browser/gpu/gpu_process_host.cc
index 33dc88f..1252f77 100644
--- a/content/browser/gpu/gpu_process_host.cc
+++ b/content/browser/gpu/gpu_process_host.cc
@@ -1178,11 +1178,16 @@
 
     shader_prefix_key_info_ =
         GetContentClient()->GetProduct() + "-" +
-#if defined(OS_ANDROID)
-        base::android::BuildInfo::GetInstance()->android_build_fp() + "-" +
-#endif
         info.gl_vendor + "-" + info.gl_renderer + "-" + info.driver_version +
         "-" + info.driver_vendor;
+
+#if defined(OS_ANDROID)
+    std::string build_fp =
+        base::android::BuildInfo::GetInstance()->android_build_fp();
+    // TODO(ericrk): Remove this after it's up for a few days. crbug.com/699122
+    CHECK(!build_fp.empty());
+    shader_prefix_key_info_ += "-" + build_fp;
+#endif
   }
 
   // The shader prefix key is a SHA1 hash of a set of per-machine info, such as
diff --git a/content/browser/memory/memory_coordinator_impl.cc b/content/browser/memory/memory_coordinator_impl.cc
index de5b783..b403cf4 100644
--- a/content/browser/memory/memory_coordinator_impl.cc
+++ b/content/browser/memory/memory_coordinator_impl.cc
@@ -228,12 +228,8 @@
   // TODO(bashi): Record memory pressure level.
 }
 
-MemoryState MemoryCoordinatorImpl::GetBrowserMemoryState() const {
-  return browser_memory_state_;
-}
-
 MemoryState MemoryCoordinatorImpl::GetCurrentMemoryState() const {
-  return GetBrowserMemoryState();
+  return browser_memory_state_;
 }
 
 void MemoryCoordinatorImpl::SetCurrentMemoryStateForTesting(
diff --git a/content/browser/memory/memory_coordinator_impl.h b/content/browser/memory/memory_coordinator_impl.h
index 4b63fb4c..09b0c97 100644
--- a/content/browser/memory/memory_coordinator_impl.h
+++ b/content/browser/memory/memory_coordinator_impl.h
@@ -78,9 +78,6 @@
   void RecordMemoryPressure(
       base::MemoryPressureMonitor::MemoryPressureLevel level);
 
-  // Returns the browser's current memory state.
-  MemoryState GetBrowserMemoryState() const;
-
   // base::MemoryCoordinator implementations:
   MemoryState GetCurrentMemoryState() const override;
   // Temporarily sets memory state of the browser process for testing.
diff --git a/content/browser/memory/memory_coordinator_impl_unittest.cc b/content/browser/memory/memory_coordinator_impl_unittest.cc
index 7104ef9..9158668 100644
--- a/content/browser/memory/memory_coordinator_impl_unittest.cc
+++ b/content/browser/memory/memory_coordinator_impl_unittest.cc
@@ -452,7 +452,7 @@
 TEST_F(MemoryCoordinatorImplTest, SetMemoryStateForTesting) {
   MockMemoryCoordinatorClient client;
   base::MemoryCoordinatorClientRegistry::GetInstance()->Register(&client);
-  EXPECT_EQ(base::MemoryState::NORMAL, coordinator_->GetBrowserMemoryState());
+  EXPECT_EQ(base::MemoryState::NORMAL, coordinator_->GetCurrentMemoryState());
   EXPECT_EQ(base::MemoryState::NORMAL,
             base::MemoryCoordinatorProxy::GetInstance()->
                 GetCurrentMemoryState());
@@ -461,7 +461,7 @@
   base::MemoryCoordinatorProxy::GetInstance()->SetCurrentMemoryStateForTesting(
       base::MemoryState::THROTTLED);
   EXPECT_EQ(base::MemoryState::THROTTLED,
-            coordinator_->GetBrowserMemoryState());
+            coordinator_->GetCurrentMemoryState());
   EXPECT_EQ(base::MemoryState::THROTTLED,
             base::MemoryCoordinatorProxy::GetInstance()->
             GetCurrentMemoryState());
diff --git a/content/browser/renderer_host/render_widget_host_view_android.cc b/content/browser/renderer_host/render_widget_host_view_android.cc
index 9d68910a..f6f9f43b 100644
--- a/content/browser/renderer_host/render_widget_host_view_android.cc
+++ b/content/browser/renderer_host/render_widget_host_view_android.cc
@@ -84,14 +84,12 @@
 #include "ui/android/window_android.h"
 #include "ui/android/window_android_compositor.h"
 #include "ui/base/layout.h"
-#include "ui/display/display.h"
-#include "ui/display/screen.h"
+#include "ui/events/android/motion_event_android.h"
 #include "ui/events/base_event_utils.h"
 #include "ui/events/blink/blink_event_util.h"
 #include "ui/events/blink/did_overscroll_params.h"
 #include "ui/events/blink/web_input_event_traits.h"
 #include "ui/events/gesture_detection/gesture_provider_config_helper.h"
-#include "ui/events/gesture_detection/motion_event.h"
 #include "ui/gfx/android/java_bitmap.h"
 #include "ui/gfx/android/view_configuration.h"
 #include "ui/gfx/geometry/dip_util.h"
@@ -403,6 +401,17 @@
       display_compositor::GLHelper::SCALER_QUALITY_GOOD);
 }
 
+void RecordToolTypeForActionDown(const ui::MotionEventAndroid& event) {
+  ui::MotionEventAndroid::Action action = event.GetAction();
+  if (action == ui::MotionEventAndroid::ACTION_DOWN ||
+      action == ui::MotionEventAndroid::ACTION_POINTER_DOWN ||
+      action == ui::MotionEventAndroid::ACTION_BUTTON_PRESS) {
+    UMA_HISTOGRAM_ENUMERATION("Event.AndroidActionDown.ToolType",
+                              event.GetToolType(0),
+                              ui::MotionEventAndroid::TOOL_TYPE_LAST + 1);
+  }
+}
+
 bool FloatEquals(float a, float b) {
   return std::abs(a - b) < FLT_EPSILON;
 }
@@ -441,6 +450,7 @@
       content_view_core_(nullptr),
       ime_adapter_android_(this),
       cached_background_color_(SK_ColorWHITE),
+      view_(this),
       last_compositor_frame_sink_id_(kUndefinedCompositorFrameSinkId),
       gesture_provider_(ui::GetGestureProviderConfig(
                             ui::GestureProviderConfigType::CURRENT_PLATFORM),
@@ -456,6 +466,8 @@
   // Set the layer which will hold the content layer for this view. The content
   // layer is managed by the DelegatedFrameHost.
   view_.SetLayer(cc::Layer::Create());
+  view_.SetLayout(ui::ViewAndroid::LayoutParams::MatchParent());
+
   if (using_browser_compositor_) {
     cc::FrameSinkId frame_sink_id =
         host_->AllocateFrameSinkId(false /* is_guest_view_hack */);
@@ -796,8 +808,7 @@
   if (!delegated_frame_host_)
     return cc::FrameSinkId();
 
-  float scale_factor =
-      display::Screen::GetScreen()->GetPrimaryDisplay().device_scale_factor();
+  float scale_factor = view_.GetDipScale();
   DCHECK_GT(scale_factor, 0);
   // The surface hittest happens in device pixels, so we need to convert the
   // |point| from DIPs to pixels before hittesting.
@@ -854,8 +865,7 @@
   if (!delegated_frame_host_)
     return false;
 
-  float scale_factor =
-      display::Screen::GetScreen()->GetPrimaryDisplay().device_scale_factor();
+  float scale_factor = view_.GetDipScale();
   DCHECK_GT(scale_factor, 0);
   // Transformations use physical pixels rather than DIP, so conversion
   // is necessary.
@@ -1043,9 +1053,7 @@
   }
 
   base::TimeTicks start_time = base::TimeTicks::Now();
-  const display::Display& display =
-      display::Screen::GetScreen()->GetDisplayNearestWindow(GetNativeView());
-  float device_scale_factor = display.device_scale_factor();
+  float device_scale_factor = view_.GetDipScale();
   gfx::Size dst_size_in_pixel =
       gfx::ConvertRectToPixel(device_scale_factor, gfx::Rect(dst_size)).size();
   // Note: When |src_subrect| is empty, a conversion from the view size must be
@@ -1892,6 +1900,22 @@
   }
 }
 
+bool RenderWidgetHostViewAndroid::OnTouchEvent(
+    const ui::MotionEventAndroid& event,
+    bool for_touch_handle) {
+  RecordToolTypeForActionDown(event);
+
+  // TODO(jinsukkim): Remove this distinction.
+  return for_touch_handle ? OnTouchHandleEvent(event) : OnTouchEvent(event);
+}
+
+bool RenderWidgetHostViewAndroid::OnMouseEvent(
+    const ui::MotionEventAndroid& event) {
+  RecordToolTypeForActionDown(event);
+  SendMouseEvent(event, event.GetActionButton());
+  return true;
+}
+
 void RenderWidgetHostViewAndroid::OnGestureEvent(
     const ui::GestureEventData& gesture) {
   blink::WebGestureEvent web_gesture =
diff --git a/content/browser/renderer_host/render_widget_host_view_android.h b/content/browser/renderer_host/render_widget_host_view_android.h
index 780f1f2..59454f5 100644
--- a/content/browser/renderer_host/render_widget_host_view_android.h
+++ b/content/browser/renderer_host/render_widget_host_view_android.h
@@ -34,8 +34,8 @@
 #include "third_party/skia/include/core/SkColor.h"
 #include "ui/android/delegated_frame_host_android.h"
 #include "ui/android/view_android.h"
+#include "ui/android/view_client.h"
 #include "ui/android/window_android_observer.h"
-#include "ui/events/android/motion_event_android.h"
 #include "ui/events/gesture_detection/filtered_gesture_provider.h"
 #include "ui/gfx/geometry/size.h"
 #include "ui/gfx/geometry/vector2d_f.h"
@@ -45,6 +45,7 @@
 class GURL;
 
 namespace ui {
+class MotionEventAndroid;
 struct DidOverscrollParams;
 }
 
@@ -63,6 +64,7 @@
 class CONTENT_EXPORT RenderWidgetHostViewAndroid
     : public RenderWidgetHostViewBase,
       public ui::GestureProviderClient,
+      public ui::ViewClient,
       public ui::WindowAndroidObserver,
       public DelegatedFrameEvictorClient,
       public StylusTextSelectorClient,
@@ -161,6 +163,11 @@
       RenderWidgetHostViewBase* target_view,
       gfx::Point* transformed_point) override;
 
+  // ui::ViewClient implementation.
+  bool OnTouchEvent(const ui::MotionEventAndroid& m,
+                    bool for_touch_handle) override;
+  bool OnMouseEvent(const ui::MotionEventAndroid& m) override;
+
   // ui::GestureProviderClient implementation.
   void OnGestureEvent(const ui::GestureEventData& gesture) override;
 
diff --git a/content/browser/tracing/trace_message_filter.cc b/content/browser/tracing/trace_message_filter.cc
index a0e97d1b3..70089d9 100644
--- a/content/browser/tracing/trace_message_filter.cc
+++ b/content/browser/tracing/trace_message_filter.cc
@@ -46,6 +46,10 @@
                         OnTraceDataCollected)
     IPC_MESSAGE_HANDLER(TracingHostMsg_TraceLogStatusReply,
                         OnTraceLogStatusReply)
+    IPC_MESSAGE_HANDLER(TracingHostMsg_GlobalMemoryDumpRequest,
+                        OnGlobalMemoryDumpRequest)
+    IPC_MESSAGE_HANDLER(TracingHostMsg_ProcessMemoryDumpResponse,
+                        OnProcessMemoryDumpResponse)
     IPC_MESSAGE_HANDLER(TracingHostMsg_TriggerBackgroundTrace,
                         OnTriggerBackgroundTrace)
     IPC_MESSAGE_HANDLER(TracingHostMsg_AbortBackgroundTrace,
@@ -83,6 +87,18 @@
   Send(new TracingMsg_GetTraceLogStatus);
 }
 
+// Called by TracingControllerImpl, which handles the multiprocess coordination.
+void TraceMessageFilter::SendProcessMemoryDumpRequest(
+    const base::trace_event::MemoryDumpRequestArgs& args) {
+  Send(new TracingMsg_ProcessMemoryDumpRequest(args));
+}
+
+// Called by TracingControllerImpl, which handles the multiprocess coordination.
+void TraceMessageFilter::SendGlobalMemoryDumpResponse(uint64_t dump_guid,
+                                                      bool success) {
+  Send(new TracingMsg_GlobalMemoryDumpResponse(dump_guid, success));
+}
+
 void TraceMessageFilter::OnChildSupportsTracing() {
   has_child_ = true;
   TracingControllerImpl::GetInstance()->AddTraceMessageFilter(this);
@@ -117,6 +133,19 @@
   }
 }
 
+void TraceMessageFilter::OnGlobalMemoryDumpRequest(
+    const base::trace_event::MemoryDumpRequestArgs& args) {
+  TracingControllerImpl::GetInstance()->RequestGlobalMemoryDump(
+      args,
+      base::Bind(&TraceMessageFilter::SendGlobalMemoryDumpResponse, this));
+}
+
+void TraceMessageFilter::OnProcessMemoryDumpResponse(uint64_t dump_guid,
+                                                     bool success) {
+  TracingControllerImpl::GetInstance()->OnProcessMemoryDumpResponse(
+      this, dump_guid, success);
+}
+
 void TraceMessageFilter::OnTriggerBackgroundTrace(const std::string& name) {
   BackgroundTracingManagerImpl::GetInstance()->OnHistogramTrigger(name);
 }
diff --git a/content/browser/tracing/trace_message_filter.h b/content/browser/tracing/trace_message_filter.h
index 97ae0a2..94c435e 100644
--- a/content/browser/tracing/trace_message_filter.h
+++ b/content/browser/tracing/trace_message_filter.h
@@ -11,6 +11,7 @@
 #include <vector>
 
 #include "base/macros.h"
+#include "base/trace_event/memory_dump_request_args.h"
 #include "base/trace_event/trace_event.h"
 #include "content/public/browser/browser_message_filter.h"
 
@@ -35,6 +36,8 @@
   void SendSetWatchEvent(const std::string& category_name,
                          const std::string& event_name);
   void SendCancelWatchEvent();
+  void SendProcessMemoryDumpRequest(
+      const base::trace_event::MemoryDumpRequestArgs& args);
 
  protected:
   ~TraceMessageFilter() override;
@@ -46,6 +49,11 @@
   void OnWatchEventMatched();
   void OnTraceLogStatusReply(const base::trace_event::TraceLogStatus& status);
   void OnTraceDataCollected(const std::string& data);
+  void OnGlobalMemoryDumpRequest(
+      const base::trace_event::MemoryDumpRequestArgs& args);
+  void OnProcessMemoryDumpResponse(uint64_t dump_guid, bool success);
+
+  void SendGlobalMemoryDumpResponse(uint64_t dump_guid, bool success);
   void OnTriggerBackgroundTrace(const std::string& histogram_name);
   void OnAbortBackgroundTrace();
 
diff --git a/content/browser/tracing/tracing_controller_impl.cc b/content/browser/tracing/tracing_controller_impl.cc
index 54d1455..5fc480346 100644
--- a/content/browser/tracing/tracing_controller_impl.cc
+++ b/content/browser/tracing/tracing_controller_impl.cc
@@ -193,8 +193,13 @@
       pending_trace_log_status_ack_count_(0),
       maximum_trace_buffer_usage_(0),
       approximate_event_count_(0),
+      pending_memory_dump_ack_count_(0),
+      failed_memory_dump_count_(0),
       pending_clock_sync_ack_count_(0),
       is_tracing_(false) {
+  base::trace_event::MemoryDumpManager::GetInstance()->Initialize(
+      this /* delegate */, true /* is_coordinator */);
+
   // Deliberately leaked, like this class.
   base::FileTracing::SetProvider(new FileTracingProviderImpl);
 }
@@ -531,6 +536,20 @@
                      base::trace_event::TraceLogStatus()));
     }
   }
+  if (pending_memory_dump_ack_count_ > 0) {
+    DCHECK(!queued_memory_dump_requests_.empty());
+    TraceMessageFilterSet::const_iterator it =
+        pending_memory_dump_filters_.find(trace_message_filter);
+    if (it != pending_memory_dump_filters_.end()) {
+      BrowserThread::PostTask(
+          BrowserThread::UI, FROM_HERE,
+          base::Bind(&TracingControllerImpl::OnProcessMemoryDumpResponse,
+                     base::Unretained(this),
+                     base::RetainedRef(trace_message_filter),
+                     queued_memory_dump_requests_.front().args.dump_guid,
+                     false /* success */));
+    }
+  }
   trace_message_filters_.erase(trace_message_filter);
 }
 
@@ -877,6 +896,87 @@
   sink->AddMetadata(std::move(filtered_metadata));
 }
 
+void TracingControllerImpl::RequestGlobalMemoryDump(
+    const base::trace_event::MemoryDumpRequestArgs& args,
+    const base::trace_event::MemoryDumpCallback& callback) {
+  if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
+    BrowserThread::PostTask(
+        BrowserThread::UI, FROM_HERE,
+        base::Bind(&TracingControllerImpl::RequestGlobalMemoryDump,
+                   base::Unretained(this), args, callback));
+    return;
+  }
+
+  bool another_dump_already_in_progress = !queued_memory_dump_requests_.empty();
+
+  // If this is a periodic memory dump request and there already is another
+  // request in the queue with the same level of detail, there's no point in
+  // enqueuing this request.
+  if (another_dump_already_in_progress &&
+      args.dump_type == base::trace_event::MemoryDumpType::PERIODIC_INTERVAL) {
+    for (const auto& request : queued_memory_dump_requests_) {
+      if (request.args.level_of_detail == args.level_of_detail) {
+        VLOG(1) << base::trace_event::MemoryDumpManager::kLogPrefix << " ("
+                << base::trace_event::MemoryDumpTypeToString(args.dump_type)
+                << ") skipped because another dump request with the same "
+                   "level of detail ("
+                << base::trace_event::MemoryDumpLevelOfDetailToString(
+                       args.level_of_detail)
+                << ") is already in the queue";
+        if (!callback.is_null())
+          callback.Run(args.dump_guid, false /* success */);
+        return;
+      }
+    }
+  }
+
+  queued_memory_dump_requests_.emplace_back(args, callback);
+
+  // If another dump is already in progress, this dump will automatically be
+  // scheduled when the other dump finishes.
+  if (another_dump_already_in_progress)
+    return;
+
+  PerformNextQueuedGlobalMemoryDump();
+}
+
+void TracingControllerImpl::PerformNextQueuedGlobalMemoryDump() {
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  DCHECK(!queued_memory_dump_requests_.empty());
+  const base::trace_event::MemoryDumpRequestArgs& args =
+      queued_memory_dump_requests_.front().args;
+
+  // Count myself (local trace) in pending_memory_dump_ack_count_, acked by
+  // OnBrowserProcessMemoryDumpDone().
+  pending_memory_dump_ack_count_ = trace_message_filters_.size() + 1;
+  pending_memory_dump_filters_.clear();
+  failed_memory_dump_count_ = 0;
+
+  MemoryDumpManagerDelegate::CreateProcessDump(
+      args, base::Bind(&TracingControllerImpl::OnBrowserProcessMemoryDumpDone,
+                       base::Unretained(this)));
+
+  // If there are no child processes we are just done.
+  if (pending_memory_dump_ack_count_ == 1)
+    return;
+
+  pending_memory_dump_filters_ = trace_message_filters_;
+
+  for (const scoped_refptr<TraceMessageFilter>& tmf : trace_message_filters_)
+    tmf->SendProcessMemoryDumpRequest(args);
+}
+
+TracingControllerImpl::QueuedMemoryDumpRequest::QueuedMemoryDumpRequest(
+    const base::trace_event::MemoryDumpRequestArgs& args,
+    const base::trace_event::MemoryDumpCallback& callback)
+    : args(args), callback(callback) {}
+
+TracingControllerImpl::QueuedMemoryDumpRequest::~QueuedMemoryDumpRequest() {}
+
+uint64_t TracingControllerImpl::GetTracingProcessId() const {
+  return ChildProcessHost::kBrowserTracingProcessId;
+}
+
 void TracingControllerImpl::AddTraceMessageFilterObserver(
     TraceMessageFilterObserver* observer) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
@@ -895,4 +995,76 @@
     observer->OnTraceMessageFilterRemoved(filter.get());
 }
 
+void TracingControllerImpl::OnProcessMemoryDumpResponse(
+    TraceMessageFilter* trace_message_filter,
+    uint64_t dump_guid,
+    bool success) {
+  if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
+    BrowserThread::PostTask(
+        BrowserThread::UI, FROM_HERE,
+        base::Bind(&TracingControllerImpl::OnProcessMemoryDumpResponse,
+                   base::Unretained(this),
+                   base::RetainedRef(trace_message_filter), dump_guid,
+                   success));
+    return;
+  }
+
+  TraceMessageFilterSet::iterator it =
+      pending_memory_dump_filters_.find(trace_message_filter);
+
+  DCHECK(!queued_memory_dump_requests_.empty());
+  if (queued_memory_dump_requests_.front().args.dump_guid != dump_guid ||
+      it == pending_memory_dump_filters_.end()) {
+    DLOG(WARNING) << "Received unexpected memory dump response: " << dump_guid;
+    return;
+  }
+
+  DCHECK_GT(pending_memory_dump_ack_count_, 0);
+  --pending_memory_dump_ack_count_;
+  pending_memory_dump_filters_.erase(it);
+  if (!success) {
+    ++failed_memory_dump_count_;
+    VLOG(1) << base::trace_event::MemoryDumpManager::kLogPrefix
+            << " failed because of NACK from child "
+            << trace_message_filter->peer_pid();
+  }
+  FinalizeGlobalMemoryDumpIfAllProcessesReplied();
+}
+
+void TracingControllerImpl::OnBrowserProcessMemoryDumpDone(uint64_t dump_guid,
+                                                           bool success) {
+  DCHECK_GT(pending_memory_dump_ack_count_, 0);
+  --pending_memory_dump_ack_count_;
+  if (!success) {
+    ++failed_memory_dump_count_;
+    VLOG(1) << base::trace_event::MemoryDumpManager::kLogPrefix
+            << " aborted on the current process";
+  }
+  FinalizeGlobalMemoryDumpIfAllProcessesReplied();
+}
+
+void TracingControllerImpl::FinalizeGlobalMemoryDumpIfAllProcessesReplied() {
+  if (pending_memory_dump_ack_count_ > 0)
+    return;
+
+  DCHECK(!queued_memory_dump_requests_.empty());
+  {
+    const auto& callback = queued_memory_dump_requests_.front().callback;
+    if (!callback.is_null()) {
+      const bool global_success = failed_memory_dump_count_ == 0;
+      callback.Run(queued_memory_dump_requests_.front().args.dump_guid,
+                   global_success);
+    }
+  }
+  queued_memory_dump_requests_.pop_front();
+
+  // Schedule the next queued dump (if applicable).
+  if (!queued_memory_dump_requests_.empty()) {
+    BrowserThread::PostTask(
+        BrowserThread::UI, FROM_HERE,
+        base::Bind(&TracingControllerImpl::PerformNextQueuedGlobalMemoryDump,
+                   base::Unretained(this)));
+  }
+}
+
 }  // namespace content
diff --git a/content/browser/tracing/tracing_controller_impl.h b/content/browser/tracing/tracing_controller_impl.h
index 288a5ac..9d18406 100644
--- a/content/browser/tracing/tracing_controller_impl.h
+++ b/content/browser/tracing/tracing_controller_impl.h
@@ -47,6 +47,7 @@
 
 class TracingControllerImpl
     : public TracingController,
+      public base::trace_event::MemoryDumpManagerDelegate,
       public base::trace_event::TracingAgent {
  public:
   // Create an endpoint that may be supplied to any TraceDataSink to
@@ -87,6 +88,12 @@
       const std::string& sync_id,
       const RecordClockSyncMarkerCallback& callback) override;
 
+  // base::trace_event::MemoryDumpManagerDelegate implementation.
+  void RequestGlobalMemoryDump(
+      const base::trace_event::MemoryDumpRequestArgs& args,
+      const base::trace_event::MemoryDumpCallback& callback) override;
+  uint64_t GetTracingProcessId() const override;
+
   class TraceMessageFilterObserver {
    public:
     virtual void OnTraceMessageFilterAdded(TraceMessageFilter* filter) = 0;
@@ -99,6 +106,16 @@
   friend struct base::LazyInstanceTraitsBase<TracingControllerImpl>;
   friend class TraceMessageFilter;
 
+  // The arguments and callback for an queued global memory dump request.
+  struct QueuedMemoryDumpRequest {
+    QueuedMemoryDumpRequest(
+        const base::trace_event::MemoryDumpRequestArgs& args,
+        const base::trace_event::MemoryDumpCallback& callback);
+    ~QueuedMemoryDumpRequest();
+    const base::trace_event::MemoryDumpRequestArgs args;
+    const base::trace_event::MemoryDumpCallback callback;
+  };
+
   TracingControllerImpl();
   ~TracingControllerImpl() override;
 
@@ -122,6 +139,8 @@
     return pending_trace_buffer_usage_callback_.is_null();
   }
 
+  void PerformNextQueuedGlobalMemoryDump();
+
   // Methods for use by TraceMessageFilter.
   void AddTraceMessageFilter(TraceMessageFilter* trace_message_filter);
   void RemoveTraceMessageFilter(TraceMessageFilter* trace_message_filter);
@@ -151,6 +170,14 @@
 
   void OnTraceLogStatusReply(TraceMessageFilter* trace_message_filter,
                              const base::trace_event::TraceLogStatus& status);
+  void OnProcessMemoryDumpResponse(TraceMessageFilter* trace_message_filter,
+                                   uint64_t dump_guid,
+                                   bool success);
+
+  // Callback of MemoryDumpManager::CreateProcessDump().
+  void OnBrowserProcessMemoryDumpDone(uint64_t dump_guid, bool success);
+
+  void FinalizeGlobalMemoryDumpIfAllProcessesReplied();
 
   void SetEnabledOnFileThread(
       const base::trace_event::TraceConfig& trace_config,
@@ -191,6 +218,12 @@
   float maximum_trace_buffer_usage_;
   size_t approximate_event_count_;
 
+  // Pending acks for memory RequestGlobalDumpPoint.
+  int pending_memory_dump_ack_count_;
+  int failed_memory_dump_count_;
+  TraceMessageFilterSet pending_memory_dump_filters_;
+  std::list<QueuedMemoryDumpRequest> queued_memory_dump_requests_;
+
   std::vector<base::trace_event::TracingAgent*> additional_tracing_agents_;
   int pending_clock_sync_ack_count_;
   base::OneShotTimer clock_sync_timer_;
diff --git a/content/browser/web_contents/web_contents_android.cc b/content/browser/web_contents/web_contents_android.cc
index 20033db..7e829ce 100644
--- a/content/browser/web_contents/web_contents_android.cc
+++ b/content/browser/web_contents/web_contents_android.cc
@@ -294,12 +294,6 @@
   return window_android->GetJavaObject();
 }
 
-ScopedJavaLocalRef<jobject> WebContentsAndroid::GetMainFrame(
-    JNIEnv* env,
-    const JavaParamRef<jobject>& obj) const {
-  return web_contents_->GetMainFrame()->GetJavaRenderFrameHost();
-}
-
 ScopedJavaLocalRef<jstring> WebContentsAndroid::GetTitle(
     JNIEnv* env,
     const JavaParamRef<jobject>& obj) const {
@@ -721,6 +715,13 @@
   return web_contents_->HasActiveEffectivelyFullscreenVideo();
 }
 
+ScopedJavaLocalRef<jobject> WebContentsAndroid::GetOrCreateEventForwarder(
+    JNIEnv* env,
+    const base::android::JavaParamRef<jobject>& obj) {
+  gfx::NativeView native_view = web_contents_->GetView()->GetNativeView();
+  return native_view->GetEventForwarder();
+}
+
 void WebContentsAndroid::OnFinishGetContentBitmap(
     const JavaRef<jobject>& obj,
     const JavaRef<jobject>& callback,
diff --git a/content/browser/web_contents/web_contents_android.h b/content/browser/web_contents/web_contents_android.h
index 3eb20b85..33b16b6 100644
--- a/content/browser/web_contents/web_contents_android.h
+++ b/content/browser/web_contents/web_contents_android.h
@@ -42,9 +42,6 @@
   base::android::ScopedJavaLocalRef<jobject> GetTopLevelNativeWindow(
       JNIEnv* env,
       const base::android::JavaParamRef<jobject>& obj);
-  base::android::ScopedJavaLocalRef<jobject> GetMainFrame(
-      JNIEnv* env,
-      const base::android::JavaParamRef<jobject>& obj) const;
   base::android::ScopedJavaLocalRef<jstring> GetTitle(
       JNIEnv* env,
       const base::android::JavaParamRef<jobject>& obj) const;
@@ -202,6 +199,10 @@
       JNIEnv* env,
       const base::android::JavaParamRef<jobject>& obj);
 
+  base::android::ScopedJavaLocalRef<jobject> GetOrCreateEventForwarder(
+      JNIEnv* env,
+      const base::android::JavaParamRef<jobject>& obj);
+
   void SetMediaSession(
       const base::android::ScopedJavaLocalRef<jobject>& j_media_session);
 
diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc
index 6d8fc2d..4b5645f 100644
--- a/content/browser/web_contents/web_contents_impl.cc
+++ b/content/browser/web_contents/web_contents_impl.cc
@@ -3586,13 +3586,6 @@
       web_contents, allowed_per_prefs, origin, resource_url);
 }
 
-#if defined(OS_ANDROID)
-base::android::ScopedJavaLocalRef<jobject>
-WebContentsImpl::GetJavaRenderFrameHostDelegate() {
-  return GetJavaWebContents();
-}
-#endif
-
 void WebContentsImpl::OnDidDisplayContentWithCertificateErrors(
     RenderFrameHostImpl* source,
     const GURL& url) {
diff --git a/content/browser/web_contents/web_contents_impl.h b/content/browser/web_contents/web_contents_impl.h
index 3cb1046..d02b08d3 100644
--- a/content/browser/web_contents/web_contents_impl.h
+++ b/content/browser/web_contents/web_contents_impl.h
@@ -516,10 +516,6 @@
                                          bool allowed_per_prefs,
                                          const url::Origin& origin,
                                          const GURL& resource_url) override;
-#if defined(OS_ANDROID)
-  base::android::ScopedJavaLocalRef<jobject> GetJavaRenderFrameHostDelegate()
-      override;
-#endif
 
   // RenderViewHostDelegate ----------------------------------------------------
   RenderViewHostDelegateView* GetDelegateView() override;
diff --git a/content/browser/web_contents/web_contents_view_android.cc b/content/browser/web_contents/web_contents_view_android.cc
index eed15aa..7129cc9 100644
--- a/content/browser/web_contents/web_contents_view_android.cc
+++ b/content/browser/web_contents/web_contents_view_android.cc
@@ -10,15 +10,16 @@
 #include "cc/layers/layer.h"
 #include "content/browser/android/content_view_core_impl.h"
 #include "content/browser/frame_host/interstitial_page_impl.h"
-#include "content/browser/renderer_host/render_widget_host_view_android.h"
 #include "content/browser/renderer_host/render_view_host_factory.h"
 #include "content/browser/renderer_host/render_view_host_impl.h"
+#include "content/browser/renderer_host/render_widget_host_view_android.h"
 #include "content/browser/web_contents/web_contents_impl.h"
 #include "content/public/browser/render_widget_host.h"
 #include "content/public/browser/web_contents_delegate.h"
 #include "content/public/common/drop_data.h"
 #include "ui/android/overscroll_refresh_handler.h"
 #include "ui/display/screen.h"
+#include "ui/events/android/motion_event_android.h"
 #include "ui/gfx/android/java_bitmap.h"
 #include "ui/gfx/image/image_skia.h"
 
@@ -82,8 +83,8 @@
     : web_contents_(web_contents),
       content_view_core_(NULL),
       delegate_(delegate),
-      synchronous_compositor_client_(nullptr) {
-}
+      view_(this),
+      synchronous_compositor_client_(nullptr) {}
 
 WebContentsViewAndroid::~WebContentsViewAndroid() {
   if (view_.GetLayer())
@@ -395,4 +396,11 @@
   web_contents_->GetRenderWidgetHostView()->Focus();
 }
 
+bool WebContentsViewAndroid::OnTouchEvent(const ui::MotionEventAndroid& event,
+                                          bool for_touch_handle) {
+  if (event.GetAction() == ui::MotionEventAndroid::ACTION_DOWN)
+    content_view_core_->OnTouchDown(event.GetJavaObject());
+  return false;  // let the children handle the actual event.
+}
+
 } // namespace content
diff --git a/content/browser/web_contents/web_contents_view_android.h b/content/browser/web_contents/web_contents_view_android.h
index c1ad8dc..1179268a 100644
--- a/content/browser/web_contents/web_contents_view_android.h
+++ b/content/browser/web_contents/web_contents_view_android.h
@@ -15,6 +15,7 @@
 #include "content/public/common/drop_data.h"
 #include "ui/android/overscroll_refresh.h"
 #include "ui/android/view_android.h"
+#include "ui/android/view_client.h"
 #include "ui/gfx/geometry/rect_f.h"
 
 namespace content {
@@ -24,7 +25,8 @@
 
 // Android-specific implementation of the WebContentsView.
 class WebContentsViewAndroid : public WebContentsView,
-                               public RenderViewHostDelegateView {
+                               public RenderViewHostDelegateView,
+                               public ui::ViewClient {
  public:
   WebContentsViewAndroid(WebContentsImpl* web_contents,
                          WebContentsViewDelegate* delegate);
@@ -105,6 +107,10 @@
                      const gfx::Point& screen_location);
   void OnDragEnded();
 
+  // ui::ViewClient implementation.
+  bool OnTouchEvent(const ui::MotionEventAndroid& event,
+                    bool for_touch_handle) override;
+
  private:
   // The WebContents whose contents we display.
   WebContentsImpl* web_contents_;
diff --git a/content/child/BUILD.gn b/content/child/BUILD.gn
index bfebc8ef..3bd4555 100644
--- a/content/child/BUILD.gn
+++ b/content/child/BUILD.gn
@@ -241,7 +241,6 @@
     "//net",
     "//services/device/public/cpp/power_monitor",
     "//services/device/public/interfaces:constants",
-    "//services/resource_coordinator/public/cpp",
     "//services/service_manager/public/cpp",
     "//services/service_manager/public/interfaces",
     "//services/service_manager/runner/common",
diff --git a/content/child/DEPS b/content/child/DEPS
index 3a07c1cb..3f315c9 100644
--- a/content/child/DEPS
+++ b/content/child/DEPS
@@ -11,7 +11,6 @@
   "+content/public/child",
   "+services/device/public/cpp/power_monitor",
   "+services/device/public/interfaces",
-  "+services/resource_coordinator",
   "+services/service_manager",
   "+services/service_manager",
   "+v8/include/v8.h"
diff --git a/content/child/child_thread_impl.cc b/content/child/child_thread_impl.cc
index c579aa6..071b20f0 100644
--- a/content/child/child_thread_impl.cc
+++ b/content/child/child_thread_impl.cc
@@ -66,7 +66,6 @@
 #include "mojo/public/cpp/system/platform_handle.h"
 #include "services/device/public/cpp/power_monitor/power_monitor_broadcast_source.h"
 #include "services/device/public/interfaces/constants.mojom.h"
-#include "services/resource_coordinator/public/cpp/memory/memory_dump_manager_delegate_impl.h"
 #include "services/service_manager/public/cpp/connector.h"
 #include "services/service_manager/public/cpp/interface_factory.h"
 #include "services/service_manager/public/cpp/interface_provider.h"
@@ -504,10 +503,6 @@
     channel_->AddFilter(new tracing::ChildTraceMessageFilter(
         ChildProcess::current()->io_task_runner()));
     channel_->AddFilter(new ChildMemoryMessageFilter());
-
-    memory_instrumentation::MemoryDumpManagerDelegateImpl::Config config(
-        GetRemoteInterfaces());
-    memory_instrumentation::MemoryDumpManagerDelegateImpl::Create(config);
   }
 
   // In single process mode we may already have a power monitor,
diff --git a/content/common/BUILD.gn b/content/common/BUILD.gn
index 546270b1..c0dedb9 100644
--- a/content/common/BUILD.gn
+++ b/content/common/BUILD.gn
@@ -394,7 +394,6 @@
     "//ppapi/features",
     "//sandbox",
     "//sandbox:sandbox_features",
-    "//services/resource_coordinator/public/interfaces",
     "//services/service_manager",
     "//services/service_manager/public/cpp",
     "//services/service_manager/public/interfaces",
diff --git a/content/common/DEPS b/content/common/DEPS
index 3e1cdd37..52f6fd1b 100644
--- a/content/common/DEPS
+++ b/content/common/DEPS
@@ -4,7 +4,6 @@
   "+components/discardable_memory/common",
   "+components/payments",
   "+device/base/synchronization",
-  "+services/resource_coordinator/public/interfaces",
   "+services/service_manager/public/cpp",
   "+services/video_capture/public/interfaces",
 
diff --git a/content/common/child_process_host_impl.cc b/content/common/child_process_host_impl.cc
index 7e8e420..082906f 100644
--- a/content/common/child_process_host_impl.cc
+++ b/content/common/child_process_host_impl.cc
@@ -32,7 +32,6 @@
 #include "ipc/ipc_logging.h"
 #include "ipc/message_filter.h"
 #include "mojo/edk/embedder/embedder.h"
-#include "services/resource_coordinator/public/interfaces/memory/constants.mojom.h"
 #include "services/service_manager/public/cpp/interface_provider.h"
 
 #if defined(OS_LINUX)
@@ -52,6 +51,9 @@
 
 int ChildProcessHost::kInvalidUniqueID = -1;
 
+uint64_t ChildProcessHost::kBrowserTracingProcessId =
+    std::numeric_limits<uint64_t>::max();
+
 // static
 ChildProcessHost* ChildProcessHost::Create(ChildProcessHostDelegate* delegate) {
   return new ChildProcessHostImpl(delegate);
@@ -203,7 +205,7 @@
   // tracing process ids.
   if (base::CommandLine::ForCurrentProcess()->HasSwitch(
           switches::kSingleProcess))
-    return memory_instrumentation::mojom::kServiceTracingProcessId;
+    return ChildProcessHost::kBrowserTracingProcessId;
 
   // The hash value is incremented so that the tracing id is never equal to
   // MemoryDumpManager::kInvalidTracingProcessId.
diff --git a/content/common/child_process_host_impl.h b/content/common/child_process_host_impl.h
index ccca69e..ebeb8aa 100644
--- a/content/common/child_process_host_impl.h
+++ b/content/common/child_process_host_impl.h
@@ -57,8 +57,8 @@
   // process.
   //
   // Never returns MemoryDumpManager::kInvalidTracingProcessId.
-  // Returns only memory_instrumentation::mojom::kServiceTracingProcessId in
-  // single-process mode.
+  // Returns only ChildProcessHost::kBrowserTracingProcessId in single-process
+  // mode.
   static uint64_t ChildProcessUniqueIdToTracingProcessId(int child_process_id);
 
   // ChildProcessHost implementation
diff --git a/content/public/android/BUILD.gn b/content/public/android/BUILD.gn
index e30d28c..dd23b964 100644
--- a/content/public/android/BUILD.gn
+++ b/content/public/android/BUILD.gn
@@ -145,7 +145,6 @@
     "java/src/org/chromium/content/browser/PopupZoomer.java",
     "java/src/org/chromium/content/browser/PositionObserver.java",
     "java/src/org/chromium/content/browser/RenderCoordinates.java",
-    "java/src/org/chromium/content/browser/SPenSupport.java",
     "java/src/org/chromium/content/browser/ScreenOrientationProvider.java",
     "java/src/org/chromium/content/browser/SelectionClient.java",
     "java/src/org/chromium/content/browser/SelectionPopupController.java",
@@ -168,8 +167,6 @@
     "java/src/org/chromium/content/browser/crypto/ByteArrayGenerator.java",
     "java/src/org/chromium/content/browser/crypto/CipherFactory.java",
     "java/src/org/chromium/content/browser/framehost/NavigationControllerImpl.java",
-    "java/src/org/chromium/content/browser/framehost/RenderFrameHostDelegate.java",
-    "java/src/org/chromium/content/browser/framehost/RenderFrameHostImpl.java",
     "java/src/org/chromium/content/browser/input/AnimationIntervalProvider.java",
     "java/src/org/chromium/content/browser/input/ChromiumBaseInputConnection.java",
     "java/src/org/chromium/content/browser/input/CursorAnchorInfoController.java",
@@ -230,7 +227,6 @@
     "java/src/org/chromium/content_public/browser/NavigationController.java",
     "java/src/org/chromium/content_public/browser/NavigationEntry.java",
     "java/src/org/chromium/content_public/browser/NavigationHistory.java",
-    "java/src/org/chromium/content_public/browser/RenderFrameHost.java",
     "java/src/org/chromium/content_public/browser/SmartClipCallback.java",
     "java/src/org/chromium/content_public/browser/WebContents.java",
     "java/src/org/chromium/content_public/browser/WebContentsObserver.java",
@@ -341,7 +337,6 @@
     "java/src/org/chromium/content/browser/TracingControllerAndroid.java",
     "java/src/org/chromium/content/browser/accessibility/BrowserAccessibilityManager.java",
     "java/src/org/chromium/content/browser/framehost/NavigationControllerImpl.java",
-    "java/src/org/chromium/content/browser/framehost/RenderFrameHostImpl.java",
     "java/src/org/chromium/content/browser/input/DateTimeChooserAndroid.java",
     "java/src/org/chromium/content/browser/input/HandleViewResources.java",
     "java/src/org/chromium/content/browser/input/ImeAdapter.java",
diff --git a/content/public/android/java/src/org/chromium/content/browser/ContentView.java b/content/public/android/java/src/org/chromium/content/browser/ContentView.java
index 33f7d53..0aecf3ee 100644
--- a/content/public/android/java/src/org/chromium/content/browser/ContentView.java
+++ b/content/public/android/java/src/org/chromium/content/browser/ContentView.java
@@ -22,6 +22,7 @@
 import android.widget.FrameLayout;
 
 import org.chromium.base.TraceEvent;
+import org.chromium.ui.base.EventForwarder;
 
 /**
  * The containing view for {@link ContentViewCore} that exists in the Android UI hierarchy and
@@ -37,6 +38,7 @@
             MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
 
     protected final ContentViewCore mContentViewCore;
+    private EventForwarder mEventForwarder;
 
     /**
      * The desired size of this view in {@link MeasureSpec}. Set by the host
@@ -184,7 +186,7 @@
 
     @Override
     public boolean onTouchEvent(MotionEvent event) {
-        return mContentViewCore.onTouchEvent(event);
+        return getEventForwarder().onTouchEvent(event);
     }
 
     /**
@@ -204,6 +206,13 @@
         return mContentViewCore.onGenericMotionEvent(event);
     }
 
+    private EventForwarder getEventForwarder() {
+        if (mEventForwarder == null) {
+            mEventForwarder = mContentViewCore.getWebContents().getEventForwarder();
+        }
+        return mEventForwarder;
+    }
+
     @Override
     public boolean performLongClick() {
         return false;
diff --git a/content/public/android/java/src/org/chromium/content/browser/ContentViewCore.java b/content/public/android/java/src/org/chromium/content/browser/ContentViewCore.java
index aa7b94b..b22367f 100644
--- a/content/public/android/java/src/org/chromium/content/browser/ContentViewCore.java
+++ b/content/public/android/java/src/org/chromium/content/browser/ContentViewCore.java
@@ -69,6 +69,7 @@
 import org.chromium.content_public.browser.WebContentsObserver;
 import org.chromium.device.gamepad.GamepadList;
 import org.chromium.ui.base.DeviceFormFactor;
+import org.chromium.ui.base.EventForwarder;
 import org.chromium.ui.base.ViewAndroidDelegate;
 import org.chromium.ui.base.WindowAndroid;
 import org.chromium.ui.base.ime.TextInputType;
@@ -87,6 +88,9 @@
  * Provides a Java-side 'wrapper' around a WebContent (native) instance.
  * Contains all the major functionality necessary to manage the lifecycle of a ContentView without
  * being tied to the view system.
+ *
+ * WARNING: ContentViewCore is in the process of being broken up. Please do not add new stuff.
+ * See https://crbug.com/598880.
  */
 @JNINamespace("content")
 public class ContentViewCore implements AccessibilityStateChangeListener, DisplayAndroidObserver,
@@ -618,6 +622,10 @@
         }
     }
 
+    private EventForwarder getEventForwarder() {
+        return getWebContents().getEventForwarder();
+    }
+
     /**
      * Set {@link ActionMode.Callback} used by {@link SelectionPopupController}.
      * @param callback ActionMode.Callback instance.
@@ -937,146 +945,11 @@
         return GamepadList.isGamepadAPIActive();
     }
 
-    /**
-     * @see View#onTouchEvent(MotionEvent)
-     */
-    public boolean onTouchEvent(MotionEvent event) {
-        // TODO(mustaq): Should we include MotionEvent.TOOL_TYPE_STYLUS here?
-        // crbug.com/592082
-        if (event.getToolType(0) == MotionEvent.TOOL_TYPE_MOUSE) {
-            // Mouse button info is incomplete on L and below
-            int apiVersion = Build.VERSION.SDK_INT;
-            if (apiVersion >= android.os.Build.VERSION_CODES.M) {
-                return sendMouseEvent(event);
-            }
-        }
-
-        final boolean isTouchHandleEvent = false;
-        return sendTouchEvent(event, isTouchHandleEvent);
-    }
-
-    @TargetApi(Build.VERSION_CODES.M)
-    private boolean sendMouseEvent(MotionEvent event) {
-        TraceEvent.begin("sendMouseEvent");
-
-        MotionEvent offsetEvent = createOffsetMotionEvent(event);
-        try {
-            if (mNativeContentViewCore == 0) return false;
-
-            int eventAction = event.getActionMasked();
-
-            // For mousedown and mouseup events, we use ACTION_BUTTON_PRESS
-            // and ACTION_BUTTON_RELEASE respectively because they provide
-            // info about the changed-button.
-            if (eventAction == MotionEvent.ACTION_DOWN || eventAction == MotionEvent.ACTION_UP) {
-                return false;
-            }
-
-            nativeSendMouseEvent(mNativeContentViewCore, event.getEventTime(), eventAction,
-                    offsetEvent.getX(), offsetEvent.getY(), event.getPointerId(0),
-                    event.getPressure(0), event.getOrientation(0),
-                    event.getAxisValue(MotionEvent.AXIS_TILT, 0), event.getActionButton(),
-                    event.getButtonState(), event.getMetaState(), event.getToolType(0));
-            return true;
-        } finally {
-            offsetEvent.recycle();
-            TraceEvent.end("sendMouseEvent");
-        }
-    }
-
-    /**
-     * Called by PopupWindow-based touch handles.
-     * @param event the MotionEvent targeting the handle.
-     */
-    public boolean onTouchHandleEvent(MotionEvent event) {
-        final boolean isTouchHandleEvent = true;
-        return sendTouchEvent(event, isTouchHandleEvent);
-    }
-
-    private boolean sendTouchEvent(MotionEvent event, boolean isTouchHandleEvent) {
-        TraceEvent.begin("sendTouchEvent");
-        try {
-            int eventAction = event.getActionMasked();
-
-            if (eventAction == MotionEvent.ACTION_DOWN) {
-                if (mShouldRequestUnbufferedDispatch) {
-                    requestUnbufferedDispatch(event);
-                }
-                cancelRequestToScrollFocusedEditableNodeIntoView();
-            }
-
-            if (SPenSupport.isSPenSupported(mContext)) {
-                eventAction = SPenSupport.convertSPenEventAction(eventAction);
-            }
-            if (!isValidTouchEventActionForNative(eventAction)) return false;
-
-            if (mNativeContentViewCore == 0) return false;
-
-            // A zero offset is quite common, in which case the unnecessary copy should be avoided.
-            MotionEvent offset = null;
-            if (mCurrentTouchOffsetX != 0 || mCurrentTouchOffsetY != 0) {
-                offset = createOffsetMotionEvent(event);
-                event = offset;
-            }
-
-            final int pointerCount = event.getPointerCount();
-
-            float[] touchMajor = {event.getTouchMajor(),
-                                  pointerCount > 1 ? event.getTouchMajor(1) : 0};
-            float[] touchMinor = {event.getTouchMinor(),
-                                  pointerCount > 1 ? event.getTouchMinor(1) : 0};
-
-            for (int i = 0; i < 2; i++) {
-                if (touchMajor[i] < touchMinor[i]) {
-                    float tmp = touchMajor[i];
-                    touchMajor[i] = touchMinor[i];
-                    touchMinor[i] = tmp;
-                }
-            }
-
-            final boolean consumed = nativeOnTouchEvent(mNativeContentViewCore, event,
-                    event.getEventTime(), eventAction,
-                    pointerCount, event.getHistorySize(), event.getActionIndex(),
-                    event.getX(), event.getY(),
-                    pointerCount > 1 ? event.getX(1) : 0,
-                    pointerCount > 1 ? event.getY(1) : 0,
-                    event.getPointerId(0), pointerCount > 1 ? event.getPointerId(1) : -1,
-                    touchMajor[0], touchMajor[1],
-                    touchMinor[0], touchMinor[1],
-                    event.getOrientation(), pointerCount > 1 ? event.getOrientation(1) : 0,
-                    event.getAxisValue(MotionEvent.AXIS_TILT),
-                    pointerCount > 1 ? event.getAxisValue(MotionEvent.AXIS_TILT, 1) : 0,
-                    event.getRawX(), event.getRawY(),
-                    event.getToolType(0),
-                    pointerCount > 1 ? event.getToolType(1) : MotionEvent.TOOL_TYPE_UNKNOWN,
-                    event.getButtonState(),
-                    event.getMetaState(),
-                    isTouchHandleEvent);
-
-            if (offset != null) offset.recycle();
-            return consumed;
-        } finally {
-            TraceEvent.end("sendTouchEvent");
-        }
-    }
-
     @CalledByNative
     private void requestDisallowInterceptTouchEvent() {
         mContainerView.requestDisallowInterceptTouchEvent(true);
     }
 
-    private static boolean isValidTouchEventActionForNative(int eventAction) {
-        // Only these actions have any effect on gesture detection.  Other
-        // actions have no corresponding WebTouchEvent type and may confuse the
-        // touch pipline, so we ignore them entirely.
-        return eventAction == MotionEvent.ACTION_DOWN
-                || eventAction == MotionEvent.ACTION_UP
-                || eventAction == MotionEvent.ACTION_CANCEL
-                || eventAction == MotionEvent.ACTION_MOVE
-                || eventAction == MotionEvent.ACTION_POINTER_DOWN
-                || eventAction == MotionEvent.ACTION_POINTER_UP;
-    }
-
     /**
      * @return Whether a scroll targeting web content is in progress.
      */
@@ -1429,6 +1302,12 @@
         }
     }
 
+    @CalledByNative
+    private void onTouchDown(MotionEvent event) {
+        if (mShouldRequestUnbufferedDispatch) requestUnbufferedDispatch(event);
+        cancelRequestToScrollFocusedEditableNodeIntoView();
+    }
+
     private void updateAfterSizeChanged() {
         mPopupZoomer.hide(false);
 
@@ -1542,13 +1421,11 @@
                 return mBrowserAccessibilityManager.onHoverEvent(offset);
             }
 
-            if (mNativeContentViewCore != 0) {
-                nativeSendMouseEvent(mNativeContentViewCore, event.getEventTime(), eventAction,
-                        offset.getX(), offset.getY(), event.getPointerId(0), event.getPressure(0),
-                        event.getOrientation(0), event.getAxisValue(MotionEvent.AXIS_TILT, 0),
-                        0 /* changedButton */, event.getButtonState(), event.getMetaState(),
-                        event.getToolType(0));
-            }
+            getEventForwarder().sendMouseEvent(event.getEventTime(), eventAction, offset.getX(),
+                    offset.getY(), event.getPointerId(0), event.getPressure(0),
+                    event.getOrientation(0), event.getAxisValue(MotionEvent.AXIS_TILT, 0),
+                    0 /* changedButton */, event.getButtonState(), event.getMetaState(),
+                    event.getToolType(0));
             return true;
         } finally {
             offset.recycle();
@@ -1577,7 +1454,7 @@
                     // TODO(mustaq): Should we include MotionEvent.TOOL_TYPE_STYLUS here?
                     // crbug.com/592082
                     if (event.getToolType(0) == MotionEvent.TOOL_TYPE_MOUSE) {
-                        return sendMouseEvent(event);
+                        return getEventForwarder().onMouseEvent(event);
                     }
             }
         } else if ((event.getSource() & InputDevice.SOURCE_CLASS_JOYSTICK) != 0) {
@@ -1601,6 +1478,7 @@
     public void setCurrentTouchEventOffsets(float dx, float dy) {
         mCurrentTouchOffsetX = dx;
         mCurrentTouchOffsetY = dy;
+        getEventForwarder().setCurrentTouchEventOffsets(dx, dy);
     }
 
     private MotionEvent createOffsetMotionEvent(MotionEvent src) {
@@ -2695,8 +2573,7 @@
             // new size).
             // TODO(jdduke): We should not assume that onSizeChanged will
             // always be called, crbug.com/294908.
-            getContainerView().getWindowVisibleDisplayFrame(
-                    mFocusPreOSKViewportRect);
+            getContainerView().getWindowVisibleDisplayFrame(mFocusPreOSKViewportRect);
         } else if (hasFocus() && resultCode == InputMethodManager.RESULT_UNCHANGED_SHOWN) {
             // If the OSK was already there, focus the form immediately.
             if (mWebContents != null) {
@@ -2732,25 +2609,6 @@
     private native void nativeSendOrientationChangeEvent(
             long nativeContentViewCoreImpl, int orientation);
 
-    // All touch events (including flings, scrolls etc) accept coordinates in physical pixels.
-    private native boolean nativeOnTouchEvent(
-            long nativeContentViewCoreImpl, MotionEvent event,
-            long timeMs, int action, int pointerCount, int historySize, int actionIndex,
-            float x0, float y0, float x1, float y1,
-            int pointerId0, int pointerId1,
-            float touchMajor0, float touchMajor1,
-            float touchMinor0, float touchMinor1,
-            float orientation0, float orientation1,
-            float tilt0, float tilt1,
-            float rawX, float rawY,
-            int androidToolType0, int androidToolType1,
-            int androidButtonState, int androidMetaState,
-            boolean isTouchHandleEvent);
-
-    private native int nativeSendMouseEvent(long nativeContentViewCoreImpl, long timeMs, int action,
-            float x, float y, int pointerId, float pressure, float orientaton, float tilt,
-            int actionButton, int buttonState, int metaState, int toolType);
-
     private native int nativeSendMouseWheelEvent(long nativeContentViewCoreImpl, long timeMs,
             float x, float y, float ticksX, float ticksY, float pixelsPerTick);
 
diff --git a/content/public/android/java/src/org/chromium/content/browser/framehost/RenderFrameHostDelegate.java b/content/public/android/java/src/org/chromium/content/browser/framehost/RenderFrameHostDelegate.java
deleted file mode 100644
index 215527e..0000000
--- a/content/public/android/java/src/org/chromium/content/browser/framehost/RenderFrameHostDelegate.java
+++ /dev/null
@@ -1,11 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-package org.chromium.content.browser.framehost;
-
-/**
- * The RenderFrameHost Java wrapper to allow communicating with the native RenderFrameHost object.
- *
- */
-public interface RenderFrameHostDelegate {}
diff --git a/content/public/android/java/src/org/chromium/content/browser/framehost/RenderFrameHostImpl.java b/content/public/android/java/src/org/chromium/content/browser/framehost/RenderFrameHostImpl.java
deleted file mode 100644
index 90b9740..0000000
--- a/content/public/android/java/src/org/chromium/content/browser/framehost/RenderFrameHostImpl.java
+++ /dev/null
@@ -1,49 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-package org.chromium.content.browser.framehost;
-
-import org.chromium.base.annotations.CalledByNative;
-import org.chromium.base.annotations.JNINamespace;
-import org.chromium.content_public.browser.RenderFrameHost;
-
-/**
- * The RenderFrameHostImpl Java wrapper to allow communicating with the native RenderFrameHost
- * object.
- */
-@JNINamespace("content")
-public class RenderFrameHostImpl implements RenderFrameHost {
-    private long mNativeRenderFrameHostAndroid;
-    // mDelegate can be null.
-    final RenderFrameHostDelegate mDelegate;
-
-    private RenderFrameHostImpl(
-            long nativeRenderFrameHostAndroid, RenderFrameHostDelegate delegate) {
-        mNativeRenderFrameHostAndroid = nativeRenderFrameHostAndroid;
-        mDelegate = delegate;
-    }
-
-    @CalledByNative
-    private static RenderFrameHostImpl create(
-            long nativeRenderFrameHostAndroid, RenderFrameHostDelegate delegate) {
-        return new RenderFrameHostImpl(nativeRenderFrameHostAndroid, delegate);
-    }
-
-    @CalledByNative
-    private void clearNativePtr() {
-        mNativeRenderFrameHostAndroid = 0;
-    }
-
-    public RenderFrameHostDelegate getRenderFrameHostDelegate() {
-        return mDelegate;
-    }
-
-    @Override
-    public String getLastCommittedURL() {
-        if (mNativeRenderFrameHostAndroid == 0) return null;
-        return nativeGetLastCommittedURL(mNativeRenderFrameHostAndroid);
-    }
-
-    private native String nativeGetLastCommittedURL(long nativeRenderFrameHostAndroid);
-}
diff --git a/content/public/android/java/src/org/chromium/content/browser/webcontents/WebContentsImpl.java b/content/public/android/java/src/org/chromium/content/browser/webcontents/WebContentsImpl.java
index 1058494a..d0b922d8 100644
--- a/content/public/android/java/src/org/chromium/content/browser/webcontents/WebContentsImpl.java
+++ b/content/public/android/java/src/org/chromium/content/browser/webcontents/WebContentsImpl.java
@@ -20,8 +20,6 @@
 import org.chromium.base.annotations.JNINamespace;
 import org.chromium.content.browser.AppWebMessagePort;
 import org.chromium.content.browser.MediaSessionImpl;
-import org.chromium.content.browser.framehost.RenderFrameHostDelegate;
-import org.chromium.content.browser.framehost.RenderFrameHostImpl;
 import org.chromium.content_public.browser.AccessibilitySnapshotCallback;
 import org.chromium.content_public.browser.AccessibilitySnapshotNode;
 import org.chromium.content_public.browser.ContentBitmapCallback;
@@ -29,12 +27,12 @@
 import org.chromium.content_public.browser.JavaScriptCallback;
 import org.chromium.content_public.browser.MessagePort;
 import org.chromium.content_public.browser.NavigationController;
-import org.chromium.content_public.browser.RenderFrameHost;
 import org.chromium.content_public.browser.SmartClipCallback;
 import org.chromium.content_public.browser.WebContents;
 import org.chromium.content_public.browser.WebContentsObserver;
 import org.chromium.ui.OverscrollRefreshHandler;
 import org.chromium.ui.accessibility.AXTextStyle;
+import org.chromium.ui.base.EventForwarder;
 import org.chromium.ui.base.WindowAndroid;
 
 import java.util.ArrayList;
@@ -46,9 +44,9 @@
  * object.
  */
 @JNINamespace("content")
-// TODO(tedchoc): Remove the package restriction once this class moves to a non-public content
+//TODO(tedchoc): Remove the package restriction once this class moves to a non-public content
 //               package whose visibility will be enforced via DEPS.
-/* package */ class WebContentsImpl implements WebContents, RenderFrameHostDelegate {
+/* package */ class WebContentsImpl implements WebContents {
     private static final String PARCEL_VERSION_KEY = "version";
     private static final String PARCEL_WEBCONTENTS_KEY = "webcontents";
     private static final String PARCEL_PROCESS_GUARD_KEY = "processguard";
@@ -95,17 +93,8 @@
                 }
             };
 
-    public static WebContents fromRenderFrameHost(RenderFrameHost rfh) {
-        RenderFrameHostDelegate delegate = ((RenderFrameHostImpl) rfh).getRenderFrameHostDelegate();
-        if (delegate == null || !(delegate instanceof WebContents)) {
-            return null;
-        }
-        return (WebContents) delegate;
-    }
-
     private long mNativeWebContentsAndroid;
     private NavigationController mNavigationController;
-    private RenderFrameHost mMainFrame;
 
     // Lazily created proxy observer for handling all Java-based WebContentsObservers.
     private WebContentsObserverProxy mObserverProxy;
@@ -116,6 +105,8 @@
 
     private SmartClipCallback mSmartClipCallback;
 
+    private EventForwarder mEventForwarder;
+
     private WebContentsImpl(
             long nativeWebContentsAndroid, NavigationController navigationController) {
         mNativeWebContentsAndroid = nativeWebContentsAndroid;
@@ -184,14 +175,6 @@
     }
 
     @Override
-    public RenderFrameHost getMainFrame() {
-        if (mMainFrame == null) {
-            mMainFrame = nativeGetMainFrame(mNativeWebContentsAndroid);
-        }
-        return mMainFrame;
-    }
-
-    @Override
     public String getTitle() {
         return nativeGetTitle(mNativeWebContentsAndroid);
     }
@@ -490,6 +473,15 @@
     }
 
     @Override
+    public EventForwarder getEventForwarder() {
+        assert mNativeWebContentsAndroid != 0;
+        if (mEventForwarder == null) {
+            mEventForwarder = nativeGetOrCreateEventForwarder(mNativeWebContentsAndroid);
+        }
+        return mEventForwarder;
+    }
+
+    @Override
     public void addObserver(WebContentsObserver observer) {
         assert mNativeWebContentsAndroid != 0;
         if (mObserverProxy == null) mObserverProxy = new WebContentsObserverProxy(this);
@@ -582,7 +574,6 @@
     private static native WebContents nativeFromNativePtr(long webContentsAndroidPtr);
 
     private native WindowAndroid nativeGetTopLevelNativeWindow(long nativeWebContentsAndroid);
-    private native RenderFrameHost nativeGetMainFrame(long nativeWebContentsAndroid);
     private native String nativeGetTitle(long nativeWebContentsAndroid);
     private native String nativeGetVisibleURL(long nativeWebContentsAndroid);
     private native boolean nativeIsLoading(long nativeWebContentsAndroid);
@@ -641,4 +632,5 @@
     private native void nativeDismissTextHandles(long nativeWebContentsAndroid);
     private native void nativeSetHasPersistentVideo(long nativeWebContentsAndroid, boolean value);
     private native boolean nativeHasActiveEffectivelyFullscreenVideo(long nativeWebContentsAndroid);
+    private native EventForwarder nativeGetOrCreateEventForwarder(long nativeWebContentsAndroid);
 }
diff --git a/content/public/android/java/src/org/chromium/content_public/browser/RenderFrameHost.java b/content/public/android/java/src/org/chromium/content_public/browser/RenderFrameHost.java
deleted file mode 100644
index 5748da2..0000000
--- a/content/public/android/java/src/org/chromium/content_public/browser/RenderFrameHost.java
+++ /dev/null
@@ -1,18 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-package org.chromium.content_public.browser;
-
-/**
- * The RenderFrameHost Java wrapper to allow communicating with the native RenderFrameHost object.
- *
- */
-public interface RenderFrameHost {
-    /**
-     * Get the last committed URL of the frame.
-     *
-     * @return The last committed URL of the frame.
-     */
-    String getLastCommittedURL();
-}
diff --git a/content/public/android/java/src/org/chromium/content_public/browser/WebContents.java b/content/public/android/java/src/org/chromium/content_public/browser/WebContents.java
index 3bee2658..1a10295 100644
--- a/content/public/android/java/src/org/chromium/content_public/browser/WebContents.java
+++ b/content/public/android/java/src/org/chromium/content_public/browser/WebContents.java
@@ -9,6 +9,7 @@
 
 import org.chromium.base.VisibleForTesting;
 import org.chromium.ui.OverscrollRefreshHandler;
+import org.chromium.ui.base.EventForwarder;
 import org.chromium.ui.base.WindowAndroid;
 
 /**
@@ -57,11 +58,6 @@
     NavigationController getNavigationController();
 
     /**
-     * @return  The main frame associated with this WebContents.
-     */
-    RenderFrameHost getMainFrame();
-
-    /**
      * @return The title for the current visible page.
      */
     String getTitle();
@@ -327,6 +323,12 @@
     void requestAccessibilitySnapshot(AccessibilitySnapshotCallback callback);
 
     /**
+     * Returns {@link EventForwarder} which is used to forward input/view events
+     * to native content layer.
+     */
+    EventForwarder getEventForwarder();
+
+    /**
      * Add an observer to the WebContents
      *
      * @param observer The observer to add.
diff --git a/content/public/android/javatests/src/org/chromium/content/browser/webcontents/WebContentsTest.java b/content/public/android/javatests/src/org/chromium/content/browser/webcontents/WebContentsTest.java
index 110a099..fa52a8a7 100644
--- a/content/public/android/javatests/src/org/chromium/content/browser/webcontents/WebContentsTest.java
+++ b/content/public/android/javatests/src/org/chromium/content/browser/webcontents/WebContentsTest.java
@@ -11,7 +11,6 @@
 import android.support.test.filters.SmallTest;
 
 import org.chromium.base.ThreadUtils;
-import org.chromium.content_public.browser.RenderFrameHost;
 import org.chromium.content_public.browser.WebContents;
 import org.chromium.content_shell.Shell;
 import org.chromium.content_shell_apk.ContentShellActivity;
@@ -311,35 +310,6 @@
         }
     }
 
-    /**
-     * Check that the main frame associated with the WebContents is not null
-     * and corresponds with the test URL.
-     *
-     * @throws InterruptedException
-     */
-    @SmallTest
-    public void testWebContentsMainFrame() throws InterruptedException {
-        final ContentShellActivity activity = launchContentShellWithUrl(TEST_URL_1);
-        waitForActiveShellToBeDoneLoading();
-        final WebContents webContents = activity.getActiveWebContents();
-
-        ThreadUtils.postOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                RenderFrameHost frameHost = webContents.getMainFrame();
-
-                assertNotNull(frameHost);
-
-                assertEquals("RenderFrameHost has incorrect last committed URL", "about:blank",
-                        frameHost.getLastCommittedURL());
-
-                WebContents associatedWebContents = WebContentsImpl.fromRenderFrameHost(frameHost);
-                assertEquals("RenderFrameHost associated with different WebContents", webContents,
-                        associatedWebContents);
-            }
-        });
-    }
-
     private boolean isWebContentsDestroyed(final WebContents webContents) {
         return ThreadUtils.runOnUiThreadBlockingNoException(new Callable<Boolean>() {
             @Override
diff --git a/content/public/app/mojo/content_browser_manifest.json b/content/public/app/mojo/content_browser_manifest.json
index 9d27518..78a5d95 100644
--- a/content/public/app/mojo/content_browser_manifest.json
+++ b/content/public/app/mojo/content_browser_manifest.json
@@ -4,9 +4,6 @@
   "interface_provider_specs": {
     "service_manager:connector": {
       "provides": {
-        "gpu": [
-          "memory_instrumentation::mojom::Coordinator"
-        ],
         "plugin": [
           "discardable_memory::mojom::DiscardableSharedMemoryManager",
           "ui::mojom::Gpu"
@@ -37,7 +34,6 @@
           "discardable_memory::mojom::DiscardableSharedMemoryManager",
           "media::mojom::ImageCapture",
           "memory_coordinator::mojom::MemoryCoordinatorHandle",
-          "memory_instrumentation::mojom::Coordinator",
           "payments::mojom::PaymentAppManager",
           "shape_detection::mojom::BarcodeDetection",
           "shape_detection::mojom::FaceDetectionProvider",
diff --git a/content/public/common/child_process_host.h b/content/public/common/child_process_host.h
index 51e617f..6a541cb 100644
--- a/content/public/common/child_process_host.h
+++ b/content/public/common/child_process_host.h
@@ -40,6 +40,11 @@
   // any kind, including the values returned by RenderProcessHost::GetID().
   static int kInvalidUniqueID;
 
+  // This value is used as the tracing id of the browser process for identifying
+  // cross-process shared memory segments when tracing.
+  // Note: In single-process mode all the clients of tracing will use this id.
+  static uint64_t kBrowserTracingProcessId;
+
   // Used to create a child process host. The delegate must outlive this object.
   static ChildProcessHost* Create(ChildProcessHostDelegate* delegate);
 
diff --git a/content/test/BUILD.gn b/content/test/BUILD.gn
index e47bd0b..c948785 100644
--- a/content/test/BUILD.gn
+++ b/content/test/BUILD.gn
@@ -741,7 +741,6 @@
     "//net:test_support",
     "//ppapi/features",
     "//services/catalog:lib",
-    "//services/resource_coordinator:lib",
     "//services/service_manager/public/cpp",
     "//services/ui/gpu/interfaces",
     "//services/ui/public/cpp/gpu",
diff --git a/extensions/common/manifest.h b/extensions/common/manifest.h
index 5455cc6..f21713ad 100644
--- a/extensions/common/manifest.h
+++ b/extensions/common/manifest.h
@@ -110,6 +110,10 @@
     return location == COMPONENT || location == EXTERNAL_COMPONENT;
   }
 
+  static inline bool IsValidLocation(Location location) {
+    return location > INVALID_LOCATION && location < NUM_LOCATIONS;
+  }
+
   // Unpacked extensions start off with file access since they are a developer
   // feature.
   static inline bool ShouldAlwaysAllowFileAccess(Location location) {
diff --git a/gpu/command_buffer/service/texture_manager.cc b/gpu/command_buffer/service/texture_manager.cc
index e9307a24..35f80f4 100644
--- a/gpu/command_buffer/service/texture_manager.cc
+++ b/gpu/command_buffer/service/texture_manager.cc
@@ -1384,10 +1384,13 @@
     case GL_TEXTURE_BASE_LEVEL:
     case GL_TEXTURE_MAX_LEVEL:
     case GL_TEXTURE_USAGE_ANGLE:
-      {
-        GLint iparam = static_cast<GLint>(std::round(param));
-        return SetParameteri(feature_info, pname, iparam);
-      }
+    case GL_TEXTURE_SWIZZLE_R:
+    case GL_TEXTURE_SWIZZLE_G:
+    case GL_TEXTURE_SWIZZLE_B:
+    case GL_TEXTURE_SWIZZLE_A: {
+      GLint iparam = static_cast<GLint>(std::round(param));
+      return SetParameteri(feature_info, pname, iparam);
+    }
     case GL_TEXTURE_MIN_LOD:
       sampler_state_.min_lod = param;
       break;
diff --git a/ios/chrome/browser/tabs/tab.mm b/ios/chrome/browser/tabs/tab.mm
index 6bd1381b..7301b09f 100644
--- a/ios/chrome/browser/tabs/tab.mm
+++ b/ios/chrome/browser/tabs/tab.mm
@@ -1985,39 +1985,6 @@
   [delegate_ discardPrerender];
 }
 
-- (CRWWebController*)webController:(CRWWebController*)webController
-         createWebControllerForURL:(const GURL&)URL
-                         openerURL:(const GURL&)openerURL
-                   initiatedByUser:(BOOL)initiatedByUser {
-  // Check if requested web controller is a popup and block it if necessary.
-  if (!initiatedByUser) {
-    web::WebState* webState = webController.webState;
-    auto* helper = BlockedPopupTabHelper::FromWebState(webState);
-    if (helper->ShouldBlockPopup(openerURL)) {
-      web::NavigationItem* item =
-          webState->GetNavigationManager()->GetLastCommittedItem();
-      web::Referrer referrer(openerURL, item->GetReferrer().policy);
-      helper->HandlePopup(URL, referrer);
-      return nil;
-    }
-  }
-
-  // Requested web controller should not be blocked from opening.
-  [self updateSnapshotWithOverlay:YES visibleFrameOnly:YES];
-
-  // Tabs open by DOM are always renderer initiated.
-  web::NavigationManager::WebLoadParams params(GURL{});
-  params.transition_type = ui::PAGE_TRANSITION_LINK;
-  params.is_renderer_initiated = YES;
-  Tab* tab = [parentTabModel_
-      insertTabWithLoadParams:params
-                       opener:self
-                  openedByDOM:YES
-                      atIndex:TabModelConstants::kTabPositionAutomatically
-                 inBackground:NO];
-  return tab.webController;
-}
-
 - (CGFloat)headerHeightForWebController:(CRWWebController*)webController {
   return [self.tabHeadersDelegate headerHeightForTab:self];
 }
diff --git a/ios/chrome/browser/ui/browser_view_controller.mm b/ios/chrome/browser/ui/browser_view_controller.mm
index 2399ec4..a9b01c7 100644
--- a/ios/chrome/browser/ui/browser_view_controller.mm
+++ b/ios/chrome/browser/ui/browser_view_controller.mm
@@ -155,6 +155,7 @@
 #import "ios/chrome/browser/ui/util/pasteboard_util.h"
 #import "ios/chrome/browser/ui/voice/text_to_speech_player.h"
 #include "ios/chrome/browser/upgrade/upgrade_center.h"
+#import "ios/chrome/browser/web/blocked_popup_tab_helper.h"
 #import "ios/chrome/browser/web/error_page_content.h"
 #import "ios/chrome/browser/web/passkit_dialog_provider.h"
 #import "ios/chrome/browser/web/repost_form_tab_helper.h"
@@ -2382,6 +2383,39 @@
 #pragma mark - CRWWebStateDelegate methods.
 
 - (web::WebState*)webState:(web::WebState*)webState
+    createNewWebStateForURL:(const GURL&)URL
+                  openerURL:(const GURL&)openerURL
+            initiatedByUser:(BOOL)initiatedByUser {
+  // Check if requested web state is a popup and block it if necessary.
+  if (!initiatedByUser) {
+    auto* helper = BlockedPopupTabHelper::FromWebState(webState);
+    if (helper->ShouldBlockPopup(openerURL)) {
+      web::NavigationItem* item =
+          webState->GetNavigationManager()->GetLastCommittedItem();
+      web::Referrer referrer(openerURL, item->GetReferrer().policy);
+      helper->HandlePopup(URL, referrer);
+      return nil;
+    }
+  }
+
+  // Requested web state should not be blocked from opening.
+  Tab* currentTab = LegacyTabHelper::GetTabForWebState(webState);
+  [currentTab updateSnapshotWithOverlay:YES visibleFrameOnly:YES];
+
+  // Tabs open by DOM are always renderer initiated.
+  web::NavigationManager::WebLoadParams params(GURL{});
+  params.transition_type = ui::PAGE_TRANSITION_LINK;
+  params.is_renderer_initiated = true;
+  Tab* childTab = [[self tabModel]
+      insertTabWithLoadParams:params
+                       opener:currentTab
+                  openedByDOM:YES
+                      atIndex:TabModelConstants::kTabPositionAutomatically
+                 inBackground:NO];
+  return childTab.webState;
+}
+
+- (web::WebState*)webState:(web::WebState*)webState
          openURLWithParams:(const web::WebState::OpenURLParams&)params {
   switch (params.disposition) {
     case WindowOpenDisposition::NEW_FOREGROUND_TAB:
diff --git a/ios/chrome/browser/ui/history/tab_history_popup_controller_unittest.mm b/ios/chrome/browser/ui/history/tab_history_popup_controller_unittest.mm
index 6f1e9e7..e0471dd 100644
--- a/ios/chrome/browser/ui/history/tab_history_popup_controller_unittest.mm
+++ b/ios/chrome/browser/ui/history/tab_history_popup_controller_unittest.mm
@@ -12,6 +12,7 @@
 #import "ios/chrome/browser/ui/history/tab_history_view_controller.h"
 #include "ios/chrome/browser/ui/ui_util.h"
 #include "ios/web/public/navigation_item.h"
+#include "ios/web/public/navigation_item_list.h"
 #include "ios/web/public/referrer.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "testing/platform_test.h"
@@ -30,7 +31,9 @@
 
 class TabHistoryPopupControllerTest : public PlatformTest {
  protected:
-  void SetUp() override {
+  TabHistoryPopupControllerTest() : PlatformTest() {
+    parent_.reset([[UIView alloc] initWithFrame:[UIScreen mainScreen].bounds]);
+    // Create test items and populate |items_|.
     web::Referrer referrer(GURL("http://www.example.com"),
                            web::ReferrerPolicyDefault);
     items_.push_back(web::NavigationItem::Create());
@@ -40,10 +43,10 @@
     items_.back()->SetURL(GURL("http://www.example.com/1"));
     items_.back()->SetReferrer(referrer);
     items_.push_back(web::NavigationItem::Create());
-    items_.back()->SetURL(GURL("http://www.example.com/0"));
+    items_.back()->SetURL(GURL("http://www.example.com/2"));
     items_.back()->SetReferrer(referrer);
-
-    parent_.reset([[UIView alloc] initWithFrame:[UIScreen mainScreen].bounds]);
+    // Create the popup controller using CRWSessionEntries created from the
+    // NavigationItems in |items_|.
     popup_.reset([[TabHistoryPopupController alloc]
         initWithOrigin:CGPointZero
             parentView:parent_
diff --git a/ios/web/BUILD.gn b/ios/web/BUILD.gn
index 73ed084a..521701a 100644
--- a/ios/web/BUILD.gn
+++ b/ios/web/BUILD.gn
@@ -56,8 +56,6 @@
     "navigation/crw_session_controller+private_constructors.h",
     "navigation/crw_session_controller.h",
     "navigation/crw_session_controller.mm",
-    "navigation/crw_session_entry.h",
-    "navigation/crw_session_entry.mm",
     "navigation/navigation_item_facade_delegate.h",
     "navigation/navigation_item_impl.h",
     "navigation/navigation_item_impl.mm",
diff --git a/ios/web/navigation/crw_session_controller.h b/ios/web/navigation/crw_session_controller.h
index 1554fd6..6fdd181 100644
--- a/ios/web/navigation/crw_session_controller.h
+++ b/ios/web/navigation/crw_session_controller.h
@@ -8,11 +8,10 @@
 #import <Foundation/Foundation.h>
 #include <vector>
 
-#include "ios/web/public/navigation_item_list.h"
+#import "ios/web/navigation/navigation_item_impl_list.h"
 #include "ui/base/page_transition_types.h"
 #include "url/gurl.h"
 
-@class CRWSessionEntry;
 @class CRWSessionCertificatePolicyManager;
 
 namespace web {
@@ -29,7 +28,7 @@
 // DEPRECATED, do not use this class and do not add any methods to it.
 // Use web::NavigationManager instead.
 // TODO(crbug.com/454984): Remove this class.
-@interface CRWSessionController : NSObject<NSCopying>
+@interface CRWSessionController : NSObject
 
 @property(nonatomic, readonly, assign) NSInteger currentNavigationIndex;
 @property(nonatomic, readonly, assign) NSInteger previousNavigationIndex;
@@ -42,10 +41,9 @@
 @property(nonatomic, readonly, strong)
     CRWSessionCertificatePolicyManager* sessionCertificatePolicyManager;
 
-// The list of CRWSessionEntries in |_entries|'s NavigationItemImpls.
-@property(nonatomic, readonly) web::NavigationItemList items;
-// The number of elements in |self.items|.
-@property(nonatomic, readonly) NSUInteger itemCount;
+// The ScopedNavigationItemImplList used to store the NavigationItemImpls for
+// this session.
+@property(nonatomic, readonly) const web::ScopedNavigationItemImplList& items;
 // The current NavigationItem.  During a pending navigation, returns the
 // NavigationItem for that navigation.  If a transient NavigationItem exists,
 // this NavigationItem will be returned.
@@ -55,8 +53,9 @@
 // Returns the NavigationItem corresponding to a load for which no data has yet
 // been received.
 @property(nonatomic, readonly) web::NavigationItemImpl* pendingItem;
-// Returns the NavigationItem corresponding with a transient navigation (i.e.
-// SSL interstitials).
+// Returns the transient NavigationItem, if any.  The transient item will be
+// discarded on any navigation, and is used to represent interstitials in the
+// session history.
 @property(nonatomic, readonly) web::NavigationItemImpl* transientItem;
 // Returns the NavigationItem corresponding with the last committed load.
 @property(nonatomic, readonly) web::NavigationItemImpl* lastCommittedItem;
@@ -72,19 +71,6 @@
 // |currentNavigationIndex|.
 @property(nonatomic, readonly) web::NavigationItemList forwardItems;
 
-// DEPRECATED: Don't add new usage of these properties.  Instead, use the
-// NavigationItem versions of these properties above.
-@property(nonatomic, readonly, strong) NSArray* entries;
-@property(nonatomic, readonly, strong) CRWSessionEntry* currentEntry;
-@property(nonatomic, readonly, strong) CRWSessionEntry* visibleEntry;
-@property(nonatomic, readonly, strong) CRWSessionEntry* pendingEntry;
-@property(nonatomic, readonly, strong) CRWSessionEntry* transientEntry;
-@property(nonatomic, readonly, strong) CRWSessionEntry* lastCommittedEntry;
-@property(nonatomic, readonly, strong) CRWSessionEntry* previousEntry;
-@property(nonatomic, readonly, strong) CRWSessionEntry* lastUserEntry;
-@property(nonatomic, readonly, weak) NSArray* backwardEntries;
-@property(nonatomic, readonly, weak) NSArray* forwardEntries;
-
 // CRWSessionController doesn't have public constructors. New
 // CRWSessionControllers are created by deserialization, or via a
 // NavigationManager.
@@ -118,9 +104,9 @@
 // be made from outside and then handed in.
 - (void)addTransientItemWithURL:(const GURL&)URL;
 
-// Creates a new CRWSessionEntry with the given URL and state object. A state
+// Creates a new NavigationItem with the given URL and state object. A state
 // object is a serialized generic JavaScript object that contains details of the
-// UI's state for a given CRWSessionEntry/URL. The current item's URL is the
+// UI's state for a given NavigationItem/URL. The current item's URL is the
 // new item's referrer.
 - (void)pushNewItemWithURL:(const GURL&)URL
                stateObject:(NSString*)stateObject
@@ -134,8 +120,7 @@
 - (void)discardNonCommittedItems;
 
 // Inserts history state from |otherController| to the front of |items|.  This
-// function transfers ownership of |otherController|'s NavigationItems to the
-// receiver.
+// function will create copies of |otherController|'s NavigationItems.
 - (void)insertStateFromSessionController:(CRWSessionController*)otherController;
 
 // Sets |currentNavigationIndex_| to the |index| if it's in the entries bounds.
diff --git a/ios/web/navigation/crw_session_controller.mm b/ios/web/navigation/crw_session_controller.mm
index 351320b..1bb37874 100644
--- a/ios/web/navigation/crw_session_controller.mm
+++ b/ios/web/navigation/crw_session_controller.mm
@@ -8,17 +8,16 @@
 
 #include <algorithm>
 #include <utility>
-#include <vector>
 
 #include "base/format_macros.h"
 #include "base/logging.h"
 #import "base/mac/foundation_util.h"
 #import "base/mac/scoped_nsobject.h"
+#include "base/memory/ptr_util.h"
 #include "base/strings/sys_string_conversions.h"
 #include "ios/web/history_state_util.h"
 #import "ios/web/navigation/crw_session_certificate_policy_manager.h"
 #import "ios/web/navigation/crw_session_controller+private_constructors.h"
-#import "ios/web/navigation/crw_session_entry.h"
 #import "ios/web/navigation/navigation_item_impl.h"
 #include "ios/web/navigation/navigation_manager_facade_delegate.h"
 #import "ios/web/navigation/navigation_manager_impl.h"
@@ -38,27 +37,12 @@
   // the incremental merging of the two classes.
   web::NavigationManagerImpl* _navigationManager;
 
-  // Identifies the index of the current navigation in the CRWSessionEntry
+  // Identifies the index of the current navigation in the NavigationItem
   // array.
   NSInteger _currentNavigationIndex;
-  // Identifies the index of the previous navigation in the CRWSessionEntry
+  // Identifies the index of the previous navigation in the NavigationItem
   // array.
   NSInteger _previousNavigationIndex;
-  // Ordered array of |CRWSessionEntry| objects, one for each site in session
-  // history. End of the list is the most recent load.
-  NSMutableArray* _entries;
-
-  // An entry we haven't gotten a response for yet. This will be discarded
-  // when we navigate again. It's used only so we know what the currently
-  // displayed tab is.  It backs the property of the same name and should only
-  // be set through its setter.
-  base::scoped_nsobject<CRWSessionEntry> _pendingEntry;
-
-  // The transient entry, if any. A transient entry is discarded on any
-  // navigation, and is used for representing interstitials that need to be
-  // represented in the session.  It backs the property of the same name and
-  // should only be set through its setter.
-  base::scoped_nsobject<CRWSessionEntry> _transientEntry;
 
    // Stores the certificate policies decided by the user.
   CRWSessionCertificatePolicyManager* _sessionCertificatePolicyManager;
@@ -66,9 +50,18 @@
   // The browser state associated with this CRWSessionController;
   web::BrowserState* _browserState;  // weak
 
-  // Time smoother for navigation entry timestamps; see comment in
+  // Time smoother for navigation item timestamps; see comment in
   // navigation_controller_impl.h
   web::TimeSmoother _timeSmoother;
+
+  // Backing objects for properties of the same name.
+  web::ScopedNavigationItemImplList _items;
+  // |_pendingItem| only contains a NavigationItem for non-history navigations.
+  // For back/forward navigations within session history, _pendingItemIndex will
+  // be equal to -1, and self.pendingItem will return an item contained within
+  // |_items|.
+  std::unique_ptr<web::NavigationItemImpl> _pendingItem;
+  std::unique_ptr<web::NavigationItemImpl> _transientItem;
 }
 
 // Redefine as readwrite.
@@ -76,7 +69,6 @@
 
 // TODO(rohitrao): These properties must be redefined readwrite to work around a
 // clang bug. crbug.com/228650
-@property(nonatomic, readwrite, strong) NSArray* entries;
 @property(nonatomic, readwrite, strong)
     CRWSessionCertificatePolicyManager* sessionCertificatePolicyManager;
 
@@ -86,21 +78,20 @@
 @property(nonatomic, readwrite, getter=isOpenedByDOM) BOOL openedByDOM;
 @property(nonatomic, readwrite, assign) NSInteger previousNavigationIndex;
 
-// Removes all entries after currentNavigationIndex_.
+// Removes all items after currentNavigationIndex_.
 - (void)clearForwardItems;
-// Discards the transient entry, if any.
+// Discards the transient item, if any.
 - (void)discardTransientItem;
-// Create a new autoreleased session entry.
-- (CRWSessionEntry*)sessionEntryWithURL:(const GURL&)url
-                               referrer:(const web::Referrer&)referrer
-                             transition:(ui::PageTransition)transition
-                         initiationType:
-                             (web::NavigationInitiationType)initiationType;
+// Creates a NavigationItemImpl with the specified properties.
+- (std::unique_ptr<web::NavigationItemImpl>)
+   itemWithURL:(const GURL&)url
+      referrer:(const web::Referrer&)referrer
+    transition:(ui::PageTransition)transition
+initiationType:(web::NavigationInitiationType)initiationType;
 // Returns YES if the PageTransition for the underlying navigationItem at
-// |index| in |entries_| has ui::PAGE_TRANSITION_IS_REDIRECT_MASK.
-- (BOOL)isRedirectTransitionForItemAtIndex:(NSInteger)index;
-// Returns a NavigationItemList containing the NavigationItems from |entries|.
-- (web::NavigationItemList)itemListForEntryList:(NSArray*)entries;
+// |index| in |items| has ui::PAGE_TRANSITION_IS_REDIRECT_MASK.
+- (BOOL)isRedirectTransitionForItemAtIndex:(size_t)index;
+
 @end
 
 @implementation CRWSessionController
@@ -108,7 +99,6 @@
 @synthesize currentNavigationIndex = _currentNavigationIndex;
 @synthesize previousNavigationIndex = _previousNavigationIndex;
 @synthesize pendingItemIndex = _pendingItemIndex;
-@synthesize entries = _entries;
 @synthesize openedByDOM = _openedByDOM;
 @synthesize sessionCertificatePolicyManager = _sessionCertificatePolicyManager;
 
@@ -118,7 +108,6 @@
   if (self) {
     _openedByDOM = openedByDOM;
     _browserState = browserState;
-    _entries = [NSMutableArray array];
     _currentNavigationIndex = -1;
     _previousNavigationIndex = -1;
     _pendingItemIndex = -1;
@@ -134,21 +123,10 @@
   self = [super init];
   if (self) {
     _browserState = browserState;
-
-    // Create entries array from list of navigations.
-    _entries = [[NSMutableArray alloc] initWithCapacity:items.size()];
-    for (auto& item : items) {
-      base::scoped_nsobject<CRWSessionEntry> entry(
-          [[CRWSessionEntry alloc] initWithNavigationItem:std::move(item)]);
-      [_entries addObject:entry];
-    }
-    self.currentNavigationIndex = currentIndex;
-    // Prior to M34, 0 was used as "no index" instead of -1; adjust for that.
-    if (![_entries count])
-      self.currentNavigationIndex = -1;
-    if (_currentNavigationIndex >= static_cast<NSInteger>(items.size())) {
-      self.currentNavigationIndex = static_cast<NSInteger>(items.size()) - 1;
-    }
+    _items = web::CreateScopedNavigationItemImplList(std::move(items));
+    _currentNavigationIndex =
+        std::min(static_cast<NSInteger>(currentIndex),
+                 static_cast<NSInteger>(_items.size()) - 1);
     _previousNavigationIndex = -1;
     _pendingItemIndex = -1;
     _sessionCertificatePolicyManager =
@@ -157,18 +135,7 @@
   return self;
 }
 
-- (id)copyWithZone:(NSZone*)zone {
-  CRWSessionController* copy = [[[self class] alloc] init];
-  copy->_openedByDOM = _openedByDOM;
-  copy->_currentNavigationIndex = _currentNavigationIndex;
-  copy->_previousNavigationIndex = _previousNavigationIndex;
-  copy->_pendingItemIndex = _pendingItemIndex;
-  copy->_entries =
-      [[NSMutableArray alloc] initWithArray:_entries copyItems:YES];
-  copy->_sessionCertificatePolicyManager =
-      [_sessionCertificatePolicyManager copy];
-  return copy;
-}
+#pragma mark - Accessors
 
 - (void)setCurrentNavigationIndex:(NSInteger)currentNavigationIndex {
   if (_currentNavigationIndex != currentNavigationIndex) {
@@ -178,15 +145,137 @@
   }
 }
 
-- (void)setPendingItemIndex:(NSInteger)index {
-  DCHECK_GE(index, -1);
-  DCHECK_LT(index, static_cast<NSInteger>(_entries.count));
-  _pendingItemIndex = index;
-  CRWSessionEntry* entry = index != -1 ? _entries[index] : nil;
-  _pendingEntry.reset(entry);
-  DCHECK(_pendingItemIndex == -1 || _pendingEntry);
+- (void)setPendingItemIndex:(NSInteger)pendingItemIndex {
+  DCHECK_GE(pendingItemIndex, -1);
+  DCHECK_LT(pendingItemIndex, static_cast<NSInteger>(self.items.size()));
+  _pendingItemIndex = pendingItemIndex;
+  DCHECK(_pendingItemIndex == -1 || self.pendingItem);
 }
 
+- (const web::ScopedNavigationItemImplList&)items {
+  return _items;
+}
+
+- (web::NavigationItemImpl*)currentItem {
+  if (self.transientItem)
+    return self.transientItem;
+  if (self.pendingItem)
+    return self.pendingItem;
+  return self.lastCommittedItem;
+}
+
+- (web::NavigationItemImpl*)visibleItem {
+  if (self.transientItem)
+    return self.transientItem;
+  // Only return the |pendingItem| for new (non-history), browser-initiated
+  // navigations in order to prevent URL spoof attacks.
+  web::NavigationItemImpl* pendingItem = self.pendingItem;
+  if (pendingItem) {
+    bool isUserInitiated = pendingItem->NavigationInitiationType() ==
+                           web::NavigationInitiationType::USER_INITIATED;
+    bool safeToShowPending = isUserInitiated && _pendingItemIndex == -1;
+
+    if (safeToShowPending)
+      return pendingItem;
+  }
+  return self.lastCommittedItem;
+}
+
+- (web::NavigationItemImpl*)pendingItem {
+  if (self.pendingItemIndex == -1)
+    return _pendingItem.get();
+  return self.items[self.pendingItemIndex].get();
+}
+
+- (web::NavigationItemImpl*)transientItem {
+  return _transientItem.get();
+}
+
+- (web::NavigationItemImpl*)lastCommittedItem {
+  NSInteger index = self.currentNavigationIndex;
+  return index == -1 ? nullptr : self.items[index].get();
+}
+
+- (web::NavigationItemImpl*)previousItem {
+  NSInteger index = self.previousNavigationIndex;
+  return index == -1 || self.items.empty() ? nullptr : self.items[index].get();
+}
+
+- (web::NavigationItemImpl*)lastUserItem {
+  if (self.items.empty())
+    return nil;
+
+  NSInteger index = self.currentNavigationIndex;
+  // This will return the first NavigationItem if all other items are
+  // redirects, regardless of the transition state of the first item.
+  while (index > 0 && [self isRedirectTransitionForItemAtIndex:index])
+    --index;
+
+  return self.items[index].get();
+}
+
+- (web::NavigationItemList)backwardItems {
+  web::NavigationItemList items;
+  for (size_t index = _currentNavigationIndex; index > 0; --index) {
+    if (![self isRedirectTransitionForItemAtIndex:index])
+      items.push_back(self.items[index - 1].get());
+  }
+  return items;
+}
+
+- (web::NavigationItemList)forwardItems {
+  web::NavigationItemList items;
+  NSUInteger lastNonRedirectedIndex = _currentNavigationIndex + 1;
+  while (lastNonRedirectedIndex < self.items.size()) {
+    web::NavigationItem* item = self.items[lastNonRedirectedIndex].get();
+    if (!ui::PageTransitionIsRedirect(item->GetTransitionType()))
+      items.push_back(item);
+    ++lastNonRedirectedIndex;
+  }
+  return items;
+}
+
+#pragma mark - NSObject
+
+- (NSString*)description {
+  // Create description for |items|.
+  NSMutableString* itemsDescription = [NSMutableString stringWithString:@"[\n"];
+#ifndef NDEBUG
+  for (const auto& item : self.items)
+    [itemsDescription appendFormat:@"%@\n", item->GetDescription()];
+#endif
+  [itemsDescription appendString:@"]"];
+  // Create description for |pendingItem| and |transientItem|.
+  NSString* pendingItemDescription = @"(null)";
+  NSString* transientItemDescription = @"(null)";
+#ifndef NDEBUG
+  if (self.pendingItem)
+    pendingItemDescription = self.pendingItem->GetDescription();
+  if (self.transientItem)
+    transientItemDescription = self.transientItem->GetDescription();
+#else
+  if (self.pendingItem) {
+    pendingItemDescription =
+        [NSString stringWithFormat:@"%p", self.pendingItem];
+  }
+  if (self.transientItem) {
+    transientItemDescription =
+        [NSString stringWithFormat:@"%p", self.transientItem];
+  }
+#endif
+  return [NSString stringWithFormat:@"current index: %" PRIdNS
+                                    @"\nprevious index: %" PRIdNS
+                                    @"\npending"
+                                    @" index: %" PRIdNS
+                                    @"\n%@\npending: %@\ntransient: %@\n",
+                                    _currentNavigationIndex,
+                                    _previousNavigationIndex, _pendingItemIndex,
+                                    itemsDescription, pendingItemDescription,
+                                    transientItemDescription];
+}
+
+#pragma mark - Public
+
 - (void)setNavigationManager:(web::NavigationManagerImpl*)navigationManager {
   _navigationManager = navigationManager;
   if (_navigationManager) {
@@ -206,121 +295,14 @@
          _navigationManager->GetBrowserState() == _browserState);
 }
 
-- (NSString*)description {
-  return [NSString stringWithFormat:@"current index: %" PRIdNS
-                                    @"\nprevious index: %" PRIdNS
-                                    @"\npending index: %" PRIdNS
-                                    @"\n%@\npending: %@\ntransient: %@\n",
-                                    _currentNavigationIndex,
-                                    _previousNavigationIndex, _pendingItemIndex,
-                                    _entries, _pendingEntry.get(),
-                                    _transientEntry.get()];
-}
-
-- (web::NavigationItemList)items {
-  return [self itemListForEntryList:self.entries];
-}
-
-- (NSUInteger)itemCount {
-  return self.entries.count;
-}
-
-- (web::NavigationItemImpl*)currentItem {
-  return self.currentEntry.navigationItemImpl;
-}
-
-- (web::NavigationItemImpl*)visibleItem {
-  return self.visibleEntry.navigationItemImpl;
-}
-
-- (web::NavigationItemImpl*)pendingItem {
-  return self.pendingEntry.navigationItemImpl;
-}
-
-- (web::NavigationItemImpl*)transientItem {
-  return self.transientEntry.navigationItemImpl;
-}
-
-- (web::NavigationItemImpl*)lastCommittedItem {
-  return self.lastCommittedEntry.navigationItemImpl;
-}
-
-- (web::NavigationItemImpl*)previousItem {
-  return self.previousEntry.navigationItemImpl;
-}
-
-- (web::NavigationItemImpl*)lastUserItem {
-  return self.lastUserEntry.navigationItemImpl;
-}
-
-- (web::NavigationItemList)backwardItems {
-  return [self itemListForEntryList:self.backwardEntries];
-}
-
-- (web::NavigationItemList)forwardItems {
-  return [self itemListForEntryList:self.forwardEntries];
-}
-
-// Returns the current entry in the session list, or the pending entry if there
-// is a navigation in progress.
-- (CRWSessionEntry*)currentEntry {
-  if (_transientEntry)
-    return _transientEntry.get();
-  if (_pendingEntry)
-    return _pendingEntry.get();
-  return [self lastCommittedEntry];
-}
-
-// See NavigationController::GetVisibleEntry for the motivation for this
-// distinction.
-- (CRWSessionEntry*)visibleEntry {
-  if (_transientEntry)
-    return _transientEntry.get();
-  // Only return the pending_entry for new (non-history), browser-initiated
-  // navigations in order to prevent URL spoof attacks.
-  web::NavigationItemImpl* pendingItem = [_pendingEntry navigationItemImpl];
-
-  if (pendingItem) {
-    bool isUserInitiated = pendingItem->NavigationInitiationType() ==
-                           web::NavigationInitiationType::USER_INITIATED;
-    bool safeToShowPending = isUserInitiated && _pendingItemIndex == -1;
-
-    if (safeToShowPending)
-      return _pendingEntry.get();
-  }
-
-  return [self lastCommittedEntry];
-}
-
-- (CRWSessionEntry*)pendingEntry {
-  return _pendingEntry.get();
-}
-
-- (CRWSessionEntry*)transientEntry {
-  return _transientEntry.get();
-}
-
-- (CRWSessionEntry*)lastCommittedEntry {
-  if (_currentNavigationIndex == -1)
-    return nil;
-  return [_entries objectAtIndex:_currentNavigationIndex];
-}
-
-// Returns the previous entry in the session list, or nil if there isn't any.
-- (CRWSessionEntry*)previousEntry {
-  if ((_previousNavigationIndex < 0) || (![_entries count]))
-    return nil;
-  return [_entries objectAtIndex:_previousNavigationIndex];
-}
-
 - (void)addPendingItem:(const GURL&)url
               referrer:(const web::Referrer&)ref
             transition:(ui::PageTransition)trans
         initiationType:(web::NavigationInitiationType)initiationType {
   [self discardTransientItem];
-  _pendingItemIndex = -1;
+  self.pendingItemIndex = -1;
 
-  // Don't create a new entry if it's already the same as the current entry,
+  // Don't create a new item if it's already the same as the current item,
   // allowing this routine to be called multiple times in a row without issue.
   // Note: CRWSessionController currently has the responsibility to distinguish
   // between new navigations and history stack navigation, hence the inclusion
@@ -329,15 +311,13 @@
   // TODO(crbug.com/676129): Fix the way changes are detected/reported elsewhere
   // in the web layer so that this hack can be removed.
   // Remove the workaround code from -presentSafeBrowsingWarningForResource:.
-  CRWSessionEntry* currentEntry = self.currentEntry;
-  if (currentEntry) {
-    web::NavigationItem* item = [currentEntry navigationItem];
-
-    BOOL hasSameURL = item->GetURL() == url;
+  web::NavigationItemImpl* currentItem = self.currentItem;
+  if (currentItem) {
+    BOOL hasSameURL = currentItem->GetURL() == url;
     BOOL isPendingTransitionFormSubmit =
         PageTransitionCoreTypeIs(trans, ui::PAGE_TRANSITION_FORM_SUBMIT);
     BOOL isCurrentTransitionFormSubmit = PageTransitionCoreTypeIs(
-        item->GetTransitionType(), ui::PAGE_TRANSITION_FORM_SUBMIT);
+        currentItem->GetTransitionType(), ui::PAGE_TRANSITION_FORM_SUBMIT);
     BOOL shouldCreatePendingItem =
         !hasSameURL ||
         (isPendingTransitionFormSubmit && !isCurrentTransitionFormSubmit);
@@ -347,34 +327,32 @@
       // whether anything currently relies on this, but since both this whole
       // hack and the content facade will both be going away, it's not worth
       // trying to unwind.
-      if (_navigationManager && _navigationManager->GetFacadeDelegate()) {
+      if (_navigationManager && _navigationManager->GetFacadeDelegate())
         _navigationManager->GetFacadeDelegate()->OnNavigationItemPending();
-      }
       return;
     }
   }
 
-  _pendingEntry.reset([self sessionEntryWithURL:url
-                                       referrer:ref
-                                     transition:trans
-                                 initiationType:initiationType]);
+  _pendingItem = [self itemWithURL:url
+                          referrer:ref
+                        transition:trans
+                    initiationType:initiationType];
 
-  if (_navigationManager && _navigationManager->GetFacadeDelegate()) {
+  if (_navigationManager && _navigationManager->GetFacadeDelegate())
     _navigationManager->GetFacadeDelegate()->OnNavigationItemPending();
-  }
 }
 
 - (void)updatePendingItem:(const GURL&)url {
-  // If there is no pending entry, navigation is probably happening within the
-  // session history. Don't modify the entry list.
-  if (!_pendingEntry)
+  // If there is no pending item, navigation is probably happening within the
+  // session history. Don't modify the item list.
+  web::NavigationItemImpl* item = self.pendingItem;
+  if (!item)
     return;
 
-  web::NavigationItemImpl* item = [_pendingEntry navigationItemImpl];
   if (url != item->GetURL()) {
-    // Assume a redirection, and discard any transient entry.
+    // Assume a redirection, and discard any transient item.
     // TODO(stuartmorgan): Once the current safe browsing code is gone,
-    // consider making this a DCHECK that there's no transient entry.
+    // consider making this a DCHECK that there's no transient item.
     [self discardTransientItem];
 
     item->SetURL(url);
@@ -387,97 +365,88 @@
 
   // This should probably not be sent if the URLs matched, but that's what was
   // done before, so preserve behavior in case something relies on it.
-  if (_navigationManager && _navigationManager->GetFacadeDelegate()) {
+  if (_navigationManager && _navigationManager->GetFacadeDelegate())
     _navigationManager->GetFacadeDelegate()->OnNavigationItemPending();
-  }
 }
 
 - (void)clearForwardItems {
-  DCHECK_EQ(_pendingItemIndex, -1);
+  DCHECK_EQ(self.pendingItemIndex, -1);
   [self discardTransientItem];
 
   NSInteger forwardItemStartIndex = _currentNavigationIndex + 1;
   DCHECK(forwardItemStartIndex >= 0);
 
-  if (forwardItemStartIndex >= static_cast<NSInteger>([_entries count]))
+  size_t itemCount = self.items.size();
+  if (forwardItemStartIndex >= static_cast<NSInteger>(itemCount))
     return;
 
-  NSRange remove = NSMakeRange(forwardItemStartIndex,
-                               [_entries count] - forwardItemStartIndex);
-  // Store removed items in temporary NSArray so they can be deallocated after
-  // their facades.
-  base::scoped_nsobject<NSArray> removedItems(
-      [_entries subarrayWithRange:remove]);
-  [_entries removeObjectsInRange:remove];
   if (_previousNavigationIndex >= forwardItemStartIndex)
     _previousNavigationIndex = -1;
+
+  // Remove the NavigationItems and notify the NavigationManater
+  _items.erase(_items.begin() + forwardItemStartIndex, _items.end());
   if (_navigationManager) {
-    _navigationManager->OnNavigationItemsPruned(remove.length);
+    _navigationManager->OnNavigationItemsPruned(itemCount -
+                                                forwardItemStartIndex);
   }
 }
 
 - (void)commitPendingItem {
-  if (_pendingEntry) {
-    NSInteger newNavigationIndex = _pendingItemIndex;
-    if (_pendingItemIndex == -1) {
+  if (self.pendingItem) {
+    // Once an item is committed it's not renderer-initiated any more. (Matches
+    // the implementation in NavigationController.)
+    self.pendingItem->ResetForCommit();
+
+    NSInteger newNavigationIndex = self.pendingItemIndex;
+    if (newNavigationIndex == -1) {
       [self clearForwardItems];
-      // Add the new entry at the end.
-      [_entries addObject:_pendingEntry];
-      newNavigationIndex = [_entries count] - 1;
+      // Add the new item at the end.
+      _items.push_back(std::move(_pendingItem));
+      newNavigationIndex = self.items.size() - 1;
     }
     _previousNavigationIndex = _currentNavigationIndex;
     self.currentNavigationIndex = newNavigationIndex;
-    // Once an entry is committed it's not renderer-initiated any more. (Matches
-    // the implementation in NavigationController.)
-    [_pendingEntry navigationItemImpl]->ResetForCommit();
-    _pendingEntry.reset();
-    _pendingItemIndex = -1;
+    self.pendingItemIndex = -1;
+    DCHECK(!_pendingItem);
   }
 
-  CRWSessionEntry* currentEntry = self.currentEntry;
-  web::NavigationItem* item = currentEntry.navigationItem;
+  web::NavigationItem* item = self.currentItem;
   // Update the navigation timestamp now that it's actually happened.
   if (item)
     item->SetTimestamp(_timeSmoother.GetSmoothedTime(base::Time::Now()));
 
   if (_navigationManager && item)
     _navigationManager->OnNavigationItemCommitted();
-  DCHECK_EQ(_pendingItemIndex, -1);
+  DCHECK_EQ(self.pendingItemIndex, -1);
 }
 
 - (void)addTransientItemWithURL:(const GURL&)URL {
-  _transientEntry.reset([self
-      sessionEntryWithURL:URL
-                 referrer:web::Referrer()
-               transition:ui::PAGE_TRANSITION_CLIENT_REDIRECT
-           initiationType:web::NavigationInitiationType::USER_INITIATED]);
-
-  web::NavigationItem* navigationItem = [_transientEntry navigationItem];
-  DCHECK(navigationItem);
-  navigationItem->SetTimestamp(
+  _transientItem =
+      [self itemWithURL:URL
+                referrer:web::Referrer()
+              transition:ui::PAGE_TRANSITION_CLIENT_REDIRECT
+          initiationType:web::NavigationInitiationType::USER_INITIATED];
+  _transientItem->SetTimestamp(
       _timeSmoother.GetSmoothedTime(base::Time::Now()));
 }
 
 - (void)pushNewItemWithURL:(const GURL&)URL
                stateObject:(NSString*)stateObject
                 transition:(ui::PageTransition)transition {
-  DCHECK(![self pendingEntry]);
-  DCHECK([self currentEntry]);
+  DCHECK(!self.pendingItem);
+  DCHECK(self.currentItem);
 
-  web::NavigationItem* lastCommittedItem =
-      self.lastCommittedEntry.navigationItem;
+  web::NavigationItem* lastCommittedItem = self.lastCommittedItem;
   CHECK(web::history_state_util::IsHistoryStateChangeValid(
       lastCommittedItem->GetURL(), URL));
 
   web::Referrer referrer(lastCommittedItem->GetURL(),
                          web::ReferrerPolicyDefault);
-  base::scoped_nsobject<CRWSessionEntry> pushedEntry([self
-      sessionEntryWithURL:URL
-                 referrer:referrer
-               transition:transition
-           initiationType:web::NavigationInitiationType::USER_INITIATED]);
-
-  web::NavigationItemImpl* pushedItem = [pushedEntry navigationItemImpl];
+  std::unique_ptr<web::NavigationItemImpl> pushedItem =
+      [self itemWithURL:URL
+                referrer:referrer
+              transition:transition
+          initiationType:web::NavigationInitiationType::USER_INITIATED];
   pushedItem->SetUserAgentType(lastCommittedItem->GetUserAgentType());
   pushedItem->SetSerializedStateObject(stateObject);
   pushedItem->SetIsCreatedFromPushState(true);
@@ -485,10 +454,10 @@
   pushedItem->SetTimestamp(_timeSmoother.GetSmoothedTime(base::Time::Now()));
 
   [self clearForwardItems];
-  // Add the new entry at the end.
-  [_entries addObject:pushedEntry];
+  // Add the new item at the end.
+  _items.push_back(std::move(pushedItem));
   _previousNavigationIndex = _currentNavigationIndex;
-  self.currentNavigationIndex = [_entries count] - 1;
+  self.currentNavigationIndex = self.items.size() - 1;
 
   if (_navigationManager)
     _navigationManager->OnNavigationItemCommitted();
@@ -496,62 +465,60 @@
 
 - (void)updateCurrentItemWithURL:(const GURL&)url
                      stateObject:(NSString*)stateObject {
-  DCHECK(!_transientEntry);
-  CRWSessionEntry* currentEntry = self.currentEntry;
-  web::NavigationItemImpl* currentItem = self.currentEntry.navigationItemImpl;
+  DCHECK(!self.transientItem);
+  web::NavigationItemImpl* currentItem = self.currentItem;
   currentItem->SetURL(url);
   currentItem->SetSerializedStateObject(stateObject);
   currentItem->SetHasStateBeenReplaced(true);
   currentItem->SetPostData(nil);
-  currentEntry.navigationItem->SetURL(url);
-  // If the change is to a committed entry, notify interested parties.
-  if (currentEntry != self.pendingEntry && _navigationManager)
+  // If the change is to a committed item, notify interested parties.
+  if (currentItem != self.pendingItem && _navigationManager)
     _navigationManager->OnNavigationItemChanged();
 }
 
 - (void)discardNonCommittedItems {
   [self discardTransientItem];
-  _pendingEntry.reset();
-  _pendingItemIndex = -1;
+  _pendingItem.reset();
+  self.pendingItemIndex = -1;
 }
 
 - (void)discardTransientItem {
-  // Keep the entry alive temporarily. There are flows that get the current
-  // entry, do some navigation operation, and then try to use that old current
-  // entry; since navigations clear the transient entry, these flows might
-  // crash. (This should be removable once more session management is handled
-  // within this class and/or NavigationManager).
-  _transientEntry.reset();
+  _transientItem.reset();
 }
 
 - (void)insertStateFromSessionController:(CRWSessionController*)sourceSession {
   DCHECK(sourceSession);
-  // The other session may not have any entries, in which case there is nothing
-  // to insert.  The other session's currentNavigationEntry will be bogus
-  // in such cases, so ignore it and return early.
-  NSArray* sourceEntries = sourceSession.entries;
-  if (!sourceEntries.count)
+
+  // The other session may not have any items, in which case there is nothing
+  // to insert.  The other session's currentItem will be bogus in such cases, so
+  // ignore it and return early.
+  web::ScopedNavigationItemImplList& sourceItems = sourceSession->_items;
+  if (sourceItems.empty())
     return;
 
-  // Cycle through the entries from the other session and insert them before any
-  // entries from this session.  Do not copy anything that comes after the other
-  // session's current entry.
+  // Cycle through the items from the other session and insert them before any
+  // items from this session.  Do not copy anything that comes after the other
+  // session's current item.
   NSInteger lastIndexToCopy = sourceSession.currentNavigationIndex;
   for (NSInteger i = 0; i <= lastIndexToCopy; ++i) {
-    [_entries insertObject:sourceEntries[i] atIndex:i];
+    std::unique_ptr<web::NavigationItemImpl> sourceItemCopy =
+        base::MakeUnique<web::NavigationItemImpl>(*sourceItems[i]);
+    _items.insert(_items.begin() + i, std::move(sourceItemCopy));
   }
 
+  // Update state to reflect inserted NavigationItems.
   _previousNavigationIndex = -1;
   _currentNavigationIndex += lastIndexToCopy + 1;
-  if (_pendingItemIndex != -1)
-    _pendingItemIndex += lastIndexToCopy + 1;
+  if (self.pendingItemIndex != -1)
+    self.pendingItemIndex += lastIndexToCopy + 1;
 
-  DCHECK_LT(static_cast<NSUInteger>(_currentNavigationIndex), _entries.count);
-  DCHECK(_pendingItemIndex == -1 || _pendingEntry);
+  DCHECK_LT(static_cast<NSUInteger>(_currentNavigationIndex),
+            self.items.size());
+  DCHECK(self.pendingItemIndex == -1 || self.pendingItem);
 }
 
 - (void)goToItemAtIndex:(NSInteger)index {
-  if (index < 0 || static_cast<NSUInteger>(index) >= _entries.count)
+  if (index < 0 || static_cast<NSUInteger>(index) >= self.items.size())
     return;
 
   if (index < _currentNavigationIndex) {
@@ -570,42 +537,19 @@
 }
 
 - (void)removeItemAtIndex:(NSInteger)index {
-  DCHECK(index < static_cast<NSInteger>([_entries count]));
+  DCHECK(index < static_cast<NSInteger>(self.items.size()));
   DCHECK(index != _currentNavigationIndex);
   DCHECK(index >= 0);
 
   [self discardNonCommittedItems];
 
-  [_entries removeObjectAtIndex:index];
+  _items.erase(_items.begin() + index);
   if (_currentNavigationIndex > index)
     _currentNavigationIndex--;
   if (_previousNavigationIndex >= index)
     _previousNavigationIndex--;
 }
 
-- (NSArray*)backwardEntries {
-  NSMutableArray* entries = [NSMutableArray array];
-  for (NSInteger index = _currentNavigationIndex; index > 0; --index) {
-    if (![self isRedirectTransitionForItemAtIndex:index])
-      [entries addObject:_entries[index - 1]];
-  }
-  return entries;
-}
-
-- (NSArray*)forwardEntries {
-  NSMutableArray* entries = [NSMutableArray array];
-  NSUInteger lastNonRedirectedIndex = _currentNavigationIndex + 1;
-  while (lastNonRedirectedIndex < [_entries count]) {
-    CRWSessionEntry* entry = [_entries objectAtIndex:lastNonRedirectedIndex];
-    if (!ui::PageTransitionIsRedirect(
-            entry.navigationItem->GetTransitionType())) {
-      [entries addObject:entry];
-    }
-    ++lastNonRedirectedIndex;
-  }
-  return entries;
-}
-
 - (BOOL)isSameDocumentNavigationBetweenItem:(web::NavigationItem*)firstItem
                                     andItem:(web::NavigationItem*)secondItem {
   if (!firstItem || !secondItem || firstItem == secondItem)
@@ -618,12 +562,12 @@
   NSUInteger endIndex = firstIndex < secondIndex ? secondIndex : firstIndex;
 
   for (NSUInteger i = startIndex + 1; i <= endIndex; i++) {
-    web::NavigationItemImpl* item = [_entries[i] navigationItemImpl];
-    // Every entry in the sequence has to be created from a hash change or
+    web::NavigationItemImpl* item = self.items[i].get();
+    // Every item in the sequence has to be created from a hash change or
     // pushState() call.
     if (!item->IsCreatedFromPushState() && !item->IsCreatedFromHashChange())
       return NO;
-    // Every entry in the sequence has to have a URL that could have been
+    // Every item in the sequence has to have a URL that could have been
     // created from a pushState() call.
     if (!web::history_state_util::IsHistoryStateChangeValid(firstItem->GetURL(),
                                                             item->GetURL()))
@@ -632,42 +576,29 @@
   return YES;
 }
 
-- (CRWSessionEntry*)lastUserEntry {
-  if (![_entries count])
-    return nil;
-
-  NSInteger index = _currentNavigationIndex;
-  // This will return the first session entry if all other entries are
-  // redirects, regardless of the transition state of the first entry.
-  while (index > 0 && [self isRedirectTransitionForItemAtIndex:index]) {
-    --index;
-  }
-  return [_entries objectAtIndex:index];
-}
-
 - (NSInteger)indexOfItem:(const web::NavigationItem*)item {
-  for (NSUInteger index = 0; index < self.entries.count; ++index) {
-    if ([self.entries[index] navigationItem] == item)
-      return static_cast<NSInteger>(index);
+  DCHECK(item);
+  for (size_t index = 0; index < self.items.size(); ++index) {
+    if (self.items[index].get() == item)
+      return index;
   }
   return NSNotFound;
 }
 
 - (web::NavigationItemImpl*)itemAtIndex:(NSInteger)index {
-  if (index < 0 || self.entries.count <= static_cast<NSUInteger>(index))
+  if (index < 0 || self.items.size() <= static_cast<NSUInteger>(index))
     return nullptr;
-  return static_cast<web::NavigationItemImpl*>(
-      [self.entries[index] navigationItem]);
+  return self.items[index].get();
 }
 
 #pragma mark -
 #pragma mark Private methods
 
-- (CRWSessionEntry*)sessionEntryWithURL:(const GURL&)url
-                               referrer:(const web::Referrer&)referrer
-                             transition:(ui::PageTransition)transition
-                         initiationType:
-                             (web::NavigationInitiationType)initiationType {
+- (std::unique_ptr<web::NavigationItemImpl>)
+   itemWithURL:(const GURL&)url
+      referrer:(const web::Referrer&)referrer
+    transition:(ui::PageTransition)transition
+initiationType:(web::NavigationInitiationType)initiationType {
   GURL loaded_url(url);
   BOOL urlWasRewritten = NO;
   if (_navigationManager) {
@@ -691,20 +622,13 @@
   item->SetNavigationInitiationType(initiationType);
   if (web::GetWebClient()->IsAppSpecificURL(loaded_url))
     item->SetUserAgentType(web::UserAgentType::NONE);
-  return [[CRWSessionEntry alloc] initWithNavigationItem:std::move(item)];
+  return item;
 }
 
-- (BOOL)isRedirectTransitionForItemAtIndex:(NSInteger)index {
-  ui::PageTransition transition =
-      [_entries[index] navigationItem]->GetTransitionType();
+- (BOOL)isRedirectTransitionForItemAtIndex:(size_t)index {
+  DCHECK_LT(index, self.items.size());
+  ui::PageTransition transition = self.items[index]->GetTransitionType();
   return (transition & ui::PAGE_TRANSITION_IS_REDIRECT_MASK) ? YES : NO;
 }
 
-- (web::NavigationItemList)itemListForEntryList:(NSArray*)entries {
-  web::NavigationItemList list(entries.count);
-  for (size_t index = 0; index < entries.count; ++index)
-    list[index] = [entries[index] navigationItem];
-  return list;
-}
-
 @end
diff --git a/ios/web/navigation/crw_session_controller_unittest.mm b/ios/web/navigation/crw_session_controller_unittest.mm
index 3a6ab60..7c7d0177 100644
--- a/ios/web/navigation/crw_session_controller_unittest.mm
+++ b/ios/web/navigation/crw_session_controller_unittest.mm
@@ -30,9 +30,8 @@
 
 @implementation CRWSessionController (Testing)
 - (const GURL&)URLForItemAtIndex:(size_t)index {
-  web::NavigationItemList items = self.items;
-  if (index < items.size())
-    return items[index]->GetURL();
+  if (index < self.items.size())
+    return self.items[index]->GetURL();
   return GURL::EmptyGURL();
 }
 
@@ -78,7 +77,7 @@
   EXPECT_EQ(-1, [session_controller_ pendingItemIndex]);
   [session_controller_ setPendingItemIndex:0];
   EXPECT_EQ(0, [session_controller_ pendingItemIndex]);
-  EXPECT_EQ([session_controller_ items].back(),
+  EXPECT_EQ([session_controller_ items].back().get(),
             [session_controller_ pendingItem]);
 }
 
@@ -178,7 +177,7 @@
   EXPECT_EQ(1U, [session_controller_ items].size());
   EXPECT_EQ(GURL("http://www.url.com/"),
             [session_controller_ URLForItemAtIndex:0U]);
-  EXPECT_EQ([session_controller_ items].front(),
+  EXPECT_EQ([session_controller_ items].front().get(),
             [session_controller_ currentItem]);
 }
 
@@ -198,7 +197,7 @@
   EXPECT_EQ(1U, [session_controller_ items].size());
   EXPECT_EQ(GURL("http://www.another.url.com/"),
             [session_controller_ URLForItemAtIndex:0U]);
-  EXPECT_EQ([session_controller_ items].front(),
+  EXPECT_EQ([session_controller_ items].front().get(),
             [session_controller_ currentItem]);
 }
 
@@ -222,7 +221,8 @@
             [session_controller_ URLForItemAtIndex:0U]);
   EXPECT_EQ(GURL("http://www.another.url.com/"),
             [session_controller_ URLForItemAtIndex:1U]);
-  EXPECT_EQ([session_controller_ items][1U], [session_controller_ currentItem]);
+  EXPECT_EQ([session_controller_ items][1U].get(),
+            [session_controller_ currentItem]);
 }
 
 TEST_F(CRWSessionControllerTest, addPendingItemAndDiscard) {
@@ -273,7 +273,7 @@
   EXPECT_EQ(1U, [session_controller_ items].size());
   EXPECT_EQ(GURL("http://www.another.url.com/"),
             [session_controller_ URLForItemAtIndex:0U]);
-  EXPECT_EQ([session_controller_ items].front(),
+  EXPECT_EQ([session_controller_ items].front().get(),
             [session_controller_ currentItem]);
 }
 
@@ -295,7 +295,7 @@
   EXPECT_EQ(1U, [session_controller_ items].size());
   EXPECT_EQ(GURL("http://www.url.com/"),
             [session_controller_ URLForItemAtIndex:0U]);
-  EXPECT_EQ([session_controller_ items].front(),
+  EXPECT_EQ([session_controller_ items].front().get(),
             [session_controller_ currentItem]);
 }
 
@@ -311,8 +311,8 @@
        commitPendingItemWithoutPendingItemWithCommittedItem) {
   // Setup committed item.
   [session_controller_
-      addPendingItem:GURL("http://www.url.com")
-            referrer:MakeReferrer("http://www.referer.com")
+      addPendingItem:GURL("http://www.url.com/")
+            referrer:MakeReferrer("http://www.referrer.com/")
           transition:ui::PAGE_TRANSITION_TYPED
       initiationType:web::NavigationInitiationType::USER_INITIATED];
   [session_controller_ commitPendingItem];
@@ -321,13 +321,13 @@
   [session_controller_ commitPendingItem];
 
   EXPECT_EQ(1U, [session_controller_ items].size());
-  EXPECT_EQ([session_controller_ items].front(),
+  EXPECT_EQ([session_controller_ items].front().get(),
             [session_controller_ currentItem]);
 }
 
-// Tests that forward entries are discarded after navigation item is committed.
+// Tests that forward items are discarded after navigation item is committed.
 TEST_F(CRWSessionControllerTest, commitPendingItemWithExistingForwardItems) {
-  // Make 3 entries.
+  // Make 3 items.
   [session_controller_
       addPendingItem:GURL("http://www.example.com/0")
             referrer:MakeReferrer("http://www.example.com/a")
@@ -358,7 +358,7 @@
       initiationType:web::NavigationInitiationType::RENDERER_INITIATED];
   [session_controller_ commitPendingItem];
 
-  // All forward entries should go away.
+  // All forward items should go away.
   EXPECT_EQ(2U, [session_controller_ items].size());
   EXPECT_EQ(0U, [session_controller_ forwardItems].size());
   ASSERT_EQ(1, [session_controller_ currentNavigationIndex]);
@@ -391,15 +391,15 @@
   [session_controller_ goToItemAtIndex:1];
   [session_controller_ setPendingItemIndex:0];
   ASSERT_EQ(0, [session_controller_ pendingItemIndex]);
-  web::NavigationItem* pendingItem = [session_controller_ pendingItem];
-  ASSERT_TRUE(pendingItem);
+  web::NavigationItem* pending_item = [session_controller_ pendingItem];
+  ASSERT_TRUE(pending_item);
   ASSERT_EQ(1, [session_controller_ currentNavigationIndex]);
   EXPECT_EQ(2, [session_controller_ previousNavigationIndex]);
   [session_controller_ commitPendingItem];
 
   // Verify that pending item has been committed and current and previous item
   // indices updated.
-  EXPECT_EQ([session_controller_ lastCommittedItem], pendingItem);
+  EXPECT_EQ(pending_item, [session_controller_ lastCommittedItem]);
   EXPECT_EQ(-1, [session_controller_ pendingItemIndex]);
   EXPECT_FALSE([session_controller_ pendingItem]);
   EXPECT_EQ(0, [session_controller_ currentNavigationIndex]);
@@ -425,11 +425,11 @@
       initiationType:web::NavigationInitiationType::USER_INITIATED];
   [session_controller_ commitPendingItem];
 
-  // Discard noncommitted entries when there is no such one
+  // Discard noncommitted items when there is no such one
   [session_controller_ discardNonCommittedItems];
 
   EXPECT_EQ(1U, [session_controller_ items].size());
-  EXPECT_EQ([session_controller_ items].front(),
+  EXPECT_EQ([session_controller_ items].front().get(),
             [session_controller_ currentItem]);
 }
 
@@ -468,7 +468,7 @@
   EXPECT_EQ(1U, [session_controller_ items].size());
   EXPECT_EQ(GURL("http://www.url.com/"),
             [session_controller_ URLForItemAtIndex:0U]);
-  EXPECT_EQ([session_controller_ items].front(),
+  EXPECT_EQ([session_controller_ items].front().get(),
             [session_controller_ currentItem]);
 }
 
@@ -522,7 +522,7 @@
 
 // Tests inserting session controller state from empty session controller.
 TEST_F(CRWSessionControllerTest, InsertStateFromEmptySessionController) {
-  // Add 2 committed entries to target controller.
+  // Add 2 committed items to target controller.
   [session_controller_
       addPendingItem:GURL("http://www.url.com/0")
             referrer:web::Referrer()
@@ -557,7 +557,7 @@
 
 // Tests inserting session controller state to empty session controller.
 TEST_F(CRWSessionControllerTest, InsertStateToEmptySessionController) {
-  // Create source session controller with 2 committed entries and one
+  // Create source session controller with 2 committed items and one
   // pending item.
   base::scoped_nsobject<CRWSessionController> other_session_controller(
       [[CRWSessionController alloc] initWithBrowserState:&browser_state_
@@ -599,7 +599,7 @@
 // remains valid.
 TEST_F(CRWSessionControllerTest,
        InsertStateWithPendingItemIndexInTargetController) {
-  // Add 2 committed entries and make the first item pending.
+  // Add 2 committed items and make the first item pending.
   [session_controller_
       addPendingItem:GURL("http://www.url.com/2")
             referrer:web::Referrer()
@@ -652,7 +652,7 @@
 }
 
 // Helper to create a NavigationItem.
-std::unique_ptr<web::NavigationItemImpl> CreateNavigationItem(
+std::unique_ptr<web::NavigationItem> CreateNavigationItem(
     const std::string& url,
     const std::string& referrer,
     NSString* title) {
@@ -665,7 +665,11 @@
   navigation_item->SetTitle(base::SysNSStringToUTF16(title));
   navigation_item->SetTransitionType(ui::PAGE_TRANSITION_TYPED);
 
-  return navigation_item;
+  // XCode clang doesn't correctly handle implicit unique_ptr casting for
+  // returned values, so manually cast here.
+  std::unique_ptr<web::NavigationItem> item(
+      static_cast<web::NavigationItem*>(navigation_item.release()));
+  return item;
 }
 
 TEST_F(CRWSessionControllerTest, CreateWithEmptyNavigations) {
@@ -696,10 +700,9 @@
   EXPECT_EQ(controller.get().items.size(), 3U);
   EXPECT_EQ(controller.get().currentNavigationIndex, 1);
   EXPECT_EQ(controller.get().previousNavigationIndex, -1);
-  // Sanity check the current item, the CRWSessionItem unit test will ensure
+  // Sanity check the current item, the NavigationItem unit test will ensure
   // the entire object is created properly.
-  EXPECT_EQ(controller.get().currentItem->GetURL(),
-            GURL("http://www.yahoo.com"));
+  EXPECT_EQ([controller currentItem]->GetURL(), GURL("http://www.yahoo.com"));
 }
 
 // Tests index of previous navigation item.
@@ -802,18 +805,12 @@
       [[CRWSessionController alloc] initWithBrowserState:&browser_state_
                                          navigationItems:std::move(items)
                                             currentIndex:0]);
-  web::NavigationItemImpl* item0 =
-      static_cast<web::NavigationItemImpl*>([controller items][0]);
-  web::NavigationItemImpl* item1 =
-      static_cast<web::NavigationItemImpl*>([controller items][1]);
-  web::NavigationItemImpl* item2 =
-      static_cast<web::NavigationItemImpl*>([controller items][2]);
-  web::NavigationItemImpl* item3 =
-      static_cast<web::NavigationItemImpl*>([controller items][3]);
-  web::NavigationItemImpl* item4 =
-      static_cast<web::NavigationItemImpl*>([controller items][4]);
-  web::NavigationItemImpl* item5 =
-      static_cast<web::NavigationItemImpl*>([controller items][5]);
+  web::NavigationItemImpl* item0 = [controller items][0].get();
+  web::NavigationItemImpl* item1 = [controller items][1].get();
+  web::NavigationItemImpl* item2 = [controller items][2].get();
+  web::NavigationItemImpl* item3 = [controller items][3].get();
+  web::NavigationItemImpl* item4 = [controller items][4].get();
+  web::NavigationItemImpl* item5 = [controller items][5].get();
   item1->SetIsCreatedFromPushState(true);
   item4->SetIsCreatedFromHashChange(true);
   item5->SetIsCreatedFromPushState(true);
@@ -916,7 +913,7 @@
   EXPECT_EQ("http://www.example.com/2", forwardItems[1]->GetURL().spec());
 }
 
-// Tests going to entries with existing and non-existing indices.
+// Tests going to items with existing and non-existing indices.
 TEST_F(CRWSessionControllerTest, GoToItemAtIndex) {
   [session_controller_
       addPendingItem:GURL("http://www.example.com/0")
@@ -953,20 +950,20 @@
   EXPECT_TRUE([session_controller_ pendingItem]);
   EXPECT_TRUE([session_controller_ transientItem]);
 
-  // Going back should discard transient and pending entries.
+  // Going back should discard transient and pending items.
   [session_controller_ goToItemAtIndex:1];
   EXPECT_EQ(1, session_controller_.get().currentNavigationIndex);
   EXPECT_EQ(3, session_controller_.get().previousNavigationIndex);
-  EXPECT_FALSE([session_controller_ pendingItem]);
-  EXPECT_FALSE([session_controller_ transientItem]);
+  EXPECT_FALSE(session_controller_.get().pendingItem);
+  EXPECT_FALSE(session_controller_.get().transientItem);
 
   // Going forward should discard transient item.
   [session_controller_ addTransientItemWithURL:GURL("http://www.example.com")];
-  EXPECT_TRUE([session_controller_ transientItem]);
+  EXPECT_TRUE(session_controller_.get().transientItem);
   [session_controller_ goToItemAtIndex:2];
   EXPECT_EQ(2, session_controller_.get().currentNavigationIndex);
   EXPECT_EQ(1, session_controller_.get().previousNavigationIndex);
-  EXPECT_FALSE([session_controller_ transientItem]);
+  EXPECT_FALSE(session_controller_.get().transientItem);
 
   // Out of bounds navigations should be no-op.
   [session_controller_ goToItemAtIndex:-1];
@@ -983,7 +980,7 @@
 }
 
 // Tests that visible URL is the same as transient URL if there are no committed
-// entries.
+// items.
 TEST_F(CRWSessionControllerTest, VisibleItemWithSingleTransientItem) {
   [session_controller_ addTransientItemWithURL:GURL("http://www.example.com")];
   web::NavigationItem* visible_item = [session_controller_ visibleItem];
@@ -1095,7 +1092,7 @@
   EXPECT_EQ("http://www.example.com/0", visible_item->GetURL().spec());
 }
 
-// Tests that |-backwardItems| is empty if all preceding entries are
+// Tests that |-backwardItems| is empty if all preceding items are
 // redirects.
 TEST_F(CRWSessionControllerTest, BackwardItemsForAllRedirects) {
   [session_controller_
diff --git a/ios/web/navigation/crw_session_entry.h b/ios/web/navigation/crw_session_entry.h
deleted file mode 100644
index 1251d34..0000000
--- a/ios/web/navigation/crw_session_entry.h
+++ /dev/null
@@ -1,45 +0,0 @@
-// Copyright 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef IOS_WEB_NAVIGATION_CRW_SESSION_ENTRY_H_
-#define IOS_WEB_NAVIGATION_CRW_SESSION_ENTRY_H_
-
-#import <Foundation/Foundation.h>
-#include <stdint.h>
-
-#include <memory>
-
-#include "base/strings/string16.h"
-#include "base/time/time.h"
-#include "ui/base/page_transition_types.h"
-#include "url/gurl.h"
-
-namespace web {
-class NavigationItem;
-class NavigationItemImpl;
-}
-
-// A CRWSessionEntry is similar to a NavigationEntry object in desktop Chrome.
-// It maintains the information needed to save/restore a single entry in session
-// history (i.e., one site) for a tab. A tab may have multiple of these objects
-// comprising its entire session history.
-// DEPRECATED, do not use this class and do not add any methods to it.
-// Use web::NavigationItem instead.
-// TODO(crbug.com/454984): Remove this class.
-@interface CRWSessionEntry : NSObject<NSCopying>
-
-// Pointer to the NavigationItem associated with this CRWSessionEntry.
-// Eventually, this will replace CRWSessionEntry entirely.
-@property(nonatomic, readonly) web::NavigationItem* navigationItem;
-
-// Pointer to the NavigationItemImpl associated with this CRWSessionEntry.
-@property(nonatomic, readonly) web::NavigationItemImpl* navigationItemImpl;
-
-// Initialize the session entry with the given NavigationItem.
-- (instancetype)initWithNavigationItem:
-    (std::unique_ptr<web::NavigationItem>)item;
-
-@end
-
-#endif  // IOS_WEB_NAVIGATION_CRW_SESSION_ENTRY_H_
diff --git a/ios/web/navigation/crw_session_entry.mm b/ios/web/navigation/crw_session_entry.mm
deleted file mode 100644
index 59ef9350..0000000
--- a/ios/web/navigation/crw_session_entry.mm
+++ /dev/null
@@ -1,68 +0,0 @@
-// Copyright 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#import "ios/web/navigation/crw_session_entry.h"
-
-#include <stdint.h>
-
-#include <memory>
-
-#import "base/mac/scoped_nsobject.h"
-#include "base/strings/sys_string_conversions.h"
-#import "ios/web/navigation/navigation_item_impl.h"
-#import "ios/web/navigation/nscoder_util.h"
-#import "ios/web/public/navigation_item.h"
-#import "ios/web/public/web_state/page_display_state.h"
-#import "net/base/mac/url_conversions.h"
-
-#if !defined(__has_feature) || !__has_feature(objc_arc)
-#error "This file requires ARC support."
-#endif
-
-@interface CRWSessionEntry () {
-  // The NavigationItemImpl corresponding to this CRWSessionEntry.
-  // TODO(stuartmorgan): Move ownership to NavigationManagerImpl.
-  std::unique_ptr<web::NavigationItemImpl> _navigationItem;
-}
-
-@end
-
-@implementation CRWSessionEntry
-
-- (instancetype)initWithNavigationItem:
-    (std::unique_ptr<web::NavigationItem>)item {
-  self = [super init];
-  if (self) {
-    _navigationItem.reset(
-        static_cast<web::NavigationItemImpl*>(item.release()));
-  }
-  return self;
-}
-
-// TODO(ios): Shall we overwrite EqualTo:?
-
-- (instancetype)copyWithZone:(NSZone*)zone {
-  CRWSessionEntry* copy = [[[self class] alloc] init];
-  copy->_navigationItem.reset(
-      new web::NavigationItemImpl(*_navigationItem.get()));
-  return copy;
-}
-
-- (NSString*)description {
-#ifndef NDEBUG
-  return _navigationItem->GetDescription();
-#else
-  return [super description];
-#endif
-}
-
-- (web::NavigationItem*)navigationItem {
-  return _navigationItem.get();
-}
-
-- (web::NavigationItemImpl*)navigationItemImpl {
-  return _navigationItem.get();
-}
-
-@end
diff --git a/ios/web/navigation/navigation_item_impl.h b/ios/web/navigation/navigation_item_impl.h
index 28a632f..cc9e8ec 100644
--- a/ios/web/navigation/navigation_item_impl.h
+++ b/ios/web/navigation/navigation_item_impl.h
@@ -74,7 +74,7 @@
   // Serialized representation of the state object that was used in conjunction
   // with a JavaScript window.history.pushState() or
   // window.history.replaceState() call that created or modified this
-  // CRWSessionEntry. Intended to be used for JavaScript history operations and
+  // NavigationItem. Intended to be used for JavaScript history operations and
   // will be nil in most cases.
   void SetSerializedStateObject(NSString* serialized_state_object);
   NSString* GetSerializedStateObject() const;
diff --git a/ios/web/navigation/navigation_item_impl_list.h b/ios/web/navigation/navigation_item_impl_list.h
index 432f37b9..8db5001 100644
--- a/ios/web/navigation/navigation_item_impl_list.h
+++ b/ios/web/navigation/navigation_item_impl_list.h
@@ -24,6 +24,10 @@
 ScopedNavigationItemImplList CreateScopedNavigationItemImplList(
     ScopedNavigationItemList scoped_item_list);
 
+// Creates a NavigationItemList from |scoped_item_list|.
+NavigationItemList CreateNavigationItemList(
+    const ScopedNavigationItemImplList& scoped_item_list);
+
 }  // namespace web
 
 #endif  // IOS_WEB_NAVIGATION_NAVIGATION_ITEM_IMPL_LIST_H_
diff --git a/ios/web/navigation/navigation_item_impl_list.mm b/ios/web/navigation/navigation_item_impl_list.mm
index 22d58130..755bf024 100644
--- a/ios/web/navigation/navigation_item_impl_list.mm
+++ b/ios/web/navigation/navigation_item_impl_list.mm
@@ -19,4 +19,13 @@
   return list;
 }
 
+NavigationItemList CreateNavigationItemList(
+    const ScopedNavigationItemImplList& scoped_item_list) {
+  NavigationItemList list(scoped_item_list.size());
+  for (size_t index = 0; index < scoped_item_list.size(); ++index) {
+    list[index] = scoped_item_list[index].get();
+  }
+  return list;
+}
+
 }  // namespace web
diff --git a/ios/web/navigation/navigation_manager_impl.h b/ios/web/navigation/navigation_manager_impl.h
index 9ce05c5..560a383 100644
--- a/ios/web/navigation/navigation_manager_impl.h
+++ b/ios/web/navigation/navigation_manager_impl.h
@@ -153,10 +153,6 @@
   // Called to reset the transient url rewriter list.
   void RemoveTransientURLRewriters();
 
-  // Copy state from |navigation_manager|, including a copy of that object's
-  // CRWSessionController.
-  void CopyState(NavigationManagerImpl* navigation_manager);
-
   // Returns the navigation index that differs from the current item (or pending
   // item if it exists) by the specified |offset|, skipping redirect navigation
   // items. The index returned is not guaranteed to be valid.
diff --git a/ios/web/navigation/navigation_manager_impl.mm b/ios/web/navigation/navigation_manager_impl.mm
index ac70ae6..76c6046 100644
--- a/ios/web/navigation/navigation_manager_impl.mm
+++ b/ios/web/navigation/navigation_manager_impl.mm
@@ -12,6 +12,7 @@
 #import "ios/web/navigation/crw_session_controller+private_constructors.h"
 #import "ios/web/navigation/crw_session_controller.h"
 #import "ios/web/navigation/navigation_item_impl.h"
+#import "ios/web/navigation/navigation_item_impl_list.h"
 #import "ios/web/navigation/navigation_manager_delegate.h"
 #include "ios/web/navigation/navigation_manager_facade_delegate.h"
 #include "ios/web/public/load_committed_details.h"
@@ -220,7 +221,7 @@
 }
 
 NavigationItemList NavigationManagerImpl::GetItems() const {
-  return [session_controller_ items];
+  return CreateNavigationItemList([session_controller_ items]);
 }
 
 BrowserState* NavigationManagerImpl::GetBrowserState() const {
@@ -271,7 +272,7 @@
 }
 
 int NavigationManagerImpl::GetItemCount() const {
-  return [session_controller_ itemCount];
+  return [session_controller_ items].size();
 }
 
 NavigationItem* NavigationManagerImpl::GetItemAtIndex(size_t index) const {
@@ -358,11 +359,6 @@
   transient_url_rewriters_.reset();
 }
 
-void NavigationManagerImpl::CopyState(
-    NavigationManagerImpl* navigation_manager) {
-  SetSessionController([navigation_manager->GetSessionController() copy]);
-}
-
 int NavigationManagerImpl::GetIndexForOffset(int offset) const {
   int result = [session_controller_ pendingItemIndex] == -1
                    ? GetCurrentItemIndex()
@@ -446,9 +442,9 @@
   if (index == -1)
     return nullptr;
   WebClient* client = GetWebClient();
-  NavigationItemList items = [session_controller_ items];
+  const ScopedNavigationItemImplList& items = [session_controller_ items];
   while (index >= 0) {
-    NavigationItem* item = items[index--];
+    NavigationItem* item = items[index--].get();
     if (!client->IsAppSpecificURL(item->GetVirtualURL()))
       return item;
   }
diff --git a/ios/web/navigation/session_storage_builder.mm b/ios/web/navigation/session_storage_builder.mm
index fceabc0..0f932e8 100644
--- a/ios/web/navigation/session_storage_builder.mm
+++ b/ios/web/navigation/session_storage_builder.mm
@@ -48,8 +48,7 @@
   NSMutableArray* item_storages = [[NSMutableArray alloc] init];
   NavigationItemStorageBuilder item_storage_builder;
   for (size_t index = 0; index < session_controller.items.size(); ++index) {
-    web::NavigationItemImpl* item =
-        static_cast<web::NavigationItemImpl*>(session_controller.items[index]);
+    web::NavigationItemImpl* item = session_controller.items[index].get();
     [item_storages addObject:item_storage_builder.BuildStorage(item)];
   }
   serialized_navigation_manager.itemStorages = item_storages;
diff --git a/ios/web/public/test/crw_mock_web_state_delegate.h b/ios/web/public/test/crw_mock_web_state_delegate.h
index 51389b6..c92bfdc3 100644
--- a/ios/web/public/test/crw_mock_web_state_delegate.h
+++ b/ios/web/public/test/crw_mock_web_state_delegate.h
@@ -19,6 +19,9 @@
 // ContextMenuParams reveived in |webState:handleContextMenu:| call.
 // nullptr if that delegate method was not called.
 @property(nonatomic, readonly) web::ContextMenuParams* contextMenuParams;
+// Whether |webState:createNewWebStateForURL:openerURL:initiatedByUser:| has
+// been called or not.
+@property(nonatomic, readonly) BOOL webStateCreationRequested;
 // Whether |webState:runRepostFormDialogWithCompletionHandler:| has been called
 // or not.
 @property(nonatomic, readonly) BOOL repostFormWarningRequested;
diff --git a/ios/web/public/test/crw_mock_web_state_delegate.mm b/ios/web/public/test/crw_mock_web_state_delegate.mm
index f6dfa0a..06cbb75 100644
--- a/ios/web/public/test/crw_mock_web_state_delegate.mm
+++ b/ios/web/public/test/crw_mock_web_state_delegate.mm
@@ -17,10 +17,20 @@
 }
 
 @synthesize webState = _webState;
+@synthesize webStateCreationRequested = _webStateCreationRequested;
 @synthesize repostFormWarningRequested = _repostFormWarningRequested;
 @synthesize authenticationRequested = _authenticationRequested;
 
 - (web::WebState*)webState:(web::WebState*)webState
+    createNewWebStateForURL:(const GURL&)URL
+                  openerURL:(const GURL&)openerURL
+            initiatedByUser:(BOOL)initiatedByUser {
+  _webState = webState;
+  _webStateCreationRequested = YES;
+  return nil;
+}
+
+- (web::WebState*)webState:(web::WebState*)webState
          openURLWithParams:(const web::WebState::OpenURLParams&)params {
   _webState = webState;
   _openURLParams.reset(new web::WebState::OpenURLParams(params));
diff --git a/ios/web/public/test/fakes/test_web_state_delegate.h b/ios/web/public/test/fakes/test_web_state_delegate.h
index dc20ad11..3dfca1e4 100644
--- a/ios/web/public/test/fakes/test_web_state_delegate.h
+++ b/ios/web/public/test/fakes/test_web_state_delegate.h
@@ -8,6 +8,7 @@
 #import <Foundation/Foundation.h>
 
 #include <memory>
+#include <set>
 
 #include "base/mac/scoped_nsobject.h"
 #import "ios/web/public/web_state/web_state_delegate.h"
@@ -15,6 +16,14 @@
 
 namespace web {
 
+// Encapsulates parameters passed to CreateNewWebState.
+struct TestCreateNewWebStateRequest {
+  WebState* web_state = nullptr;
+  GURL url;
+  GURL opener_url;
+  bool initiated_by_user = false;
+};
+
 // Encapsulates parameters passed to OpenURLFromWebState.
 struct TestOpenURLRequest {
   TestOpenURLRequest();
@@ -44,6 +53,14 @@
   WebStateDelegate::AuthCallback auth_callback;
 };
 
+// Encapsulates information about popup.
+struct TestPopup {
+  TestPopup(const GURL& url, const GURL& opener_url)
+      : url(url), opener_url(opener_url) {}
+  GURL url;
+  GURL opener_url;
+};
+
 // Fake WebStateDelegate used for testing purposes.
 class TestWebStateDelegate : public WebStateDelegate {
  public:
@@ -51,6 +68,10 @@
   ~TestWebStateDelegate() override;
 
   // WebStateDelegate overrides:
+  WebState* CreateNewWebState(WebState* source,
+                              const GURL& url,
+                              const GURL& opener_url,
+                              bool initiated_by_user) override;
   WebState* OpenURLFromWebState(WebState*,
                                 const WebState::OpenURLParams&) override;
   JavaScriptDialogPresenter* GetJavaScriptDialogPresenter(WebState*) override;
@@ -65,11 +86,29 @@
                       NSURLCredential* proposed_credential,
                       const AuthCallback& callback) override;
 
+  // Allows popups requested by a page with |opener_url|.
+  void allow_popups(const GURL& opener_url) {
+    allowed_popups_.insert(opener_url);
+  }
+
+  // Returns list of all child windows opened via CreateNewWebState.
+  const std::vector<std::unique_ptr<WebState>>& child_windows() const {
+    return child_windows_;
+  }
+
+  // Returns list of all popups requested via CreateNewWebState.
+  const std::vector<TestPopup>& popups() const { return popups_; }
+
   // True if the WebStateDelegate HandleContextMenu method has been called.
   bool handle_context_menu_called() const {
     return handle_context_menu_called_;
   }
 
+  // Returns the last Web State creation request passed to |CreateNewWebState|.
+  TestCreateNewWebStateRequest* last_create_new_web_state_request() const {
+    return last_create_new_web_state_request_.get();
+  }
+
   // Returns the last Open URL request passed to |OpenURLFromWebState|.
   TestOpenURLRequest* last_open_url_request() const {
     return last_open_url_request_.get();
@@ -98,7 +137,13 @@
   }
 
  private:
+  std::vector<std::unique_ptr<WebState>> child_windows_;
+  // A page can open popup if its URL is in this set.
+  std::set<GURL> allowed_popups_;
+  std::vector<TestPopup> popups_;
   bool handle_context_menu_called_ = false;
+  std::unique_ptr<TestCreateNewWebStateRequest>
+      last_create_new_web_state_request_;
   std::unique_ptr<TestOpenURLRequest> last_open_url_request_;
   std::unique_ptr<TestRepostFormRequest> last_repost_form_request_;
   bool get_java_script_dialog_presenter_called_ = false;
diff --git a/ios/web/public/test/fakes/test_web_state_delegate.mm b/ios/web/public/test/fakes/test_web_state_delegate.mm
index 6019215..8263400e 100644
--- a/ios/web/public/test/fakes/test_web_state_delegate.mm
+++ b/ios/web/public/test/fakes/test_web_state_delegate.mm
@@ -5,6 +5,7 @@
 #import "ios/web/public/test/fakes/test_web_state_delegate.h"
 
 #include "base/memory/ptr_util.h"
+#import "ios/web/web_state/web_state_impl.h"
 
 namespace web {
 
@@ -37,6 +38,32 @@
 
 TestWebStateDelegate::~TestWebStateDelegate() = default;
 
+WebState* TestWebStateDelegate::CreateNewWebState(WebState* source,
+                                                  const GURL& url,
+                                                  const GURL& opener_url,
+                                                  bool initiated_by_user) {
+  last_create_new_web_state_request_ =
+      base::MakeUnique<TestCreateNewWebStateRequest>();
+  last_create_new_web_state_request_->web_state = source;
+  last_create_new_web_state_request_->url = url;
+  last_create_new_web_state_request_->opener_url = opener_url;
+  last_create_new_web_state_request_->initiated_by_user = initiated_by_user;
+
+  if (!initiated_by_user &&
+      allowed_popups_.find(opener_url) == allowed_popups_.end()) {
+    popups_.push_back(TestPopup(url, opener_url));
+    return nullptr;
+  }
+
+  std::unique_ptr<WebStateImpl> child(
+      base::MakeUnique<WebStateImpl>(source->GetBrowserState()));
+  child->GetNavigationManagerImpl().InitializeSession(YES /*opened_by_dom*/);
+  child->SetWebUsageEnabled(true);
+
+  child_windows_.push_back(std::move(child));
+  return child_windows_.back().get();
+}
+
 WebState* TestWebStateDelegate::OpenURLFromWebState(
     WebState* web_state,
     const WebState::OpenURLParams& params) {
diff --git a/ios/web/public/web_state/ui/crw_web_delegate.h b/ios/web/public/web_state/ui/crw_web_delegate.h
index 4335b21..01248eaf 100644
--- a/ios/web/public/web_state/ui/crw_web_delegate.h
+++ b/ios/web/public/web_state/ui/crw_web_delegate.h
@@ -18,7 +18,6 @@
 #include "ui/base/page_transition_types.h"
 
 class GURL;
-@class CRWSessionEntry;
 @class CRWWebController;
 
 // Methods implemented by the delegate of the CRWWebController.
@@ -71,17 +70,6 @@
 // TODO(crbug.com/692331): Remove this method and use |DidFinishNavigation|.
 - (void)webWillFinishHistoryNavigation;
 
-// ---------------------------------------------------------------------
-// Called when |webController| wants to open a new window. |URL| is the URL of
-// the new window; |openerURL| is the URL of the page which requested a window
-// to be open; |initiatedByUser| is YES if action was caused by the user.
-// |webController| will not open a window if this method returns nil. This
-// method can not return |webController|.
-- (CRWWebController*)webController:(CRWWebController*)webController
-         createWebControllerForURL:(const GURL&)URL
-                         openerURL:(const GURL&)openerURL
-                   initiatedByUser:(BOOL)initiatedByUser;
-
 @optional
 
 // Called to ask CRWWebDelegate if |CRWWebController| should open the given URL.
diff --git a/ios/web/public/web_state/web_state_delegate.h b/ios/web/public/web_state/web_state_delegate.h
index 8eebecc..01e189f 100644
--- a/ios/web/public/web_state/web_state_delegate.h
+++ b/ios/web/public/web_state/web_state_delegate.h
@@ -23,6 +23,16 @@
  public:
   WebStateDelegate();
 
+  // Called when |source| wants to open a new window. |url| is the URL of
+  // the new window; |opener_url| is the URL of the page which requested a
+  // window to be open; |initiated_by_user| is true if action was caused by the
+  // user. |source| will not open a window if this method returns nil. This
+  // method can not return |source|.
+  virtual WebState* CreateNewWebState(WebState* source,
+                                      const GURL& url,
+                                      const GURL& opener_url,
+                                      bool initiated_by_user);
+
   // Returns the WebState the URL is opened in, or nullptr if the URL wasn't
   // opened immediately.
   virtual WebState* OpenURLFromWebState(WebState*,
diff --git a/ios/web/public/web_state/web_state_delegate_bridge.h b/ios/web/public/web_state/web_state_delegate_bridge.h
index 62de0b8..11dae7c 100644
--- a/ios/web/public/web_state/web_state_delegate_bridge.h
+++ b/ios/web/public/web_state/web_state_delegate_bridge.h
@@ -14,6 +14,16 @@
 @protocol CRWWebStateDelegate<NSObject>
 @optional
 
+// Called when |webState| wants to open a new window. |url| is the URL of
+// the new window; |opener_url| is the URL of the page which requested a
+// window to be open; |initiated_by_user| is true if action was caused by the
+// user. |webState| will not open a window if this method returns nil. This
+// method can not return |webState|.
+- (web::WebState*)webState:(web::WebState*)webState
+    createNewWebStateForURL:(const GURL&)URL
+                  openerURL:(const GURL&)openerURL
+            initiatedByUser:(BOOL)initiatedByUser;
+
 // Returns the WebState the URL is opened in, or nullptr if the URL wasn't
 // opened immediately.
 - (web::WebState*)webState:(web::WebState*)webState
@@ -57,6 +67,10 @@
   ~WebStateDelegateBridge() override;
 
   // web::WebStateDelegate methods.
+  WebState* CreateNewWebState(WebState* source,
+                              const GURL& url,
+                              const GURL& opener_url,
+                              bool initiated_by_user) override;
   WebState* OpenURLFromWebState(WebState*,
                                 const WebState::OpenURLParams&) override;
   bool HandleContextMenu(WebState* source,
diff --git a/ios/web/web_state/ui/crw_web_controller.mm b/ios/web/web_state/ui/crw_web_controller.mm
index a2f1da5..0758389f 100644
--- a/ios/web/web_state/ui/crw_web_controller.mm
+++ b/ios/web/web_state/ui/crw_web_controller.mm
@@ -683,15 +683,15 @@
     (const web::PageScrollState&)scrollState;
 // Returns the referrer for the current page.
 - (web::Referrer)currentReferrer;
-// Adds a new CRWSessionEntry with the given URL and state object to the history
+// Adds a new NavigationItem with the given URL and state object to the history
 // stack. A state object is a serialized generic JavaScript object that contains
-// details of the UI's state for a given CRWSessionEntry/URL.
+// details of the UI's state for a given NavigationItem/URL.
 // TODO(stuartmorgan): Move the pushState/replaceState logic into
 // NavigationManager.
 - (void)pushStateWithPageURL:(const GURL&)pageURL
                  stateObject:(NSString*)stateObject
                   transition:(ui::PageTransition)transition;
-// Assigns the given URL and state object to the current CRWSessionEntry.
+// Assigns the given URL and state object to the current NavigationItem.
 - (void)replaceStateWithPageURL:(const GURL&)pageUrl
                     stateObject:(NSString*)stateObject;
 // Sets _documentURL to newURL, and updates any relevant state information.
@@ -2108,7 +2108,7 @@
 
 - (void)goToItemAtIndex:(int)index {
   CRWSessionController* sessionController = self.sessionController;
-  web::NavigationItemList items = sessionController.items;
+  const web::ScopedNavigationItemImplList& items = sessionController.items;
   if (index < 0 || index >= static_cast<int>(items.size())) {
     NOTREACHED();
     return;
@@ -2117,7 +2117,7 @@
   if (!_webStateImpl->IsShowingWebInterstitial())
     [self recordStateInHistory];
   web::NavigationItem* fromItem = sessionController.currentItem;
-  web::NavigationItem* toItem = items[index];
+  web::NavigationItem* toItem = items[index].get();
 
   NSUserDefaults* userDefaults = [NSUserDefaults standardUserDefaults];
   if (![userDefaults boolForKey:@"PendingIndexNavigationDisabled"]) {
@@ -4341,11 +4341,17 @@
   NSString* referrer = [self referrerFromNavigationAction:action];
   GURL openerURL =
       referrer ? GURL(base::SysNSStringToUTF8(referrer)) : _documentURL;
-  CRWWebController* child = [_delegate webController:self
-                           createWebControllerForURL:requestURL
-                                           openerURL:openerURL
-                                     initiatedByUser:[self userIsInteracting]];
-  DCHECK(!child || child.sessionController.openedByDOM);
+
+  WebState* childWebState = _webStateImpl->CreateNewWebState(
+      requestURL, openerURL, [self userIsInteracting]);
+  if (!childWebState)
+    return nil;
+
+  CRWWebController* childWebController =
+      static_cast<WebStateImpl*>(childWebState)->GetWebController();
+
+  DCHECK(!childWebController ||
+         childWebController.sessionController.openedByDOM);
 
   // WKWebView requires WKUIDelegate to return a child view created with
   // exactly the same |configuration| object (exception is raised if config is
@@ -4353,8 +4359,8 @@
   // WKWebViewConfigurationProvider are different objects because WKWebView
   // makes a shallow copy of the config inside init, so every WKWebView
   // owns a separate shallow copy of WKWebViewConfiguration.
-  [child ensureWebViewCreatedWithConfiguration:configuration];
-  return child.webView;
+  [childWebController ensureWebViewCreatedWithConfiguration:configuration];
+  return childWebController.webView;
 }
 
 - (void)webViewDidClose:(WKWebView*)webView {
diff --git a/ios/web/web_state/ui/crw_web_controller_unittest.mm b/ios/web/web_state/ui/crw_web_controller_unittest.mm
index b8ff6d05..9a6f792 100644
--- a/ios/web/web_state/ui/crw_web_controller_unittest.mm
+++ b/ios/web/web_state/ui/crw_web_controller_unittest.mm
@@ -56,48 +56,10 @@
 
 // Used to mock CRWWebDelegate methods with C++ params.
 @interface MockInteractionLoader : OCMockComplexTypeHelper
-// Whether or not the delegate should block popups.
-@property(nonatomic, assign) BOOL blockPopups;
-// A web controller that will be returned by webPageOrdered... methods.
-@property(nonatomic, assign) CRWWebController* childWebController;
-
-// Values of arguments passed to
-// |webController:createWebControllerForURL:openerURL:initiatedByUser:|.
-@property(nonatomic, readonly) CRWWebController* webController;
-@property(nonatomic, readonly) GURL childURL;
-@property(nonatomic, readonly) GURL openerURL;
-@property(nonatomic, readonly) BOOL initiatedByUser;
 @end
 
 @implementation MockInteractionLoader
 
-@synthesize blockPopups = _blockPopups;
-@synthesize childWebController = _childWebController;
-@synthesize webController = _webController;
-@synthesize childURL = _childURL;
-@synthesize openerURL = _openerURL;
-@synthesize initiatedByUser = _initiatedByUser;
-
-- (instancetype)initWithRepresentedObject:(id)representedObject {
-  self = [super initWithRepresentedObject:representedObject];
-  if (self) {
-    _blockPopups = YES;
-  }
-  return self;
-}
-
-- (CRWWebController*)webController:(CRWWebController*)webController
-         createWebControllerForURL:(const GURL&)childURL
-                         openerURL:(const GURL&)openerURL
-                   initiatedByUser:(BOOL)initiatedByUser {
-  _webController = webController;
-  _childURL = childURL;
-  _openerURL = openerURL;
-  _initiatedByUser = initiatedByUser;
-
-  return (_blockPopups && !initiatedByUser) ? nil : _childWebController;
-}
-
 typedef BOOL (^openExternalURLBlockType)(const GURL&);
 
 - (BOOL)openExternalURL:(const GURL&)url {
@@ -650,17 +612,19 @@
 typedef web::WebTestWithWebController CRWWebControllerNavigationTest;
 
 // Tests navigation between 2 URLs which differ only by fragment.
-TEST_F(CRWWebControllerNavigationTest, GoToEntryWithoutDocumentChange) {
+TEST_F(CRWWebControllerNavigationTest, GoToItemWithoutDocumentChange) {
   LoadHtml(@"<html><body></body></html>", GURL("https://chromium.test"));
   LoadHtml(@"<html><body></body></html>", GURL("https://chromium.test#hash"));
   NavigationManagerImpl& nav_manager =
       web_controller().webStateImpl->GetNavigationManagerImpl();
   CRWSessionController* session_controller = nav_manager.GetSessionController();
   EXPECT_EQ(2U, session_controller.items.size());
-  EXPECT_EQ(session_controller.items.back(), session_controller.currentItem);
+  EXPECT_EQ(session_controller.items.back().get(),
+            session_controller.currentItem);
 
   [web_controller() goToItemAtIndex:0];
-  EXPECT_EQ(session_controller.items.front(), session_controller.currentItem);
+  EXPECT_EQ(session_controller.items.front().get(),
+            session_controller.currentItem);
 }
 
 // Tests that didShowPasswordInputOnHTTP updates the SSLStatus to indicate that
@@ -863,42 +827,18 @@
 };
 
 // Test fixture for window.open tests.
-class CRWWebControllerWindowOpenTest : public web::WebTestWithWebController {
+class WindowOpenByDomTest : public web::WebTestWithWebController {
  protected:
+  WindowOpenByDomTest() : opener_url_("http://test") {}
+
   void SetUp() override {
-    web::WebTestWithWebController::SetUp();
-
-    // Configure web delegate.
-    delegate_.reset([[MockInteractionLoader alloc]
-        initWithRepresentedObject:
-            [OCMockObject niceMockForProtocol:@protocol(CRWWebDelegate)]]);
-    ASSERT_TRUE([delegate_ blockPopups]);
-    [web_controller() setDelegate:delegate_];
-
-    // Configure child web state.
-    child_web_state_.reset(new web::WebStateImpl(GetBrowserState()));
-    child_web_state_->SetWebUsageEnabled(true);
-    [delegate_ setChildWebController:child_web_state_->GetWebController()];
-
-    // Configure child web controller's session controller mock.
-    id sessionController =
-        [OCMockObject niceMockForClass:[CRWSessionController class]];
-    BOOL yes = YES;
-    [[[sessionController stub] andReturnValue:OCMOCK_VALUE(yes)] isOpenedByDOM];
-    child_web_state_->GetNavigationManagerImpl().SetSessionController(
-        sessionController);
-
-    LoadHtml(@"<html><body></body></html>", GURL("http://test"));
-  }
-  void TearDown() override {
-    EXPECT_OCMOCK_VERIFY(delegate_);
-    [web_controller() setDelegate:nil];
-
-    web::WebTestWithWebController::TearDown();
+    WebTestWithWebController::SetUp();
+    web_state()->SetDelegate(&delegate_);
+    LoadHtml(@"<html><body></body></html>", opener_url_);
   }
   // Executes JavaScript that opens a new window and returns evaluation result
   // as a string.
-  id OpenWindowByDOM() {
+  id OpenWindowByDom() {
     NSString* const kOpenWindowScript =
         @"var w = window.open('javascript:void(0);', target='_blank');"
          "w ? w.toString() : null;";
@@ -906,71 +846,55 @@
     WaitForBackgroundTasks();
     return windowJSObject;
   }
-  // A CRWWebDelegate mock used for testing.
-  base::scoped_nsobject<id> delegate_;
-  // A child WebState used for testing.
-  std::unique_ptr<web::WebStateImpl> child_web_state_;
+  // URL of a page which opens child windows.
+  const GURL opener_url_;
+  web::TestWebStateDelegate delegate_;
 };
 
-// Tests that absence of web delegate is handled gracefully.
-TEST_F(CRWWebControllerWindowOpenTest, NoDelegate) {
-  [web_controller() setDelegate:nil];
+// Tests that absence of web state delegate is handled gracefully.
+TEST_F(WindowOpenByDomTest, NoDelegate) {
+  web_state()->SetDelegate(nullptr);
 
-  EXPECT_NSEQ([NSNull null], OpenWindowByDOM());
+  EXPECT_NSEQ([NSNull null], OpenWindowByDom());
 
-  EXPECT_FALSE([delegate_ webController]);
-  EXPECT_FALSE([delegate_ childURL].is_valid());
-  EXPECT_FALSE([delegate_ openerURL].is_valid());
-  EXPECT_FALSE([delegate_ initiatedByUser]);
+  EXPECT_TRUE(delegate_.child_windows().empty());
+  EXPECT_TRUE(delegate_.popups().empty());
 }
 
 // Tests that window.open triggered by user gesture opens a new non-popup
 // window.
-TEST_F(CRWWebControllerWindowOpenTest, OpenWithUserGesture) {
+TEST_F(WindowOpenByDomTest, OpenWithUserGesture) {
   [web_controller() touched:YES];
-  EXPECT_NSEQ(@"[object Window]", OpenWindowByDOM());
+  EXPECT_NSEQ(@"[object Window]", OpenWindowByDom());
 
-  EXPECT_EQ(web_controller(), [delegate_ webController]);
-  EXPECT_EQ("javascript:void(0);", [delegate_ childURL].spec());
-  EXPECT_EQ("http://test/", [delegate_ openerURL].spec());
-  EXPECT_TRUE([delegate_ initiatedByUser]);
+  ASSERT_EQ(1U, delegate_.child_windows().size());
+  ASSERT_TRUE(delegate_.child_windows()[0]);
+  EXPECT_TRUE(delegate_.popups().empty());
 }
 
-// Tests that window.open executed w/o user gesture does not open a new window.
-// Once the blocked popup is allowed a new window is opened.
-TEST_F(CRWWebControllerWindowOpenTest, AllowPopup) {
+// Tests that window.open executed w/o user gesture does not open a new window,
+// but blocks popup instead.
+TEST_F(WindowOpenByDomTest, BlockPopup) {
   ASSERT_FALSE([web_controller() userIsInteracting]);
-  EXPECT_NSEQ([NSNull null], OpenWindowByDOM());
+  EXPECT_NSEQ([NSNull null], OpenWindowByDom());
 
-  EXPECT_EQ(web_controller(), [delegate_ webController]);
-  EXPECT_EQ("javascript:void(0);", [delegate_ childURL].spec());
-  EXPECT_EQ("http://test/", [delegate_ openerURL].spec());
-  EXPECT_FALSE([delegate_ initiatedByUser]);
+  EXPECT_TRUE(delegate_.child_windows().empty());
+  ASSERT_EQ(1U, delegate_.popups().size());
+  EXPECT_EQ(GURL("javascript:void(0);"), delegate_.popups()[0].url);
+  EXPECT_EQ(opener_url_, delegate_.popups()[0].opener_url);
 }
 
 // Tests that window.open executed w/o user gesture opens a new window, assuming
 // that delegate allows popups.
-TEST_F(CRWWebControllerWindowOpenTest, DontBlockPopup) {
-  [delegate_ setBlockPopups:NO];
-  EXPECT_NSEQ(@"[object Window]", OpenWindowByDOM());
+TEST_F(WindowOpenByDomTest, DontBlockPopup) {
+  delegate_.allow_popups(opener_url_);
+  EXPECT_NSEQ(@"[object Window]", OpenWindowByDom());
 
-  EXPECT_EQ(web_controller(), [delegate_ webController]);
-  EXPECT_EQ("javascript:void(0);", [delegate_ childURL].spec());
-  EXPECT_EQ("http://test/", [delegate_ openerURL].spec());
-  EXPECT_FALSE([delegate_ initiatedByUser]);
+  ASSERT_EQ(1U, delegate_.child_windows().size());
+  ASSERT_TRUE(delegate_.child_windows()[0]);
+  EXPECT_TRUE(delegate_.popups().empty());
 }
 
-// Tests that window.open executed w/o user gesture does not open a new window.
-TEST_F(CRWWebControllerWindowOpenTest, BlockPopup) {
-  ASSERT_FALSE([web_controller() userIsInteracting]);
-  EXPECT_NSEQ([NSNull null], OpenWindowByDOM());
-
-  EXPECT_EQ(web_controller(), [delegate_ webController]);
-  EXPECT_EQ("javascript:void(0);", [delegate_ childURL].spec());
-  EXPECT_EQ("http://test/", [delegate_ openerURL].spec());
-  EXPECT_FALSE([delegate_ initiatedByUser]);
-};
-
 // Tests page title changes.
 typedef web::WebTestWithWebState CRWWebControllerTitleTest;
 TEST_F(CRWWebControllerTitleTest, TitleChange) {
diff --git a/ios/web/web_state/web_state_delegate.mm b/ios/web/web_state/web_state_delegate.mm
index 67a12bc8..b3f3faa 100644
--- a/ios/web/web_state/web_state_delegate.mm
+++ b/ios/web/web_state/web_state_delegate.mm
@@ -18,6 +18,13 @@
   DCHECK(attached_states_.empty());
 }
 
+WebState* WebStateDelegate::CreateNewWebState(WebState* source,
+                                              const GURL& url,
+                                              const GURL& opener_url,
+                                              bool initiated_by_user) {
+  return nullptr;
+}
+
 WebState* WebStateDelegate::OpenURLFromWebState(
     WebState*,
     const WebState::OpenURLParams&) {
diff --git a/ios/web/web_state/web_state_delegate_bridge.mm b/ios/web/web_state/web_state_delegate_bridge.mm
index 54d788b8..a0232609 100644
--- a/ios/web/web_state/web_state_delegate_bridge.mm
+++ b/ios/web/web_state/web_state_delegate_bridge.mm
@@ -14,6 +14,21 @@
 
 WebStateDelegateBridge::~WebStateDelegateBridge() {}
 
+WebState* WebStateDelegateBridge::CreateNewWebState(WebState* source,
+                                                    const GURL& url,
+                                                    const GURL& opener_url,
+                                                    bool initiated_by_user) {
+  SEL selector =
+      @selector(webState:createNewWebStateForURL:openerURL:initiatedByUser:);
+  if ([delegate_ respondsToSelector:selector]) {
+    return [delegate_ webState:source
+        createNewWebStateForURL:url
+                      openerURL:opener_url
+                initiatedByUser:initiated_by_user];
+  }
+  return nullptr;
+}
+
 WebState* WebStateDelegateBridge::OpenURLFromWebState(
     WebState* source,
     const WebState::OpenURLParams& params) {
diff --git a/ios/web/web_state/web_state_delegate_bridge_unittest.mm b/ios/web/web_state/web_state_delegate_bridge_unittest.mm
index 45cd392..2ce4c8b0a 100644
--- a/ios/web/web_state/web_state_delegate_bridge_unittest.mm
+++ b/ios/web/web_state/web_state_delegate_bridge_unittest.mm
@@ -56,6 +56,19 @@
   web::TestWebState test_web_state_;
 };
 
+// Tests |webState:createNewWebStateForURL:openerURL:initiatedByUser:|
+// forwarding.
+TEST_F(WebStateDelegateBridgeTest, CreateNewWebState) {
+  ASSERT_FALSE([delegate_ webState]);
+  ASSERT_FALSE([delegate_ webStateCreationRequested]);
+
+  EXPECT_FALSE(
+      bridge_->CreateNewWebState(&test_web_state_, GURL(), GURL(), true));
+
+  EXPECT_EQ(&test_web_state_, [delegate_ webState]);
+  ASSERT_TRUE([delegate_ webStateCreationRequested]);
+}
+
 // Tests |webState:openURLWithParams:| forwarding.
 TEST_F(WebStateDelegateBridgeTest, OpenURLFromWebState) {
   ASSERT_FALSE([delegate_ webState]);
diff --git a/ios/web/web_state/web_state_impl.h b/ios/web/web_state/web_state_impl.h
index 6fc0d3c..c873eea 100644
--- a/ios/web/web_state/web_state_impl.h
+++ b/ios/web/web_state/web_state_impl.h
@@ -253,6 +253,14 @@
                            NSString* default_prompt_text,
                            const DialogClosedCallback& callback);
 
+  // Instructs the delegate to create a new web state. Called when this WebState
+  // wants to open a new window. |url| is the URL of the new window;
+  // |opener_url| is the URL of the page which requested a window to be open;
+  // |initiated_by_user| is true if action was caused by the user.
+  WebState* CreateNewWebState(const GURL& url,
+                              const GURL& opener_url,
+                              bool initiated_by_user);
+
   // Notifies the delegate that request receives an authentication challenge
   // and is unable to respond using cached credentials.
   void OnAuthRequired(NSURLProtectionSpace* protection_space,
diff --git a/ios/web/web_state/web_state_impl.mm b/ios/web/web_state/web_state_impl.mm
index 1a1c475..7bd67587 100644
--- a/ios/web/web_state/web_state_impl.mm
+++ b/ios/web/web_state/web_state_impl.mm
@@ -504,6 +504,16 @@
                                  message_text, default_prompt_text, callback);
 }
 
+WebState* WebStateImpl::CreateNewWebState(const GURL& url,
+                                          const GURL& opener_url,
+                                          bool initiated_by_user) {
+  if (delegate_) {
+    return delegate_->CreateNewWebState(this, url, opener_url,
+                                        initiated_by_user);
+  }
+  return nullptr;
+}
+
 void WebStateImpl::OnAuthRequired(
     NSURLProtectionSpace* protection_space,
     NSURLCredential* proposed_credential,
diff --git a/ios/web/web_state/web_state_impl_unittest.mm b/ios/web/web_state/web_state_impl_unittest.mm
index b2a16a2..1d84b6a2 100644
--- a/ios/web/web_state/web_state_impl_unittest.mm
+++ b/ios/web/web_state/web_state_impl_unittest.mm
@@ -422,6 +422,37 @@
   TestWebStateDelegate delegate;
   web_state_->SetDelegate(&delegate);
 
+  // Test that CreateNewWebState() is called.
+  GURL child_url("https://child.test/");
+  GURL opener_url("https://opener.test/");
+  EXPECT_FALSE(delegate.last_create_new_web_state_request());
+  web_state_->CreateNewWebState(child_url, opener_url, true);
+  TestCreateNewWebStateRequest* create_new_web_state_request =
+      delegate.last_create_new_web_state_request();
+  ASSERT_TRUE(create_new_web_state_request);
+  EXPECT_EQ(web_state_.get(), create_new_web_state_request->web_state);
+  EXPECT_EQ(child_url, create_new_web_state_request->url);
+  EXPECT_EQ(opener_url, create_new_web_state_request->opener_url);
+  EXPECT_TRUE(create_new_web_state_request->initiated_by_user);
+
+  // Test that OpenURLFromWebState() is called.
+  WebState::OpenURLParams params(GURL("https://chromium.test/"), Referrer(),
+                                 WindowOpenDisposition::CURRENT_TAB,
+                                 ui::PAGE_TRANSITION_LINK, true);
+  EXPECT_FALSE(delegate.last_open_url_request());
+  web_state_->OpenURL(params);
+  TestOpenURLRequest* open_url_request = delegate.last_open_url_request();
+  ASSERT_TRUE(open_url_request);
+  EXPECT_EQ(web_state_.get(), open_url_request->web_state);
+  WebState::OpenURLParams actual_params = open_url_request->params;
+  EXPECT_EQ(params.url, actual_params.url);
+  EXPECT_EQ(params.referrer.url, actual_params.referrer.url);
+  EXPECT_EQ(params.referrer.policy, actual_params.referrer.policy);
+  EXPECT_EQ(params.disposition, actual_params.disposition);
+  EXPECT_TRUE(
+      PageTransitionCoreTypeIs(params.transition, actual_params.transition));
+  EXPECT_EQ(params.is_renderer_initiated, actual_params.is_renderer_initiated);
+
   // Test that HandleContextMenu() is called.
   EXPECT_FALSE(delegate.handle_context_menu_called());
   web::ContextMenuParams context_menu_params;
diff --git a/ios/web_view/shell/shell_view_controller.h b/ios/web_view/shell/shell_view_controller.h
index acf4b60..4f9fe17 100644
--- a/ios/web_view/shell/shell_view_controller.h
+++ b/ios/web_view/shell/shell_view_controller.h
@@ -7,9 +7,6 @@
 
 #import <UIKit/UIKit.h>
 
-#import "ios/web_view/public/cwv_navigation_delegate.h"
-#import "ios/web_view/public/cwv_ui_delegate.h"
-
 // Accessibility label added to the back button.
 extern NSString* const kWebViewShellBackButtonAccessibilityLabel;
 // Accessibility label added to the forward button.
diff --git a/ios/web_view/shell/shell_view_controller.m b/ios/web_view/shell/shell_view_controller.m
index f29d4cb7..f96b0997 100644
--- a/ios/web_view/shell/shell_view_controller.m
+++ b/ios/web_view/shell/shell_view_controller.m
@@ -8,6 +8,8 @@
 
 #import "ios/web_view/public/cwv.h"
 #import "ios/web_view/public/cwv_html_element.h"
+#import "ios/web_view/public/cwv_navigation_delegate.h"
+#import "ios/web_view/public/cwv_ui_delegate.h"
 #import "ios/web_view/public/cwv_web_view.h"
 #import "ios/web_view/shell/translate_controller.h"
 
diff --git a/mojo/edk/embedder/BUILD.gn b/mojo/edk/embedder/BUILD.gn
index b3fa007..8105bed 100644
--- a/mojo/edk/embedder/BUILD.gn
+++ b/mojo/edk/embedder/BUILD.gn
@@ -7,6 +7,7 @@
 source_set("headers") {
   sources = [
     "configuration.h",
+    "connection_params.h",
     "embedder.h",
     "embedder_internal.h",
     "named_platform_channel_pair.h",
@@ -35,6 +36,8 @@
 
   sources = [
     "configuration.h",
+    "connection_params.cc",
+    "connection_params.h",
     "embedder.cc",
     "embedder.h",
     "embedder_internal.h",
diff --git a/mojo/edk/embedder/connection_params.cc b/mojo/edk/embedder/connection_params.cc
new file mode 100644
index 0000000..9b7ec54
--- /dev/null
+++ b/mojo/edk/embedder/connection_params.cc
@@ -0,0 +1,28 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "mojo/edk/embedder/connection_params.h"
+
+#include <utility>
+
+namespace mojo {
+namespace edk {
+
+ConnectionParams::ConnectionParams(ScopedPlatformHandle channel)
+    : channel_(std::move(channel)) {}
+
+ConnectionParams::ConnectionParams(ConnectionParams&& param)
+    : channel_(std::move(param.channel_)) {}
+
+ConnectionParams& ConnectionParams::operator=(ConnectionParams&& param) {
+  channel_ = std::move(param.channel_);
+  return *this;
+}
+
+ScopedPlatformHandle ConnectionParams::TakeChannelHandle() {
+  return std::move(channel_);
+}
+
+}  // namespace edk
+}  // namespace mojo
diff --git a/mojo/edk/embedder/connection_params.h b/mojo/edk/embedder/connection_params.h
new file mode 100644
index 0000000..25ffdde7
--- /dev/null
+++ b/mojo/edk/embedder/connection_params.h
@@ -0,0 +1,34 @@
+// 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 MOJO_EDK_EMBEDDER_CONNECTION_PARAMS_H_
+#define MOJO_EDK_EMBEDDER_CONNECTION_PARAMS_H_
+
+#include "base/macros.h"
+#include "build/build_config.h"
+#include "mojo/edk/embedder/scoped_platform_handle.h"
+#include "mojo/edk/system/system_impl_export.h"
+
+namespace mojo {
+namespace edk {
+
+class MOJO_SYSTEM_IMPL_EXPORT ConnectionParams {
+ public:
+  explicit ConnectionParams(ScopedPlatformHandle channel);
+
+  ConnectionParams(ConnectionParams&& param);
+  ConnectionParams& operator=(ConnectionParams&& param);
+
+  ScopedPlatformHandle TakeChannelHandle();
+
+ private:
+  ScopedPlatformHandle channel_;
+
+  DISALLOW_COPY_AND_ASSIGN(ConnectionParams);
+};
+
+}  // namespace edk
+}  // namespace mojo
+
+#endif  // MOJO_EDK_EMBEDDER_CONNECTION_PARAMS_H_
diff --git a/mojo/edk/embedder/embedder.cc b/mojo/edk/embedder/embedder.cc
index d581e65..0fdda5cd 100644
--- a/mojo/edk/embedder/embedder.cc
+++ b/mojo/edk/embedder/embedder.cc
@@ -44,7 +44,7 @@
 
 void SetParentPipeHandle(ScopedPlatformHandle pipe) {
   CHECK(internal::g_core);
-  internal::g_core->InitChild(std::move(pipe));
+  internal::g_core->InitChild(ConnectionParams(std::move(pipe)));
 }
 
 void SetParentPipeHandleFromCommandLine() {
diff --git a/mojo/edk/embedder/embedder_unittest.cc b/mojo/edk/embedder/embedder_unittest.cc
index 2eb6beb..d5a87e5 100644
--- a/mojo/edk/embedder/embedder_unittest.cc
+++ b/mojo/edk/embedder/embedder_unittest.cc
@@ -210,7 +210,8 @@
   PendingProcessConnection process;
   std::string pipe_token;
   ScopedMessagePipeHandle parent_mp = process.CreateMessagePipe(&pipe_token);
-  process.Connect(base::GetCurrentProcessHandle(), pair.PassServerHandle());
+  process.Connect(base::GetCurrentProcessHandle(),
+                  ConnectionParams(pair.PassServerHandle()));
 
   // Close the remote end, simulating child death before the child connects to
   // the reserved port.
diff --git a/mojo/edk/embedder/pending_process_connection.cc b/mojo/edk/embedder/pending_process_connection.cc
index 8ab58727..d6be76e 100644
--- a/mojo/edk/embedder/pending_process_connection.cc
+++ b/mojo/edk/embedder/pending_process_connection.cc
@@ -33,7 +33,7 @@
 
 void PendingProcessConnection::Connect(
     base::ProcessHandle process,
-    ScopedPlatformHandle channel,
+    ConnectionParams connection_params,
     const ProcessErrorCallback& error_callback) {
   // It's now safe to avoid cleanup in the destructor, as the lifetime of any
   // associated resources is effectively bound to the |channel| passed to
@@ -42,8 +42,8 @@
   connected_ = true;
 
   DCHECK(internal::g_core);
-  internal::g_core->AddChild(process, std::move(channel), process_token_,
-                             error_callback);
+  internal::g_core->AddChild(process, std::move(connection_params),
+                             process_token_, error_callback);
 }
 
 }  // namespace edk
diff --git a/mojo/edk/embedder/pending_process_connection.h b/mojo/edk/embedder/pending_process_connection.h
index 1f5cad9..ca182276 100644
--- a/mojo/edk/embedder/pending_process_connection.h
+++ b/mojo/edk/embedder/pending_process_connection.h
@@ -10,6 +10,7 @@
 #include "base/callback.h"
 #include "base/macros.h"
 #include "base/process/process_handle.h"
+#include "mojo/edk/embedder/connection_params.h"
 #include "mojo/edk/embedder/scoped_platform_handle.h"
 #include "mojo/edk/system/system_impl_export.h"
 #include "mojo/public/cpp/system/message_pipe.h"
@@ -88,9 +89,9 @@
   // Connects to the process. This must be called at most once, with the process
   // handle in |process|.
   //
-  // |channel| is the platform handle of an OS pipe which can be used to
-  // communicate with the connected process. The other end of that pipe must
-  // ultimately be passed to mojo::edk::SetParentPipeHandle in the remote
+  // |connection_param| contains the platform handle of an OS pipe which can be
+  // used to communicate with the connected process. The other end of that pipe
+  // must ultimately be passed to mojo::edk::SetParentPipeHandle in the remote
   // process, and getting that end of the pipe into the other process is the
   // embedder's responsibility.
   //
@@ -101,7 +102,7 @@
   // created by CreateMessagePipe above) will be cleaned up at that time.
   void Connect(
       base::ProcessHandle process,
-      ScopedPlatformHandle channel,
+      ConnectionParams connection_params,
       const ProcessErrorCallback& error_callback = ProcessErrorCallback());
 
  private:
diff --git a/mojo/edk/js/tests/run_js_unittests.cc b/mojo/edk/js/tests/run_js_unittests.cc
index bc5e242..a7b70b7f 100644
--- a/mojo/edk/js/tests/run_js_unittests.cc
+++ b/mojo/edk/js/tests/run_js_unittests.cc
@@ -45,10 +45,6 @@
 }
 
 // TODO(abarth): Should we autogenerate these stubs from GYP?
-TEST(JSTest, Binding) {
-  RunTest("binding_unittest.js", false);
-}
-
 TEST(JSTest, Codec) {
   RunTest("codec_unittest.js", true);
 }
diff --git a/mojo/edk/system/broker_host.cc b/mojo/edk/system/broker_host.cc
index 28d51573..6096034 100644
--- a/mojo/edk/system/broker_host.cc
+++ b/mojo/edk/system/broker_host.cc
@@ -29,8 +29,8 @@
 
   base::MessageLoop::current()->AddDestructionObserver(this);
 
-  channel_ = Channel::Create(
-      this, std::move(platform_handle), base::ThreadTaskRunnerHandle::Get());
+  channel_ = Channel::Create(this, ConnectionParams(std::move(platform_handle)),
+                             base::ThreadTaskRunnerHandle::Get());
   channel_->Start();
 }
 
diff --git a/mojo/edk/system/channel.h b/mojo/edk/system/channel.h
index 0efc0ac..33a510c 100644
--- a/mojo/edk/system/channel.h
+++ b/mojo/edk/system/channel.h
@@ -10,6 +10,7 @@
 #include "base/memory/ref_counted.h"
 #include "base/process/process_handle.h"
 #include "base/task_runner.h"
+#include "mojo/edk/embedder/connection_params.h"
 #include "mojo/edk/embedder/platform_handle_vector.h"
 #include "mojo/edk/embedder/scoped_platform_handle.h"
 
@@ -102,7 +103,7 @@
       // Actual number of Mach ports encoded in the extra header.
       uint16_t num_ports;
 
-      // Array of encoded Mach ports. If |num_ports| > 0, |entires[0]| through
+      // Array of encoded Mach ports. If |num_ports| > 0, |entries[0]| through
       // to |entries[num_ports-1]| inclusive are valid.
       MachPortsEntry entries[0];
     };
@@ -213,7 +214,7 @@
   // |delegate| is destroyed.
   static scoped_refptr<Channel> Create(
       Delegate* delegate,
-      ScopedPlatformHandle platform_handle,
+      ConnectionParams connection_params,
       scoped_refptr<base::TaskRunner> io_task_runner);
 
   // Request that the channel be shut down. This should always be called before
diff --git a/mojo/edk/system/channel_posix.cc b/mojo/edk/system/channel_posix.cc
index a7cf9e3..8b4ca7fd 100644
--- a/mojo/edk/system/channel_posix.cc
+++ b/mojo/edk/system/channel_posix.cc
@@ -88,17 +88,18 @@
                      public base::MessageLoopForIO::Watcher {
  public:
   ChannelPosix(Delegate* delegate,
-               ScopedPlatformHandle handle,
+               ConnectionParams connection_params,
                scoped_refptr<base::TaskRunner> io_task_runner)
       : Channel(delegate),
         self_(this),
-        handle_(std::move(handle)),
+        handle_(connection_params.TakeChannelHandle()),
         io_task_runner_(io_task_runner)
 #if defined(OS_MACOSX)
         ,
         handles_to_close_(new PlatformHandleVector)
 #endif
   {
+    CHECK(handle_.is_valid());
   }
 
   void Start() override {
@@ -561,9 +562,10 @@
 // static
 scoped_refptr<Channel> Channel::Create(
     Delegate* delegate,
-    ScopedPlatformHandle platform_handle,
+    ConnectionParams connection_params,
     scoped_refptr<base::TaskRunner> io_task_runner) {
-  return new ChannelPosix(delegate, std::move(platform_handle), io_task_runner);
+  return new ChannelPosix(delegate, std::move(connection_params),
+                          io_task_runner);
 }
 
 }  // namespace edk
diff --git a/mojo/edk/system/channel_win.cc b/mojo/edk/system/channel_win.cc
index 232577ff..c15df16b 100644
--- a/mojo/edk/system/channel_win.cc
+++ b/mojo/edk/system/channel_win.cc
@@ -350,9 +350,10 @@
 // static
 scoped_refptr<Channel> Channel::Create(
     Delegate* delegate,
-    ScopedPlatformHandle platform_handle,
+    ConnectionParams connection_params,
     scoped_refptr<base::TaskRunner> io_task_runner) {
-  return new ChannelWin(delegate, std::move(platform_handle), io_task_runner);
+  return new ChannelWin(delegate, connection_params.TakeChannelHandle(),
+                        io_task_runner);
 }
 
 }  // namespace edk
diff --git a/mojo/edk/system/core.cc b/mojo/edk/system/core.cc
index a5e432b..1e0bf4e 100644
--- a/mojo/edk/system/core.cc
+++ b/mojo/edk/system/core.cc
@@ -172,12 +172,11 @@
 }
 
 void Core::AddChild(base::ProcessHandle process_handle,
-                    ScopedPlatformHandle platform_handle,
+                    ConnectionParams connection_params,
                     const std::string& child_token,
                     const ProcessErrorCallback& process_error_callback) {
   GetNodeController()->ConnectToChild(process_handle,
-                                      std::move(platform_handle),
-                                      child_token,
+                                      std::move(connection_params), child_token,
                                       process_error_callback);
 }
 
@@ -194,7 +193,9 @@
   GetNodeController()->node()->CreatePortPair(&port0, &port1);
   MojoHandle handle = AddDispatcher(new MessagePipeDispatcher(
       GetNodeController(), port0, kUnknownPipeIdForDebug, 0));
-  GetNodeController()->ConnectToPeer(std::move(pipe_handle), port1, peer_token);
+  ConnectionParams connection_params(std::move(pipe_handle));
+  GetNodeController()->ConnectToPeer(std::move(connection_params), port1,
+                                     peer_token);
   return ScopedMessagePipeHandle(MessagePipeHandle(handle));
 }
 
@@ -202,8 +203,8 @@
   GetNodeController()->ClosePeerConnection(peer_token);
 }
 
-void Core::InitChild(ScopedPlatformHandle platform_handle) {
-  GetNodeController()->ConnectToParent(std::move(platform_handle));
+void Core::InitChild(ConnectionParams connection_params) {
+  GetNodeController()->ConnectToParent(std::move(connection_params));
 }
 
 void Core::SetMachPortProvider(base::PortProvider* port_provider) {
diff --git a/mojo/edk/system/core.h b/mojo/edk/system/core.h
index db2e8cf..1e20a87 100644
--- a/mojo/edk/system/core.h
+++ b/mojo/edk/system/core.h
@@ -56,7 +56,7 @@
 
   // Called in the parent process any time a new child is launched.
   void AddChild(base::ProcessHandle process_handle,
-                ScopedPlatformHandle platform_handle,
+                ConnectionParams connection_params,
                 const std::string& child_token,
                 const ProcessErrorCallback& process_error_callback);
 
@@ -72,7 +72,7 @@
   void ClosePeerConnection(const std::string& peer_token);
 
   // Called in a child process exactly once during early initialization.
-  void InitChild(ScopedPlatformHandle platform_handle);
+  void InitChild(ConnectionParams connection_params);
 
   // Creates a message pipe endpoint associated with |token|, which a child
   // holding the token can later locate and connect to.
diff --git a/mojo/edk/system/node_channel.cc b/mojo/edk/system/node_channel.cc
index 91a69c3..b0f770d90 100644
--- a/mojo/edk/system/node_channel.cc
+++ b/mojo/edk/system/node_channel.cc
@@ -160,14 +160,14 @@
 // static
 scoped_refptr<NodeChannel> NodeChannel::Create(
     Delegate* delegate,
-    ScopedPlatformHandle platform_handle,
+    ConnectionParams connection_params,
     scoped_refptr<base::TaskRunner> io_task_runner,
     const ProcessErrorCallback& process_error_callback) {
 #if defined(OS_NACL_SFI)
   LOG(FATAL) << "Multi-process not yet supported on NaCl-SFI";
   return nullptr;
 #else
-  return new NodeChannel(delegate, std::move(platform_handle), io_task_runner,
+  return new NodeChannel(delegate, std::move(connection_params), io_task_runner,
                          process_error_callback);
 #endif
 }
@@ -454,17 +454,18 @@
 #endif  // defined(OS_WIN) || (defined(OS_MACOSX) && !defined(OS_IOS))
 
 NodeChannel::NodeChannel(Delegate* delegate,
-                         ScopedPlatformHandle platform_handle,
+                         ConnectionParams connection_params,
                          scoped_refptr<base::TaskRunner> io_task_runner,
                          const ProcessErrorCallback& process_error_callback)
     : delegate_(delegate),
       io_task_runner_(io_task_runner),
       process_error_callback_(process_error_callback)
 #if !defined(OS_NACL_SFI)
-      , channel_(
-          Channel::Create(this, std::move(platform_handle), io_task_runner_))
+      ,
+      channel_(
+          Channel::Create(this, std::move(connection_params), io_task_runner_))
 #endif
-      {
+{
 }
 
 NodeChannel::~NodeChannel() {
diff --git a/mojo/edk/system/node_channel.h b/mojo/edk/system/node_channel.h
index bbac642..95dc3410 100644
--- a/mojo/edk/system/node_channel.h
+++ b/mojo/edk/system/node_channel.h
@@ -16,6 +16,7 @@
 #include "base/synchronization/lock.h"
 #include "base/task_runner.h"
 #include "build/build_config.h"
+#include "mojo/edk/embedder/connection_params.h"
 #include "mojo/edk/embedder/embedder.h"
 #include "mojo/edk/embedder/platform_handle_vector.h"
 #include "mojo/edk/embedder/scoped_platform_handle.h"
@@ -90,7 +91,7 @@
 
   static scoped_refptr<NodeChannel> Create(
       Delegate* delegate,
-      ScopedPlatformHandle platform_handle,
+      ConnectionParams connection_params,
       scoped_refptr<base::TaskRunner> io_task_runner,
       const ProcessErrorCallback& process_error_callback);
 
@@ -167,7 +168,7 @@
       std::queue<std::pair<ports::NodeName, Channel::MessagePtr>>;
 
   NodeChannel(Delegate* delegate,
-              ScopedPlatformHandle platform_handle,
+              ConnectionParams connection_params,
               scoped_refptr<base::TaskRunner> io_task_runner,
               const ProcessErrorCallback& process_error_callback);
   ~NodeChannel() override;
diff --git a/mojo/edk/system/node_controller.cc b/mojo/edk/system/node_controller.cc
index 0d16c5ea..7bdb571 100644
--- a/mojo/edk/system/node_controller.cc
+++ b/mojo/edk/system/node_controller.cc
@@ -165,7 +165,7 @@
 
 void NodeController::ConnectToChild(
     base::ProcessHandle process_handle,
-    ScopedPlatformHandle platform_handle,
+    ConnectionParams connection_params,
     const std::string& child_token,
     const ProcessErrorCallback& process_error_callback) {
   // Generate the temporary remote node name here so that it can be associated
@@ -196,13 +196,10 @@
 #endif
 
   io_task_runner_->PostTask(
-      FROM_HERE,
-      base::Bind(&NodeController::ConnectToChildOnIOThread,
-                 base::Unretained(this),
-                 process_handle,
-                 base::Passed(&platform_handle),
-                 node_name,
-                 process_error_callback));
+      FROM_HERE, base::Bind(&NodeController::ConnectToChildOnIOThread,
+                            base::Unretained(this), process_handle,
+                            base::Passed(&connection_params), node_name,
+                            process_error_callback));
 }
 
 void NodeController::CloseChildPorts(const std::string& child_token) {
@@ -235,13 +232,13 @@
                             base::Unretained(this), peer_token));
 }
 
-void NodeController::ConnectToParent(ScopedPlatformHandle platform_handle) {
+void NodeController::ConnectToParent(ConnectionParams connection_params) {
 #if !defined(OS_MACOSX) && !defined(OS_NACL_SFI)
   // Use the bootstrap channel for the broker and receive the node's channel
   // synchronously as the first message from the broker.
   base::ElapsedTimer timer;
-  broker_.reset(new Broker(std::move(platform_handle)));
-  platform_handle = broker_->GetParentPlatformHandle();
+  broker_.reset(new Broker(connection_params.TakeChannelHandle()));
+  ScopedPlatformHandle platform_handle = broker_->GetParentPlatformHandle();
   UMA_HISTOGRAM_TIMES("Mojo.System.GetParentPlatformHandleSyncTime",
                       timer.Elapsed());
 
@@ -253,24 +250,25 @@
     CancelPendingPortMerges();
     return;
   }
+  connection_params = ConnectionParams(std::move(platform_handle));
 #endif
 
   io_task_runner_->PostTask(
       FROM_HERE,
       base::Bind(&NodeController::ConnectToParentOnIOThread,
-                 base::Unretained(this),
-                 base::Passed(&platform_handle)));
+                 base::Unretained(this), base::Passed(&connection_params)));
 }
 
-void NodeController::ConnectToPeer(ScopedPlatformHandle handle,
+void NodeController::ConnectToPeer(ConnectionParams connection_params,
                                    const ports::PortRef& port,
                                    const std::string& peer_token) {
   ports::NodeName node_name;
   GenerateRandomName(&node_name);
   io_task_runner_->PostTask(
-      FROM_HERE, base::Bind(&NodeController::ConnectToPeerOnIOThread,
-                            base::Unretained(this), base::Passed(&handle),
-                            node_name, port, peer_token));
+      FROM_HERE,
+      base::Bind(&NodeController::ConnectToPeerOnIOThread,
+                 base::Unretained(this), base::Passed(&connection_params),
+                 node_name, port, peer_token));
 }
 
 void NodeController::SetPortObserver(const ports::PortRef& port,
@@ -394,7 +392,7 @@
 
 void NodeController::ConnectToChildOnIOThread(
     base::ProcessHandle process_handle,
-    ScopedPlatformHandle platform_handle,
+    ConnectionParams connection_params,
     ports::NodeName token,
     const ProcessErrorCallback& process_error_callback) {
   DCHECK(io_task_runner_->RunsTasksOnCurrentThread());
@@ -404,7 +402,7 @@
   ScopedPlatformHandle server_handle = node_channel.PassServerHandle();
   // BrokerHost owns itself.
   BrokerHost* broker_host =
-      new BrokerHost(process_handle, std::move(platform_handle));
+      new BrokerHost(process_handle, connection_params.TakeChannelHandle());
   bool channel_ok = broker_host->SendChannel(node_channel.PassClientHandle());
 
 #if defined(OS_WIN)
@@ -419,12 +417,13 @@
   CHECK(channel_ok);
 #endif  // defined(OS_WIN)
 
-  scoped_refptr<NodeChannel> channel = NodeChannel::Create(
-      this, std::move(server_handle), io_task_runner_, process_error_callback);
+  scoped_refptr<NodeChannel> channel =
+      NodeChannel::Create(this, ConnectionParams(std::move(server_handle)),
+                          io_task_runner_, process_error_callback);
 
 #else  // !defined(OS_MACOSX) && !defined(OS_NACL)
   scoped_refptr<NodeChannel> channel =
-      NodeChannel::Create(this, std::move(platform_handle), io_task_runner_,
+      NodeChannel::Create(this, std::move(connection_params), io_task_runner_,
                           process_error_callback);
 #endif  // !defined(OS_MACOSX) && !defined(OS_NACL)
 
@@ -444,7 +443,7 @@
 }
 
 void NodeController::ConnectToParentOnIOThread(
-    ScopedPlatformHandle platform_handle) {
+    ConnectionParams connection_params) {
   DCHECK(io_task_runner_->RunsTasksOnCurrentThread());
 
   {
@@ -455,7 +454,7 @@
     // into our |peers_| map. That will happen as soon as we receive an
     // AcceptChild message from them.
     bootstrap_parent_channel_ =
-        NodeChannel::Create(this, std::move(platform_handle), io_task_runner_,
+        NodeChannel::Create(this, std::move(connection_params), io_task_runner_,
                             ProcessErrorCallback());
     // Prevent the parent pipe handle from being closed on shutdown. Pipe
     // closure is used by the parent to detect the child process has exited.
@@ -467,14 +466,14 @@
   bootstrap_parent_channel_->Start();
 }
 
-void NodeController::ConnectToPeerOnIOThread(ScopedPlatformHandle handle,
+void NodeController::ConnectToPeerOnIOThread(ConnectionParams connection_params,
                                              ports::NodeName token,
                                              ports::PortRef port,
                                              const std::string& peer_token) {
   DCHECK(io_task_runner_->RunsTasksOnCurrentThread());
 
-  scoped_refptr<NodeChannel> channel =
-      NodeChannel::Create(this, std::move(handle), io_task_runner_, {});
+  scoped_refptr<NodeChannel> channel = NodeChannel::Create(
+      this, std::move(connection_params), io_task_runner_, {});
   peer_connections_.insert(
       {token, PeerConnection{channel, port, peer_token}});
   peers_by_token_.insert({peer_token, token});
@@ -994,9 +993,10 @@
   }
 
   PlatformChannelPair broker_channel;
-  scoped_refptr<NodeChannel> client = NodeChannel::Create(
-      this, broker_channel.PassServerHandle(), io_task_runner_,
-      ProcessErrorCallback());
+  ConnectionParams connection_params(broker_channel.PassServerHandle());
+  scoped_refptr<NodeChannel> client =
+      NodeChannel::Create(this, std::move(connection_params), io_task_runner_,
+                          ProcessErrorCallback());
 
 #if defined(OS_WIN)
   // The broker must have a working handle to the client process in order to
@@ -1072,8 +1072,9 @@
     broker = parent;
   } else {
     DCHECK(broker_channel.is_valid());
-    broker = NodeChannel::Create(this, std::move(broker_channel),
-                                 io_task_runner_, ProcessErrorCallback());
+    broker =
+        NodeChannel::Create(this, ConnectionParams(std::move(broker_channel)),
+                            io_task_runner_, ProcessErrorCallback());
     AddPeer(broker_name, broker, true /* start_channel */);
   }
 
@@ -1201,8 +1202,8 @@
   }
 
   scoped_refptr<NodeChannel> channel =
-      NodeChannel::Create(this, std::move(channel_handle), io_task_runner_,
-                          ProcessErrorCallback());
+      NodeChannel::Create(this, ConnectionParams(std::move(channel_handle)),
+                          io_task_runner_, ProcessErrorCallback());
 
   DVLOG(1) << "Adding new peer " << name << " via parent introduction.";
   AddPeer(name, channel, true /* start_channel */);
diff --git a/mojo/edk/system/node_controller.h b/mojo/edk/system/node_controller.h
index 9458a08..46a2d61 100644
--- a/mojo/edk/system/node_controller.h
+++ b/mojo/edk/system/node_controller.h
@@ -73,7 +73,7 @@
 
   // Connects this node to a child node. This node will initiate a handshake.
   void ConnectToChild(base::ProcessHandle process_handle,
-                      ScopedPlatformHandle platform_handle,
+                      ConnectionParams connection_params,
                       const std::string& child_token,
                       const ProcessErrorCallback& process_error_callback);
 
@@ -86,11 +86,11 @@
 
   // Connects this node to a parent node. The parent node will initiate a
   // handshake.
-  void ConnectToParent(ScopedPlatformHandle platform_handle);
+  void ConnectToParent(ConnectionParams connection_params);
 
   // Connects this node to a peer node. On success, |port| will be merged with
   // the corresponding port in the peer node.
-  void ConnectToPeer(ScopedPlatformHandle handle,
+  void ConnectToPeer(ConnectionParams connection_params,
                      const ports::PortRef& port,
                      const std::string& peer_token);
 
@@ -168,12 +168,12 @@
 
   void ConnectToChildOnIOThread(
       base::ProcessHandle process_handle,
-      ScopedPlatformHandle platform_handle,
+      ConnectionParams connection_params,
       ports::NodeName token,
       const ProcessErrorCallback& process_error_callback);
-  void ConnectToParentOnIOThread(ScopedPlatformHandle platform_handle);
+  void ConnectToParentOnIOThread(ConnectionParams connection_params);
 
-  void ConnectToPeerOnIOThread(ScopedPlatformHandle handle,
+  void ConnectToPeerOnIOThread(ConnectionParams connection_params,
                                ports::NodeName token,
                                ports::PortRef port,
                                const std::string& peer_token);
diff --git a/mojo/edk/test/multiprocess_test_helper.cc b/mojo/edk/test/multiprocess_test_helper.cc
index 8eaccf0..de6e2d9 100644
--- a/mojo/edk/test/multiprocess_test_helper.cc
+++ b/mojo/edk/test/multiprocess_test_helper.cc
@@ -168,7 +168,8 @@
   if (launch_type == LaunchType::CHILD ||
       launch_type == LaunchType::NAMED_CHILD) {
     DCHECK(server_handle.is_valid());
-    process.Connect(test_child_.Handle(), std::move(server_handle),
+    process.Connect(test_child_.Handle(),
+                    ConnectionParams(std::move(server_handle)),
                     process_error_callback_);
   }
 
diff --git a/mojo/public/js/BUILD.gn b/mojo/public/js/BUILD.gn
index 6cb9f5fd..0fae4b4 100644
--- a/mojo/public/js/BUILD.gn
+++ b/mojo/public/js/BUILD.gn
@@ -39,7 +39,6 @@
 
   data = [
     "//mojo/public/interfaces/bindings/tests/data/validation/",
-    "tests/binding_unittest.js",
     "tests/codec_unittest.js",
     "tests/connection_unittest.js",
     "tests/core_unittest.js",
diff --git a/mojo/public/js/tests/binding_unittest.js b/mojo/public/js/tests/binding_unittest.js
deleted file mode 100644
index 7bf9a89..0000000
--- a/mojo/public/js/tests/binding_unittest.js
+++ /dev/null
@@ -1,159 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-define([
-    "gin/test/expect",
-    "mojo/public/js/bindings",
-    "mojo/public/interfaces/bindings/tests/math_calculator.mojom",
-    "mojo/public/js/threading",
-    "gc",
-], function(expect,
-            bindings,
-            math,
-            threading,
-            gc) {
-  testIsBound()
-      .then(testReusable)
-      .then(testConnectionError)
-      .then(testUnbind)
-      .then(testBindingSet)
-      .then(function() {
-    this.result = "PASS";
-    gc.collectGarbage();  // should not crash
-    threading.quit();
-  }.bind(this)).catch(function(e) {
-    this.result = "FAIL: " + (e.stack || e);
-    threading.quit();
-  }.bind(this));
-
-  function CalculatorImpl() {
-    this.total = 0;
-  }
-
-  CalculatorImpl.prototype.clear = function() {
-    this.total = 0;
-    return Promise.resolve({value: this.total});
-  };
-
-  CalculatorImpl.prototype.add = function(value) {
-    this.total += value;
-    return Promise.resolve({value: this.total});
-  };
-
-  CalculatorImpl.prototype.multiply = function(value) {
-    this.total *= value;
-    return Promise.resolve({value: this.total});
-  };
-
-  function testIsBound() {
-    var binding = new bindings.Binding(math.Calculator, new CalculatorImpl());
-    expect(binding.isBound()).toBeFalsy();
-
-    var calc = new math.CalculatorPtr();
-    var request = bindings.makeRequest(calc);
-    binding.bind(request);
-    expect(binding.isBound()).toBeTruthy();
-
-    binding.close();
-    expect(binding.isBound()).toBeFalsy();
-
-    return Promise.resolve();
-  }
-
-  function testReusable() {
-    var calc1 = new math.CalculatorPtr();
-    var calc2 = new math.CalculatorPtr();
-
-    var calcBinding = new bindings.Binding(math.Calculator,
-                                           new CalculatorImpl(),
-                                           bindings.makeRequest(calc1));
-
-    var promise = calc1.add(2).then(function(response) {
-      expect(response.value).toBe(2);
-      calcBinding.bind(bindings.makeRequest(calc2));
-      return calc2.add(2);
-    }).then(function(response) {
-      expect(response.value).toBe(4);
-      return Promise.resolve();
-    });
-
-    return promise;
-  }
-
-  function testConnectionError() {
-    var calc = new math.CalculatorPtr();
-    var calcBinding = new bindings.Binding(math.Calculator,
-                                           new CalculatorImpl(),
-                                           bindings.makeRequest(calc));
-
-    var promise = new Promise(function(resolve, reject) {
-      calcBinding.setConnectionErrorHandler(function() {
-        resolve();
-      });
-      calc.ptr.reset();
-    });
-
-    return promise;
-  }
-
-  function testUnbind() {
-    var calc = new math.CalculatorPtr();
-    var calcBinding = new bindings.Binding(math.Calculator,
-                                           new CalculatorImpl(),
-                                           bindings.makeRequest(calc));
-    var newCalcBinding = null;
-
-    var promise = calc.add(2).then(function(response) {
-      expect(response.value).toBe(2);
-      var interfaceRequest = calcBinding.unbind();
-      expect(calcBinding.isBound()).toBeFalsy();
-      newCalcBinding = new bindings.Binding(math.Calculator,
-                                            new CalculatorImpl(),
-                                            interfaceRequest);
-      return calc.add(2);
-    }).then(function(response) {
-      expect(response.value).toBe(2);
-      return Promise.resolve();
-    });
-
-    return promise;
-  }
-
-  function testBindingSet() {
-    var calc1 = new math.CalculatorPtr();
-    var calc2 = new math.CalculatorPtr();
-    var calcImpl = new CalculatorImpl();
-
-    var bindingSet = new bindings.BindingSet(math.Calculator);
-    expect(bindingSet.isEmpty()).toBeTruthy();
-    bindingSet.addBinding(calcImpl, bindings.makeRequest(calc1));
-    bindingSet.addBinding(calcImpl, bindings.makeRequest(calc2));
-    expect(bindingSet.isEmpty()).toBeFalsy();
-
-    var promise = calc1.add(3).then(function(response) {
-      expect(response.value).toBe(3);
-      return calc2.add(4);
-    }).then(function(response) {
-      expect(response.value).toBe(7);
-
-      var promiseOfConnectionError = new Promise(function(resolve, reject) {
-        bindingSet.setConnectionErrorHandler(function() {
-          resolve();
-        });
-      });
-      calc1.ptr.reset();
-      return promiseOfConnectionError;
-    }).then(function() {
-      return calc2.add(5);
-    }).then(function(response) {
-      expect(response.value).toBe(12);
-
-      bindingSet.closeAllBindings();
-      expect(bindingSet.isEmpty()).toBeTruthy();
-      return Promise.resolve();
-    });
-
-    return promise;
-  }
-});
diff --git a/net/nqe/network_quality_estimator.cc b/net/nqe/network_quality_estimator.cc
index 13e56e6..d1f87e9 100644
--- a/net/nqe/network_quality_estimator.cc
+++ b/net/nqe/network_quality_estimator.cc
@@ -845,7 +845,8 @@
   rtt_observations_.Clear();
 
 #if defined(OS_ANDROID)
-  if (NetworkChangeNotifier::IsConnectionCellular(current_network_id_.type)) {
+  if (weight_multiplier_per_dbm_ < 1.0 &&
+      NetworkChangeNotifier::IsConnectionCellular(current_network_id_.type)) {
     UMA_HISTOGRAM_BOOLEAN(
         "NQE.CellularSignalStrengthAvailable",
         min_signal_strength_since_connection_change_ != INT32_MAX &&
@@ -906,7 +907,8 @@
 
 void NetworkQualityEstimator::UpdateSignalStrength() {
 #if defined(OS_ANDROID)
-  if (!NetworkChangeNotifier::IsConnectionCellular(current_network_id_.type) ||
+  if (weight_multiplier_per_dbm_ >= 1.0 ||
+      !NetworkChangeNotifier::IsConnectionCellular(current_network_id_.type) ||
       !android::cellular_signal_strength::GetSignalStrengthDbm(
           &signal_strength_dbm_)) {
     signal_strength_dbm_ = INT32_MIN;
diff --git a/pdf/pdfium/DEPS b/pdf/pdfium/DEPS
index 0fdd714..cf1dc86 100644
--- a/pdf/pdfium/DEPS
+++ b/pdf/pdfium/DEPS
@@ -4,6 +4,6 @@
   "+printing/pdf_transform.h",
   "+printing/units.h",
   "+third_party/pdfium/public",
-  "+ui/gfx/geometry/rect.h",
   "+ui/gfx/codec/jpeg_codec.h",
+  "+ui/gfx/geometry/rect.h",
 ]
diff --git a/pdf/pdfium/pdfium_engine.cc b/pdf/pdfium/pdfium_engine.cc
index 450c2e4..a851901b 100644
--- a/pdf/pdfium/pdfium_engine.cc
+++ b/pdf/pdfium/pdfium_engine.cc
@@ -1444,7 +1444,10 @@
   FPDF_PAGEOBJECT temp_img = FPDFPageObj_NewImgeObj(temp_doc);
 
   std::vector<uint8_t> compressed_bitmap_data;
-  int quality = 40;
+  // Use quality = 40 as this does not significantly degrade the printed
+  // document relative to a normal bitmap and provides better compression than
+  // a higher quality setting.
+  const int quality = 40;
   if (!(print_settings.format & PP_PRINTOUTPUTFORMAT_PDF) &&
       (gfx::JPEGCodec::Encode(
           bitmap_data, gfx::JPEGCodec::FORMAT_BGRA, FPDFBitmap_GetWidth(bitmap),
diff --git a/printing/print_job_constants.cc b/printing/print_job_constants.cc
index b626e93d..2299621 100644
--- a/printing/print_job_constants.cc
+++ b/printing/print_job_constants.cc
@@ -172,7 +172,7 @@
 // Scaling factor
 const char kSettingScaleFactor[] = "scaleFactor";
 
-// Scaling factor
+// Whether to rasterize the PDF for printing.
 const char kSettingRasterizePdf[] = "rasterizePDF";
 
 // Ticket option. Contains the ticket in CJT format.
diff --git a/remoting/host/win/unprivileged_process_delegate.cc b/remoting/host/win/unprivileged_process_delegate.cc
index 5c0a6b5..265eb33 100644
--- a/remoting/host/win/unprivileged_process_delegate.cc
+++ b/remoting/host/win/unprivileged_process_delegate.cc
@@ -311,7 +311,8 @@
     ReportFatalError();
     return;
   }
-  process.Connect(worker_process.Get(), mojo_channel.PassServerHandle());
+  process.Connect(worker_process.Get(),
+                  mojo::edk::ConnectionParams(mojo_channel.PassServerHandle()));
 
   channel_ = std::move(server);
 
diff --git a/remoting/host/win/wts_session_process_delegate.cc b/remoting/host/win/wts_session_process_delegate.cc
index c665a59c..7376f69 100644
--- a/remoting/host/win/wts_session_process_delegate.cc
+++ b/remoting/host/win/wts_session_process_delegate.cc
@@ -555,7 +555,9 @@
   DCHECK(caller_task_runner_->BelongsToCurrentThread());
   DCHECK(!worker_process_.IsValid());
 
-  process_connection_->Connect(worker_process.Get(), std::move(server_handle));
+  process_connection_->Connect(
+      worker_process.Get(),
+      mojo::edk::ConnectionParams(std::move(server_handle)));
   process_connection_.reset();
   worker_process_ = std::move(worker_process);
 
diff --git a/services/preferences/public/cpp/BUILD.gn b/services/preferences/public/cpp/BUILD.gn
index 7271693..08518bd 100644
--- a/services/preferences/public/cpp/BUILD.gn
+++ b/services/preferences/public/cpp/BUILD.gn
@@ -6,6 +6,14 @@
   sources = [
     "pref_client_store.cc",
     "pref_client_store.h",
+    "pref_store_adapter.cc",
+    "pref_store_adapter.h",
+    "pref_store_client.cc",
+    "pref_store_client.h",
+    "pref_store_impl.cc",
+    "pref_store_impl.h",
+    "pref_store_manager_impl.cc",
+    "pref_store_manager_impl.h",
   ]
 
   public_deps = [
diff --git a/services/preferences/public/cpp/pref_store_adapter.cc b/services/preferences/public/cpp/pref_store_adapter.cc
new file mode 100644
index 0000000..2cfe85a
--- /dev/null
+++ b/services/preferences/public/cpp/pref_store_adapter.cc
@@ -0,0 +1,40 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "services/preferences/public/cpp/pref_store_adapter.h"
+
+namespace prefs {
+
+PrefStoreAdapter::PrefStoreAdapter(scoped_refptr<PrefStore> pref_store,
+                                   std::unique_ptr<PrefStoreImpl> impl)
+    : pref_store_(std::move(pref_store)), impl_(std::move(impl)) {}
+
+void PrefStoreAdapter::AddObserver(PrefStore::Observer* observer) {
+  pref_store_->AddObserver(observer);
+}
+
+void PrefStoreAdapter::RemoveObserver(PrefStore::Observer* observer) {
+  pref_store_->RemoveObserver(observer);
+}
+
+bool PrefStoreAdapter::HasObservers() const {
+  return pref_store_->HasObservers();
+}
+
+bool PrefStoreAdapter::IsInitializationComplete() const {
+  return pref_store_->IsInitializationComplete();
+}
+
+bool PrefStoreAdapter::GetValue(const std::string& key,
+                                const base::Value** result) const {
+  return pref_store_->GetValue(key, result);
+}
+
+std::unique_ptr<base::DictionaryValue> PrefStoreAdapter::GetValues() const {
+  return pref_store_->GetValues();
+}
+
+PrefStoreAdapter::~PrefStoreAdapter() = default;
+
+}  // namespace prefs
diff --git a/services/preferences/public/cpp/pref_store_adapter.h b/services/preferences/public/cpp/pref_store_adapter.h
new file mode 100644
index 0000000..c734606
--- /dev/null
+++ b/services/preferences/public/cpp/pref_store_adapter.h
@@ -0,0 +1,44 @@
+// 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 SERVICES_PREFERENCES_PUBLIC_CPP_PREF_STORE_ADAPTER_H_
+#define SERVICES_PREFERENCES_PUBLIC_CPP_PREF_STORE_ADAPTER_H_
+
+#include <memory>
+
+#include "base/macros.h"
+#include "base/values.h"
+#include "components/prefs/pref_store.h"
+#include "services/preferences/public/cpp/pref_store_impl.h"
+
+namespace prefs {
+
+// Ties the lifetime of a |PrefStoreImpl| to a |PrefStore|. Otherwise acts as a
+// |PrefStore|, forwarding all calls to the wrapped |PrefStore|.
+class PrefStoreAdapter : public PrefStore {
+ public:
+  PrefStoreAdapter(scoped_refptr<PrefStore> pref_store,
+                   std::unique_ptr<PrefStoreImpl> impl);
+
+  // PrefStore:
+  void AddObserver(PrefStore::Observer* observer) override;
+  void RemoveObserver(PrefStore::Observer* observer) override;
+  bool HasObservers() const override;
+  bool IsInitializationComplete() const override;
+  bool GetValue(const std::string& key,
+                const base::Value** result) const override;
+  std::unique_ptr<base::DictionaryValue> GetValues() const override;
+
+ private:
+  ~PrefStoreAdapter() override;
+
+  scoped_refptr<PrefStore> pref_store_;
+  std::unique_ptr<PrefStoreImpl> impl_;
+
+  DISALLOW_COPY_AND_ASSIGN(PrefStoreAdapter);
+};
+
+}  // namespace prefs
+
+#endif  // SERVICES_PREFERENCES_PUBLIC_CPP_PREF_STORE_ADAPTER_H_
diff --git a/services/preferences/public/cpp/pref_store_client.cc b/services/preferences/public/cpp/pref_store_client.cc
new file mode 100644
index 0000000..fd6708c
--- /dev/null
+++ b/services/preferences/public/cpp/pref_store_client.cc
@@ -0,0 +1,77 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "services/preferences/public/cpp/pref_store_client.h"
+#include "mojo/public/cpp/bindings/strong_binding.h"
+#include "services/preferences/public/cpp/pref_store_impl.h"
+
+namespace prefs {
+
+PrefStoreClient::PrefStoreClient(mojom::PrefStoreConnectionPtr connection)
+    : cached_prefs_(std::move(connection->initial_prefs)),
+      initialized_(connection->is_initialized),
+      observer_binding_(this, std::move(connection->observer)) {}
+
+void PrefStoreClient::AddObserver(PrefStore::Observer* observer) {
+  observers_.AddObserver(observer);
+}
+
+void PrefStoreClient::RemoveObserver(PrefStore::Observer* observer) {
+  observers_.RemoveObserver(observer);
+}
+
+bool PrefStoreClient::HasObservers() const {
+  return observers_.might_have_observers();
+}
+
+bool PrefStoreClient::IsInitializationComplete() const {
+  return initialized_;
+}
+
+bool PrefStoreClient::GetValue(const std::string& key,
+                               const base::Value** result) const {
+  DCHECK(initialized_);
+  return cached_prefs_->Get(key, result);
+}
+
+std::unique_ptr<base::DictionaryValue> PrefStoreClient::GetValues() const {
+  DCHECK(initialized_);
+  return cached_prefs_->CreateDeepCopy();
+}
+
+PrefStoreClient::~PrefStoreClient() = default;
+
+void PrefStoreClient::OnPrefChanged(const std::string& key,
+                                    std::unique_ptr<base::Value> value) {
+  bool changed = false;
+  if (!value) {  // Delete
+    if (cached_prefs_->RemovePath(key, nullptr))
+      changed = true;
+  } else {
+    const base::Value* prev;
+    if (cached_prefs_->Get(key, &prev)) {
+      if (!prev->Equals(value.get())) {
+        cached_prefs_->Set(key, std::move(value));
+        changed = true;
+      }
+    } else {
+      cached_prefs_->Set(key, std::move(value));
+      changed = true;
+    }
+  }
+  if (changed) {
+    for (Observer& observer : observers_)
+      observer.OnPrefValueChanged(key);
+  }
+}
+
+void PrefStoreClient::OnInitializationCompleted(bool succeeded) {
+  if (!initialized_) {
+    initialized_ = true;
+    for (Observer& observer : observers_)
+      observer.OnInitializationCompleted(true);
+  }
+}
+
+}  // namespace prefs
diff --git a/services/preferences/public/cpp/pref_store_client.h b/services/preferences/public/cpp/pref_store_client.h
new file mode 100644
index 0000000..adad0c2
--- /dev/null
+++ b/services/preferences/public/cpp/pref_store_client.h
@@ -0,0 +1,66 @@
+// 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 SERVICES_PREFERENCES_PUBLIC_CPP_PREF_STORE_CLIENT_H_
+#define SERVICES_PREFERENCES_PUBLIC_CPP_PREF_STORE_CLIENT_H_
+
+#include <string>
+
+#include "base/macros.h"
+#include "base/observer_list.h"
+#include "base/values.h"
+#include "components/prefs/pref_store.h"
+#include "components/prefs/pref_value_map.h"
+#include "mojo/public/cpp/bindings/binding.h"
+#include "services/preferences/public/cpp/pref_store_manager_impl.h"
+#include "services/preferences/public/interfaces/preferences.mojom.h"
+
+namespace prefs {
+
+// TODO(tibell): Make PrefObserverStore use PrefStoreClient as a base class.
+
+// An implementation of PrefStore which uses prefs::mojom::PrefStore as
+// the backing store of the preferences.
+//
+// PrefStoreClient provides synchronous access to the preferences stored by the
+// backing store by caching them locally.
+class PrefStoreClient : public ::PrefStore, public mojom::PrefStoreObserver {
+ public:
+  explicit PrefStoreClient(mojom::PrefStoreConnectionPtr connection);
+
+  // PrefStore:
+  void AddObserver(PrefStore::Observer* observer) override;
+  void RemoveObserver(PrefStore::Observer* observer) override;
+  bool HasObservers() const override;
+  bool IsInitializationComplete() const override;
+  bool GetValue(const std::string& key,
+                const base::Value** result) const override;
+  std::unique_ptr<base::DictionaryValue> GetValues() const override;
+
+ private:
+  friend class PrefStoreClientTest;
+
+  ~PrefStoreClient() override;
+
+  // prefs::mojom::PreferenceObserver:
+  void OnPrefChanged(const std::string& key,
+                     std::unique_ptr<base::Value> value) override;
+  void OnInitializationCompleted(bool succeeded) override;
+
+  // Cached preferences.
+  std::unique_ptr<base::DictionaryValue> cached_prefs_;
+
+  base::ObserverList<PrefStore::Observer, true> observers_;
+
+  // Has the PrefStore we're observing been initialized?
+  bool initialized_;
+
+  mojo::Binding<mojom::PrefStoreObserver> observer_binding_;
+
+  DISALLOW_COPY_AND_ASSIGN(PrefStoreClient);
+};
+
+}  // namespace prefs
+
+#endif  // SERVICES_PREFERENCES_PUBLIC_CPP_PREF_STORE_CLIENT_H_
diff --git a/services/preferences/public/cpp/pref_store_impl.cc b/services/preferences/public/cpp/pref_store_impl.cc
new file mode 100644
index 0000000..680b264
--- /dev/null
+++ b/services/preferences/public/cpp/pref_store_impl.cc
@@ -0,0 +1,71 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "services/preferences/public/cpp/pref_store_impl.h"
+
+#include <memory>
+
+#include "base/values.h"
+
+namespace prefs {
+
+PrefStoreImpl::PrefStoreImpl(scoped_refptr<::PrefStore> pref_store,
+                             mojom::PrefStoreRequest request)
+    : backing_pref_store_(std::move(pref_store)),
+      backing_pref_store_initialized_(false),
+      binding_(this, std::move(request)) {
+  DCHECK(backing_pref_store_);
+  if (backing_pref_store_->IsInitializationComplete())
+    OnInitializationCompleted(true);
+  backing_pref_store_->AddObserver(this);
+}
+
+PrefStoreImpl::~PrefStoreImpl() {
+  backing_pref_store_->RemoveObserver(this);
+}
+
+// static
+std::unique_ptr<PrefStoreImpl> PrefStoreImpl::Create(
+    mojom::PrefStoreRegistryPtr registry_ptr,
+    scoped_refptr<::PrefStore> pref_store,
+    PrefValueStore::PrefStoreType type) {
+  mojom::PrefStorePtr ptr;
+  auto impl = base::MakeUnique<PrefStoreImpl>(std::move(pref_store),
+                                              mojo::MakeRequest(&ptr));
+  registry_ptr->Register(type, std::move(ptr));
+  return impl;
+}
+
+void PrefStoreImpl::OnPrefValueChanged(const std::string& key) {
+  auto dictionary = base::MakeUnique<base::DictionaryValue>();
+  const base::Value* value = nullptr;
+  if (backing_pref_store_->GetValue(key, &value)) {
+    for (const auto& observer : observers_)
+      observer->OnPrefChanged(key, value->CreateDeepCopy());
+  } else {
+    for (const auto& observer : observers_)
+      observer->OnPrefChanged(key, nullptr);
+  }
+}
+
+void PrefStoreImpl::OnInitializationCompleted(bool succeeded) {
+  // Some pref stores call this more than once. We just ignore all calls after
+  // the first.
+  if (backing_pref_store_initialized_)
+    DCHECK(succeeded);
+  backing_pref_store_initialized_ = succeeded;
+  for (const auto& observer : observers_)
+    observer->OnInitializationCompleted(succeeded);
+}
+
+void PrefStoreImpl::AddObserver(const AddObserverCallback& callback) {
+  mojom::PrefStoreObserverPtr observer_ptr;
+  auto request = mojo::MakeRequest(&observer_ptr);
+  observers_.push_back(std::move(observer_ptr));
+  callback.Run(mojom::PrefStoreConnection::New(
+      std::move(request), backing_pref_store_->GetValues(),
+      backing_pref_store_->IsInitializationComplete()));
+}
+
+}  // namespace prefs
diff --git a/services/preferences/public/cpp/pref_store_impl.h b/services/preferences/public/cpp/pref_store_impl.h
new file mode 100644
index 0000000..ce8b688
--- /dev/null
+++ b/services/preferences/public/cpp/pref_store_impl.h
@@ -0,0 +1,60 @@
+// 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 SERVICES_PREFERENCES_PUBLIC_CPP_PREF_STORE_IMPL_H_
+#define SERVICES_PREFERENCES_PUBLIC_CPP_PREF_STORE_IMPL_H_
+
+#include <vector>
+
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "components/prefs/pref_store.h"
+#include "components/prefs/pref_value_store.h"
+#include "mojo/public/cpp/bindings/binding.h"
+#include "services/preferences/public/interfaces/preferences.mojom.h"
+
+namespace prefs {
+
+// Wraps an actual PrefStore implementation and exposes it as a
+// mojom::PrefStore interface.
+class PrefStoreImpl : public ::PrefStore::Observer, public mojom::PrefStore {
+ public:
+  PrefStoreImpl(scoped_refptr<::PrefStore> pref_store,
+                mojom::PrefStoreRequest request);
+  ~PrefStoreImpl() override;
+
+  // The created instance is registered in and owned by the
+  // |mojom::PrefStoreRegistry|.
+  static std::unique_ptr<PrefStoreImpl> Create(
+      mojom::PrefStoreRegistryPtr registry_ptr,
+      scoped_refptr<::PrefStore> pref_store,
+      PrefValueStore::PrefStoreType type);
+
+ private:
+  // PrefStore::Observer:
+  void OnPrefValueChanged(const std::string& key) override;
+  void OnInitializationCompleted(bool succeeded) override;
+
+  // prefs::mojom::PrefStore:
+  void AddObserver(const AddObserverCallback& callback) override;
+
+  // The backing store we observer for changes.
+  scoped_refptr<::PrefStore> backing_pref_store_;
+
+  // Observers we notify when |backing_pref_store_| changes.
+  std::vector<mojom::PrefStoreObserverPtr> observers_;
+
+  // True when the |backing_pref_store_| is initialized, either because it was
+  // passed already initialized in the constructor or after
+  // OnInitializationCompleted was called.
+  bool backing_pref_store_initialized_;
+
+  mojo::Binding<mojom::PrefStore> binding_;
+
+  DISALLOW_COPY_AND_ASSIGN(PrefStoreImpl);
+};
+
+}  // namespace prefs
+
+#endif  // SERVICES_PREFERENCES_PUBLIC_CPP_PREF_STORE_IMPL_H_
diff --git a/services/preferences/public/cpp/pref_store_manager_impl.cc b/services/preferences/public/cpp/pref_store_manager_impl.cc
new file mode 100644
index 0000000..5a72b2c
--- /dev/null
+++ b/services/preferences/public/cpp/pref_store_manager_impl.cc
@@ -0,0 +1,142 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "services/preferences/public/cpp/pref_store_manager_impl.h"
+
+#include "base/memory/ref_counted.h"
+#include "components/prefs/pref_value_store.h"
+#include "mojo/public/cpp/bindings/interface_request.h"
+#include "services/service_manager/public/cpp/interface_registry.h"
+
+namespace prefs {
+
+using ConnectCallback = mojom::PrefStoreConnector::ConnectCallback;
+using PrefStorePtrs =
+    std::unordered_map<PrefValueStore::PrefStoreType, mojom::PrefStorePtr>;
+
+// Used to make sure all pref stores have been registered before replying to any
+// Connect calls.
+class ConnectionBarrier : public base::RefCounted<ConnectionBarrier> {
+ public:
+  static void Create(const PrefStorePtrs& pref_store_ptrs,
+                     const ConnectCallback& callback);
+
+  void Init(const PrefStorePtrs& pref_store_ptrs);
+
+ private:
+  friend class base::RefCounted<ConnectionBarrier>;
+  ConnectionBarrier(const PrefStorePtrs& pref_store_ptrs,
+                    const ConnectCallback& callback);
+  ~ConnectionBarrier() = default;
+
+  void OnConnect(PrefValueStore::PrefStoreType type,
+                 mojom::PrefStoreConnectionPtr connection_ptr);
+
+  ConnectCallback callback_;
+
+  std::unordered_map<PrefValueStore::PrefStoreType,
+                     mojom::PrefStoreConnectionPtr>
+      connections_;
+
+  const size_t expected_connections_;
+
+  DISALLOW_COPY_AND_ASSIGN(ConnectionBarrier);
+};
+
+// static
+void ConnectionBarrier::Create(const PrefStorePtrs& pref_store_ptrs,
+                               const ConnectCallback& callback) {
+  make_scoped_refptr(new ConnectionBarrier(pref_store_ptrs, callback))
+      ->Init(pref_store_ptrs);
+}
+
+void ConnectionBarrier::Init(const PrefStorePtrs& pref_store_ptrs) {
+  for (const auto& ptr : pref_store_ptrs) {
+    ptr.second->AddObserver(
+        base::Bind(&ConnectionBarrier::OnConnect, this, ptr.first));
+  }
+}
+
+ConnectionBarrier::ConnectionBarrier(const PrefStorePtrs& pref_store_ptrs,
+                                     const ConnectCallback& callback)
+    : callback_(callback), expected_connections_(pref_store_ptrs.size()) {}
+
+void ConnectionBarrier::OnConnect(
+    PrefValueStore::PrefStoreType type,
+    mojom::PrefStoreConnectionPtr connection_ptr) {
+  connections_.insert(std::make_pair(type, std::move(connection_ptr)));
+  if (connections_.size() == expected_connections_) {
+    // After this method exits |this| will get destroyed so it's safe to move
+    // out the map.
+    callback_.Run(std::move(connections_));
+  }
+}
+
+PrefStoreManagerImpl::PrefStoreManagerImpl(PrefStoreTypes expected_pref_stores)
+    : expected_pref_stores_(std::move(expected_pref_stores)) {}
+
+PrefStoreManagerImpl::~PrefStoreManagerImpl() = default;
+
+void PrefStoreManagerImpl::Register(PrefValueStore::PrefStoreType type,
+                                    mojom::PrefStorePtr pref_store_ptr) {
+  if (expected_pref_stores_.count(type) == 0) {
+    LOG(WARNING) << "Not registering unexpected pref store: " << type;
+    return;
+  }
+  DVLOG(1) << "Registering pref store: " << type;
+  pref_store_ptr.set_connection_error_handler(
+      base::Bind(&PrefStoreManagerImpl::OnPrefStoreDisconnect,
+                 base::Unretained(this), type));
+  const bool success =
+      pref_store_ptrs_.insert(std::make_pair(type, std::move(pref_store_ptr)))
+          .second;
+  DCHECK(success) << "The same pref store registered twice: " << type;
+  if (AllConnected()) {
+    DVLOG(1) << "All pref stores registered.";
+    for (const auto& callback : pending_callbacks_)
+      ConnectionBarrier::Create(pref_store_ptrs_, callback);
+    pending_callbacks_.clear();
+  }
+}
+
+void PrefStoreManagerImpl::Connect(const ConnectCallback& callback) {
+  if (AllConnected())
+    ConnectionBarrier::Create(pref_store_ptrs_, callback);
+  else
+    pending_callbacks_.push_back(callback);
+}
+
+void PrefStoreManagerImpl::Create(
+    const service_manager::Identity& remote_identity,
+    prefs::mojom::PrefStoreConnectorRequest request) {
+  connector_bindings_.AddBinding(this, std::move(request));
+}
+
+void PrefStoreManagerImpl::Create(
+    const service_manager::Identity& remote_identity,
+    prefs::mojom::PrefStoreRegistryRequest request) {
+  registry_bindings_.AddBinding(this, std::move(request));
+}
+
+void PrefStoreManagerImpl::OnStart() {}
+
+bool PrefStoreManagerImpl::OnConnect(
+    const service_manager::ServiceInfo& remote_info,
+    service_manager::InterfaceRegistry* registry) {
+  registry->AddInterface<prefs::mojom::PrefStoreConnector>(this);
+  registry->AddInterface<prefs::mojom::PrefStoreRegistry>(this);
+  return true;
+}
+
+void PrefStoreManagerImpl::OnPrefStoreDisconnect(
+    PrefValueStore::PrefStoreType type) {
+  DVLOG(1) << "Deregistering pref store: " << type;
+  pref_store_ptrs_.erase(type);
+}
+
+bool PrefStoreManagerImpl::AllConnected() const {
+  return pref_store_ptrs_.size() == expected_pref_stores_.size();
+}
+
+}  // namespace prefs
diff --git a/services/preferences/public/cpp/pref_store_manager_impl.h b/services/preferences/public/cpp/pref_store_manager_impl.h
new file mode 100644
index 0000000..3310090
--- /dev/null
+++ b/services/preferences/public/cpp/pref_store_manager_impl.h
@@ -0,0 +1,85 @@
+// 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 SERVICES_PREFERENCES_PUBLIC_CPP_PREF_STORE_MANAGER_IMPL_H_
+#define SERVICES_PREFERENCES_PUBLIC_CPP_PREF_STORE_MANAGER_IMPL_H_
+
+#include "base/compiler_specific.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "components/prefs/pref_value_store.h"
+#include "mojo/public/cpp/bindings/binding_set.h"
+#include "services/preferences/public/interfaces/preferences.mojom.h"
+#include "services/service_manager/public/cpp/interface_factory.h"
+#include "services/service_manager/public/cpp/service.h"
+
+namespace prefs {
+
+// This class mediates the connection of clients who wants to read preferences
+// and the pref stores that store those preferences. Pref stores use the
+// |PrefStoreRegistry| interface to register themselves with the manager and
+// clients use the |PrefStoreConnector| interface to connect to these stores.
+class PrefStoreManagerImpl
+    : public mojom::PrefStoreRegistry,
+      public mojom::PrefStoreConnector,
+      public service_manager::InterfaceFactory<mojom::PrefStoreConnector>,
+      public service_manager::InterfaceFactory<mojom::PrefStoreRegistry>,
+      public service_manager::Service {
+ public:
+  using PrefStoreTypes = std::set<PrefValueStore::PrefStoreType>;
+
+  // Only replies to Connect calls when all |expected_pref_stores| have
+  // registered.
+  explicit PrefStoreManagerImpl(PrefStoreTypes expected_pref_stores);
+  ~PrefStoreManagerImpl() override;
+
+ private:
+  using PrefStorePtrs =
+      std::unordered_map<PrefValueStore::PrefStoreType, mojom::PrefStorePtr>;
+
+  // mojom::PrefStoreRegistry:
+  void Register(PrefValueStore::PrefStoreType type,
+                mojom::PrefStorePtr pref_store_ptr) override;
+
+  // mojom::PrefStoreConnector:
+  void Connect(const ConnectCallback& callback) override;
+
+  // service_manager::InterfaceFactory<PrefStoreConnector>:
+  void Create(const service_manager::Identity& remote_identity,
+              prefs::mojom::PrefStoreConnectorRequest request) override;
+
+  // service_manager::InterfaceFactory<PrefStoreRegistry>:
+  void Create(const service_manager::Identity& remote_identity,
+              prefs::mojom::PrefStoreRegistryRequest request) override;
+
+  // service_manager::Service:
+  void OnStart() override;
+  bool OnConnect(const service_manager::ServiceInfo& remote_info,
+                 service_manager::InterfaceRegistry* registry) override;
+
+  // Called when a PrefStore previously registered using |Register| disconnects.
+  void OnPrefStoreDisconnect(PrefValueStore::PrefStoreType type);
+
+  // Have all the expected PrefStores connected?
+  bool AllConnected() const;
+
+  // PrefStores that need to register before replying to any Connect calls.
+  PrefStoreTypes expected_pref_stores_;
+
+  // Registered pref stores.
+  PrefStorePtrs pref_store_ptrs_;
+
+  mojo::BindingSet<mojom::PrefStoreConnector> connector_bindings_;
+  mojo::BindingSet<mojom::PrefStoreRegistry> registry_bindings_;
+
+  // We hold on to the connection request callbacks until all expected
+  // PrefStores have registered.
+  std::vector<ConnectCallback> pending_callbacks_;
+
+  DISALLOW_COPY_AND_ASSIGN(PrefStoreManagerImpl);
+};
+
+}  // namespace prefs
+
+#endif  // SERVICES_PREFERENCES_PUBLIC_CPP_PREF_STORE_MANAGER_IMPL_H_
diff --git a/services/preferences/public/cpp/preferences.typemap b/services/preferences/public/cpp/preferences.typemap
new file mode 100644
index 0000000..c15d612
--- /dev/null
+++ b/services/preferences/public/cpp/preferences.typemap
@@ -0,0 +1,15 @@
+# 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.
+
+mojom = "//services/preferences/public/interfaces/preferences.mojom"
+public_headers = [ "//components/prefs/pref_value_store.h" ]
+traits_headers =
+    [ "//services/preferences/public/cpp/preferences_struct_traits.h" ]
+sources = [
+  "//services/preferences/public/cpp/preferences_struct_traits.cc",
+]
+deps = [
+  "//components/prefs",
+]
+type_mappings = [ "prefs.mojom.PrefStoreType=::PrefValueStore::PrefStoreType" ]
diff --git a/services/preferences/public/cpp/preferences_struct_traits.cc b/services/preferences/public/cpp/preferences_struct_traits.cc
new file mode 100644
index 0000000..31b0dfb
--- /dev/null
+++ b/services/preferences/public/cpp/preferences_struct_traits.cc
@@ -0,0 +1,64 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "services/preferences/public/cpp/preferences_struct_traits.h"
+
+namespace mojo {
+
+using PrefStoreType = prefs::mojom::PrefStoreType;
+
+PrefStoreType EnumTraits<PrefStoreType, PrefValueStore::PrefStoreType>::ToMojom(
+    PrefValueStore::PrefStoreType input) {
+  switch (input) {
+    case PrefValueStore::INVALID_STORE:
+      break;
+    case PrefValueStore::MANAGED_STORE:
+      return PrefStoreType::MANAGED;
+    case PrefValueStore::SUPERVISED_USER_STORE:
+      return PrefStoreType::SUPERVISED_USER;
+    case PrefValueStore::EXTENSION_STORE:
+      return PrefStoreType::EXTENSION;
+    case PrefValueStore::COMMAND_LINE_STORE:
+      return PrefStoreType::COMMAND_LINE;
+    case PrefValueStore::USER_STORE:
+      return PrefStoreType::USER;
+    case PrefValueStore::RECOMMENDED_STORE:
+      return PrefStoreType::RECOMMENDED;
+    case PrefValueStore::DEFAULT_STORE:
+      return PrefStoreType::DEFAULT;
+  }
+  NOTREACHED();
+  return {};
+}
+
+bool EnumTraits<PrefStoreType, PrefValueStore::PrefStoreType>::FromMojom(
+    PrefStoreType input,
+    PrefValueStore::PrefStoreType* output) {
+  switch (input) {
+    case PrefStoreType::MANAGED:
+      *output = PrefValueStore::MANAGED_STORE;
+      return true;
+    case PrefStoreType::SUPERVISED_USER:
+      *output = PrefValueStore::SUPERVISED_USER_STORE;
+      return true;
+    case PrefStoreType::EXTENSION:
+      *output = PrefValueStore::EXTENSION_STORE;
+      return true;
+    case PrefStoreType::COMMAND_LINE:
+      *output = PrefValueStore::COMMAND_LINE_STORE;
+      return true;
+    case PrefStoreType::USER:
+      *output = PrefValueStore::USER_STORE;
+      return true;
+    case PrefStoreType::RECOMMENDED:
+      *output = PrefValueStore::RECOMMENDED_STORE;
+      return true;
+    case PrefStoreType::DEFAULT:
+      *output = PrefValueStore::DEFAULT_STORE;
+      return true;
+  }
+  return false;
+}
+
+}  // namespace mojo
diff --git a/services/preferences/public/cpp/preferences_struct_traits.h b/services/preferences/public/cpp/preferences_struct_traits.h
new file mode 100644
index 0000000..d548d8f
--- /dev/null
+++ b/services/preferences/public/cpp/preferences_struct_traits.h
@@ -0,0 +1,26 @@
+// 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 SERVICES_PREFERENCES_PUBLIC_CPP_PREFERENCES_STRUCT_TRAITS_H_
+#define SERVICES_PREFERENCES_PUBLIC_CPP_PREFERENCES_STRUCT_TRAITS_H_
+
+#include "components/prefs/pref_value_store.h"
+#include "mojo/public/cpp/bindings/enum_traits.h"
+#include "services/preferences/public/interfaces/preferences.mojom-shared.h"
+
+namespace mojo {
+
+template <>
+struct EnumTraits<::prefs::mojom::PrefStoreType,
+                  ::PrefValueStore::PrefStoreType> {
+  static prefs::mojom::PrefStoreType ToMojom(
+      PrefValueStore::PrefStoreType input);
+
+  static bool FromMojom(prefs::mojom::PrefStoreType input,
+                        PrefValueStore::PrefStoreType* output);
+};
+
+}  // namespace mojo
+
+#endif  // SERVICES_PREFERENCES_PUBLIC_CPP_PREFERENCES_STRUCT_TRAITS_H_
diff --git a/services/preferences/public/cpp/tests/BUILD.gn b/services/preferences/public/cpp/tests/BUILD.gn
index 1c818cb..8262f89a 100644
--- a/services/preferences/public/cpp/tests/BUILD.gn
+++ b/services/preferences/public/cpp/tests/BUILD.gn
@@ -15,6 +15,7 @@
 test("preferences_unittests") {
   sources = [
     "pref_client_store_unittest.cc",
+    "pref_store_client_unittest.cc",
   ]
   deps = [
     "//base",
@@ -24,6 +25,7 @@
     "//mojo/public/cpp/bindings:bindings",
     "//services/preferences/public/cpp",
     "//services/preferences/public/interfaces",
+    "//testing/gmock",
     "//testing/gtest",
   ]
 }
diff --git a/services/preferences/public/cpp/tests/pref_store_client_unittest.cc b/services/preferences/public/cpp/tests/pref_store_client_unittest.cc
new file mode 100644
index 0000000..856bcb3
--- /dev/null
+++ b/services/preferences/public/cpp/tests/pref_store_client_unittest.cc
@@ -0,0 +1,192 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "services/preferences/public/cpp/pref_store_client.h"
+
+#include "base/macros.h"
+#include "base/memory/ptr_util.h"
+#include "base/message_loop/message_loop.h"
+#include "base/run_loop.h"
+#include "base/values.h"
+#include "services/preferences/public/interfaces/preferences.mojom.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using testing::Invoke;
+using testing::WithArg;
+using testing::WithoutArgs;
+using testing::_;
+
+namespace prefs {
+
+namespace {
+
+class PrefStoreObserverMock : public PrefStore::Observer {
+ public:
+  MOCK_METHOD1(OnPrefValueChanged, void(const std::string&));
+  MOCK_METHOD1(OnInitializationCompleted, void(bool succeeded));
+};
+
+class PrefStoreConnectorMock : public mojom::PrefStoreConnector {
+ public:
+  MOCK_METHOD1(Connect, void(const ConnectCallback&));
+};
+
+}  // namespace
+
+class PrefStoreClientTest : public testing::Test {
+ public:
+  PrefStoreClientTest() = default;
+  ~PrefStoreClientTest() override {}
+
+  PrefStoreObserverMock& observer() { return observer_; }
+  PrefStoreClient* store() { return store_.get(); }
+
+  bool initialized() { return store_->initialized_; }
+  void OnPrefChanged(const std::string& key, const base::Value& value) {
+    observer_ptr_->OnPrefChanged(key, value.CreateDeepCopy());
+  }
+  void OnInitializationCompleted() {
+    observer_ptr_->OnInitializationCompleted(true);
+  }
+
+  // testing::Test:
+  void SetUp() override {
+    store_ = new PrefStoreClient(mojom::PrefStoreConnection::New(
+        mojo::MakeRequest(&observer_ptr_),
+        base::MakeUnique<base::DictionaryValue>(), false));
+    store_->AddObserver(&observer_);
+  }
+  void TearDown() override {
+    store_->RemoveObserver(&observer_);
+    store_ = nullptr;
+  }
+
+ private:
+  mojom::PrefStoreObserverPtr observer_ptr_;
+  PrefStoreObserverMock observer_;
+  scoped_refptr<PrefStoreClient> store_;
+
+  // Required by mojo binding code within PrefStoreClient.
+  base::MessageLoop message_loop_;
+
+  DISALLOW_COPY_AND_ASSIGN(PrefStoreClientTest);
+};
+
+// Tests that observers are notified upon the completion of initialization, and
+// that values become available.
+TEST_F(PrefStoreClientTest, Initialization) {
+  // The store should start out uninitialized if the backing store does.
+  EXPECT_FALSE(initialized());
+  EXPECT_CALL(observer(), OnInitializationCompleted(_)).Times(0);
+
+  testing::Mock::VerifyAndClearExpectations(&observer());
+
+  const char key[] = "hey";
+  const int kValue = 42;
+  base::Value pref(kValue);
+
+  // PrefStore notifies of PreferencesChanged, completing
+  // initialization.
+  base::RunLoop loop;
+  EXPECT_CALL(observer(), OnInitializationCompleted(true));
+  EXPECT_CALL(observer(), OnPrefValueChanged(key))
+      .WillOnce(WithoutArgs(Invoke([&loop]() { loop.Quit(); })));
+  OnInitializationCompleted();
+  OnPrefChanged(key, pref);
+  loop.Run();
+  EXPECT_TRUE(initialized());
+
+  const base::Value* value = nullptr;
+  int actual_value;
+  EXPECT_TRUE(store()->GetValue(key, &value));
+  ASSERT_TRUE(value);
+  EXPECT_TRUE(value->GetAsInteger(&actual_value));
+  EXPECT_EQ(kValue, actual_value);
+}
+
+// Test that when initialized with multiple keys, that observers receive a
+// notification for each key.
+TEST_F(PrefStoreClientTest, MultipleKeyInitialization) {
+  const char key1[] = "hey";
+  const char key2[] = "listen";
+
+  EXPECT_FALSE(initialized());
+  EXPECT_CALL(observer(), OnInitializationCompleted(_)).Times(0);
+
+  testing::Mock::VerifyAndClearExpectations(&observer());
+
+  const int kValue = 42;
+  base::Value pref1(kValue);
+  base::Value pref2("look");
+
+  base::DictionaryValue prefs;
+  prefs.Set(key1, pref1.CreateDeepCopy());
+  prefs.Set(key2, pref2.CreateDeepCopy());
+
+  // The observer should be notified of all keys set.
+  base::RunLoop loop;
+  EXPECT_CALL(observer(), OnInitializationCompleted(true));
+  EXPECT_CALL(observer(), OnPrefValueChanged(key1));
+  EXPECT_CALL(observer(), OnPrefValueChanged(key2))
+      .WillOnce(WithoutArgs(Invoke([&loop]() { loop.Quit(); })));
+  OnInitializationCompleted();
+  OnPrefChanged(key1, pref1);
+  OnPrefChanged(key2, pref2);
+  loop.Run();
+  EXPECT_TRUE(initialized());
+}
+
+// Tests that multiple PrefStore::Observers can be added to a PrefStoreClient
+// and that they are each notified of changes.
+TEST_F(PrefStoreClientTest, MultipleObservers) {
+  PrefStoreObserverMock observer2;
+  store()->AddObserver(&observer2);
+
+  const char key[] = "hey";
+  const int kValue = 42;
+  base::Value pref(kValue);
+
+  // PrefStore notifies of PreferencesChanged, completing
+  // initialization.
+  base::RunLoop loop;
+  EXPECT_CALL(observer(), OnInitializationCompleted(true));
+  EXPECT_CALL(observer2, OnInitializationCompleted(true));
+  EXPECT_CALL(observer(), OnPrefValueChanged(key));
+  EXPECT_CALL(observer2, OnPrefValueChanged(key))
+      .WillOnce(WithoutArgs(Invoke([&loop]() { loop.Quit(); })));
+  OnInitializationCompleted();
+  OnPrefChanged(key, pref);
+  loop.Run();
+
+  store()->RemoveObserver(&observer2);
+}
+
+TEST_F(PrefStoreClientTest, Initialized) {
+  mojom::PrefStoreObserverPtr observer_ptr;
+  PrefStoreObserverMock observer;
+  const char key[] = "hey";
+  const int kValue = 42;
+  base::Value pref(kValue);
+  auto prefs = base::MakeUnique<base::DictionaryValue>();
+  prefs->Set(key, pref.CreateDeepCopy());
+  auto store =
+      make_scoped_refptr(new PrefStoreClient(mojom::PrefStoreConnection::New(
+          mojo::MakeRequest(&observer_ptr), std::move(prefs), true)));
+  store->AddObserver(&observer);
+
+  const base::Value* value = nullptr;
+  int actual_value = 0;
+  EXPECT_TRUE(store->GetValue(key, &value));
+  ASSERT_TRUE(value);
+  EXPECT_TRUE(value->GetAsInteger(&actual_value));
+  EXPECT_EQ(kValue, actual_value);
+  EXPECT_CALL(observer, OnInitializationCompleted(_)).Times(0);
+  EXPECT_CALL(observer, OnPrefValueChanged(_)).Times(0);
+  observer_ptr.FlushForTesting();
+
+  store->RemoveObserver(&observer);
+}
+
+}  // namespace prefs
diff --git a/services/preferences/public/cpp/typemaps.gni b/services/preferences/public/cpp/typemaps.gni
index ccfcef6..1e9fa69 100644
--- a/services/preferences/public/cpp/typemaps.gni
+++ b/services/preferences/public/cpp/typemaps.gni
@@ -2,4 +2,7 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-typemaps = [ "//services/preferences/public/cpp/tracked_preference_validation_delegate.typemap" ]
+typemaps = [
+  "//services/preferences/public/cpp/tracked_preference_validation_delegate.typemap",
+  "//services/preferences/public/cpp/preferences.typemap",
+]
diff --git a/services/preferences/public/interfaces/preferences.mojom b/services/preferences/public/interfaces/preferences.mojom
index 1978e4b..2f6cfdd 100644
--- a/services/preferences/public/interfaces/preferences.mojom
+++ b/services/preferences/public/interfaces/preferences.mojom
@@ -27,3 +27,67 @@
   SetPreferences(mojo.common.mojom.DictionaryValue preferences);
   Subscribe(array<string> preferences);
 };
+
+const string kPrefStoreServiceName = "preferences2";
+
+// The know pref store types.
+//
+// Should be kept in sync with PrefValueStore::PrefStoreType.
+enum PrefStoreType {
+  MANAGED,
+  SUPERVISED_USER,
+  EXTENSION,
+  COMMAND_LINE,
+  USER,
+  RECOMMENDED,
+  DEFAULT,
+};
+
+// Allows observing changes to prefs stored in a |PrefStore|.
+interface PrefStoreObserver {
+  // The preference with the given |key| has changed. If |value| is null then
+  // the preference was deleted.
+  OnPrefChanged(string key, mojo.common.mojom.Value? value);
+
+  // The PrefStore has been initialized (asynchronously).
+  OnInitializationCompleted(bool succeeded);
+};
+
+// Captures the connections to a PrefStore by supplying the initial state of the
+// store and a handle to receive notifications on.
+struct PrefStoreConnection {
+  // Handle to receive updates on.
+  PrefStoreObserver& observer;
+
+  // Initial values of the PrefStore. These will not be communicated through
+  // OnPrefChanged.
+  mojo.common.mojom.DictionaryValue initial_prefs;
+
+  // Is the PrefStore initialized? If not it should not be used before
+  // OnInitializationCompleted has been called.
+  bool is_initialized;
+};
+
+// Manages actual read of preference data. Accepts observers who subscribe to
+// preferences, notifying them of changes.
+interface PrefStore {
+  // Add an observer of changes. This current values of all prefs will not be
+  // communicated through a call to |observer| but instead be returned in
+  // |initial_prefs|.
+  AddObserver() => (PrefStoreConnection connection);
+};
+
+// Manages a registry of all pref stores. Registered pref stores can be
+// connected to through the |PrefStoreConnector| interface.
+interface PrefStoreRegistry {
+  // Register a pref store.
+  Register(PrefStoreType type, PrefStore pref_store);
+};
+
+// Allows connections to pref stores registered with |PrefStoreRegistry|.
+interface PrefStoreConnector {
+  // Connect to all registered pref stores, retrieving the current values of all
+  // prefs in each store and an |observer| interfaces through which updates can
+  // be received.
+  Connect() => (map<PrefStoreType, PrefStoreConnection> connections);
+};
diff --git a/services/resource_coordinator/BUILD.gn b/services/resource_coordinator/BUILD.gn
index 27edac5..9c5f07ef 100644
--- a/services/resource_coordinator/BUILD.gn
+++ b/services/resource_coordinator/BUILD.gn
@@ -10,7 +10,6 @@
 
   public_deps = [
     "//base",
-    "//mojo/public/cpp/bindings",
     "//services/resource_coordinator/public/cpp",
     "//services/resource_coordinator/public/interfaces",
   ]
@@ -21,15 +20,12 @@
 
   sources = [
     "memory/coordinator/coordinator_impl_unittest.cc",
-    "public/cpp/memory/memory_dump_manager_delegate_impl_unittest.cc",
   ]
 
   deps = [
     ":lib",
     "//base",
     "//mojo/public/cpp/bindings",
-    "//services/resource_coordinator/public/cpp",
-    "//services/resource_coordinator/public/interfaces",
     "//testing/gtest",
   ]
 }
diff --git a/services/resource_coordinator/memory/coordinator/coordinator_impl.cc b/services/resource_coordinator/memory/coordinator/coordinator_impl.cc
index 081db17..5c133f8 100644
--- a/services/resource_coordinator/memory/coordinator/coordinator_impl.cc
+++ b/services/resource_coordinator/memory/coordinator/coordinator_impl.cc
@@ -4,41 +4,40 @@
 
 #include "services/resource_coordinator/memory/coordinator/coordinator_impl.h"
 
-#include "base/bind.h"
 #include "base/bind_helpers.h"
+#include "base/lazy_instance.h"
 #include "base/location.h"
 #include "base/logging.h"
 #include "base/memory/ref_counted.h"
-#include "base/single_thread_task_runner.h"
-#include "base/threading/platform_thread.h"
+#include "base/threading/thread_task_runner_handle.h"
 #include "base/trace_event/memory_dump_manager.h"
 #include "base/trace_event/memory_dump_request_args.h"
-#include "services/resource_coordinator/public/cpp/memory/memory_dump_manager_delegate_impl.h"
 #include "services/resource_coordinator/public/interfaces/memory/memory_instrumentation.mojom.h"
 
 namespace memory_instrumentation {
 
+namespace {
+
+base::LazyInstance<CoordinatorImpl>::Leaky g_coordinator =
+    LAZY_INSTANCE_INITIALIZER;
+
+}  // namespace
+
 // static
 CoordinatorImpl* CoordinatorImpl::GetInstance() {
-  static CoordinatorImpl* instance = new CoordinatorImpl(true);
-  DCHECK(instance->initialize_memory_dump_manager());
-  return instance;
+  return g_coordinator.Pointer();
 }
 
-CoordinatorImpl::CoordinatorImpl(bool initialize_memory_dump_manager)
-    : failed_memory_dump_count_(0),
-      initialize_memory_dump_manager_(initialize_memory_dump_manager) {
-  if (initialize_memory_dump_manager) {
-    MemoryDumpManagerDelegateImpl::Config config(this);
-    MemoryDumpManagerDelegateImpl::Create(config);
-  }
-}
+// TODO(chiniforooshan): Initialize the global MemoryDumpManager instance here.
+// This is how the global MemoryDumpManager gets a reference to the delegate on
+// the service (read the browser) process for service process memory dumps. This
+// can be done when the delegate implementation is landed.
+CoordinatorImpl::CoordinatorImpl() : failed_memory_dump_count_(0) {}
 
 CoordinatorImpl::~CoordinatorImpl() {}
 
 void CoordinatorImpl::BindCoordinatorRequest(
     mojom::CoordinatorRequest request) {
-  DCHECK(thread_checker_.CalledOnValidThread());
   bindings_.AddBinding(this, std::move(request));
 }
 
@@ -53,6 +52,7 @@
     const base::trace_event::MemoryDumpRequestArgs& args,
     const RequestGlobalMemoryDumpCallback& callback) {
   DCHECK(thread_checker_.CalledOnValidThread());
+
   bool another_dump_already_in_progress = !queued_memory_dump_requests_.empty();
 
   // If this is a periodic memory dump request and there already is another
@@ -88,6 +88,7 @@
 void CoordinatorImpl::RegisterProcessLocalDumpManager(
     mojom::ProcessLocalDumpManagerPtr process_manager) {
   DCHECK(thread_checker_.CalledOnValidThread());
+
   process_manager.set_connection_error_handler(
       base::Bind(&CoordinatorImpl::UnregisterProcessLocalDumpManager,
                  base::Unretained(this), process_manager.get()));
@@ -100,8 +101,7 @@
 
 void CoordinatorImpl::UnregisterProcessLocalDumpManager(
     mojom::ProcessLocalDumpManager* process_manager) {
-  size_t num_deleted = process_managers_.erase(process_manager);
-  DCHECK(num_deleted == 1);
+  DCHECK(process_managers_.erase(process_manager) == 1);
 
   // Check if we are waiting for an ack from this process-local manager.
   if (pending_process_managers_.find(process_manager) !=
diff --git a/services/resource_coordinator/memory/coordinator/coordinator_impl.h b/services/resource_coordinator/memory/coordinator/coordinator_impl.h
index 670a673d..7a8e1dcd 100644
--- a/services/resource_coordinator/memory/coordinator/coordinator_impl.h
+++ b/services/resource_coordinator/memory/coordinator/coordinator_impl.h
@@ -9,6 +9,7 @@
 #include <set>
 #include <unordered_map>
 
+#include "base/lazy_instance.h"
 #include "base/memory/ref_counted.h"
 #include "base/threading/thread_checker.h"
 #include "base/trace_event/memory_dump_request_args.h"
@@ -26,13 +27,9 @@
   // Coordinator
   void BindCoordinatorRequest(mojom::CoordinatorRequest) override;
 
-  bool initialize_memory_dump_manager() const {
-    return initialize_memory_dump_manager_;
-  }
-
  private:
-  friend std::default_delete<CoordinatorImpl>;  // For testing
-  friend class CoordinatorImplTest;             // For testing
+  friend class CoordinatorImplTest;  // For testing
+  friend struct base::LazyInstanceTraitsBase<CoordinatorImpl>;
 
   struct QueuedMemoryDumpRequest {
     QueuedMemoryDumpRequest(const base::trace_event::MemoryDumpRequestArgs args,
@@ -42,7 +39,7 @@
     const RequestGlobalMemoryDumpCallback callback;
   };
 
-  CoordinatorImpl(bool initialize_memory_dump_manager);
+  CoordinatorImpl();
   ~CoordinatorImpl() override;
 
   // mojom::Coordinator
@@ -81,8 +78,6 @@
   int failed_memory_dump_count_;
   std::list<QueuedMemoryDumpRequest> queued_memory_dump_requests_;
 
-  const bool initialize_memory_dump_manager_;
-
   base::ThreadChecker thread_checker_;
 
   DISALLOW_COPY_AND_ASSIGN(CoordinatorImpl);
diff --git a/services/resource_coordinator/memory/coordinator/coordinator_impl_unittest.cc b/services/resource_coordinator/memory/coordinator/coordinator_impl_unittest.cc
index e19c3c2f3..1bc8254 100644
--- a/services/resource_coordinator/memory/coordinator/coordinator_impl_unittest.cc
+++ b/services/resource_coordinator/memory/coordinator/coordinator_impl_unittest.cc
@@ -3,7 +3,6 @@
 // found in the LICENSE file.
 
 #include "services/resource_coordinator/memory/coordinator/coordinator_impl.h"
-#include "base/bind.h"
 #include "base/bind_helpers.h"
 #include "base/callback_forward.h"
 #include "base/memory/ref_counted.h"
@@ -21,17 +20,14 @@
  public:
   CoordinatorImplTest() {}
   void SetUp() override {
-    dump_response_args_ = {0U, false};
-    coordinator_.reset(new CoordinatorImpl(false));
+    dump_response_args_ = {static_cast<uint64_t>(-1), false};
   }
 
-  void TearDown() override { coordinator_.reset(); }
-
   void RegisterProcessLocalDumpManager(
       mojom::ProcessLocalDumpManagerPtr process_manager) {
     base::ThreadTaskRunnerHandle::Get()->PostTask(
         FROM_HERE, base::Bind(&CoordinatorImpl::RegisterProcessLocalDumpManager,
-                              base::Unretained(coordinator_.get()),
+                              base::Unretained(&coordinator_),
                               base::Passed(&process_manager)));
   }
 
@@ -40,7 +36,7 @@
     base::ThreadTaskRunnerHandle::Get()->PostTask(
         FROM_HERE,
         base::Bind(&CoordinatorImpl::RequestGlobalMemoryDump,
-                   base::Unretained(coordinator_.get()), args,
+                   base::Unretained(&coordinator_), args,
                    base::Bind(&CoordinatorImplTest::OnGlobalMemoryDumpResponse,
                               base::Unretained(this), closure)));
   }
@@ -61,11 +57,11 @@
   DumpResponseArgs dump_response_args_;
 
  private:
-  std::unique_ptr<CoordinatorImpl> coordinator_;
+  CoordinatorImpl coordinator_;
   base::MessageLoop message_loop_;
 };
 
-class MockDumpManager : public mojom::ProcessLocalDumpManager {
+class MockDumpManager : mojom::ProcessLocalDumpManager {
  public:
   MockDumpManager(CoordinatorImplTest* test_coordinator, int expected_calls)
       : binding_(this), expected_calls_(expected_calls) {
@@ -97,7 +93,7 @@
       base::trace_event::MemoryDumpLevelOfDetail::DETAILED};
   RequestGlobalMemoryDump(args, run_loop.QuitClosure());
   run_loop.Run();
-  EXPECT_EQ(1234U, dump_response_args_.dump_guid);
+  EXPECT_EQ(static_cast<uint64_t>(1234), dump_response_args_.dump_guid);
   EXPECT_TRUE(dump_response_args_.success);
 }
 
@@ -113,7 +109,7 @@
 
   run_loop.Run();
 
-  EXPECT_EQ(2345U, dump_response_args_.dump_guid);
+  EXPECT_EQ(static_cast<uint64_t>(2345), dump_response_args_.dump_guid);
   EXPECT_TRUE(dump_response_args_.success);
 }
 
@@ -136,7 +132,7 @@
 
   run_loop.Run();
 
-  EXPECT_EQ(3456U, dump_response_args_.dump_guid);
+  EXPECT_EQ(static_cast<uint64_t>(3456), dump_response_args_.dump_guid);
   EXPECT_FALSE(dump_response_args_.success);
 }
 }  // namespace memory_instrumentation
diff --git a/services/resource_coordinator/public/cpp/memory/memory_dump_manager_delegate_impl.cc b/services/resource_coordinator/public/cpp/memory/memory_dump_manager_delegate_impl.cc
index 91dda86..7072b67 100644
--- a/services/resource_coordinator/public/cpp/memory/memory_dump_manager_delegate_impl.cc
+++ b/services/resource_coordinator/public/cpp/memory/memory_dump_manager_delegate_impl.cc
@@ -4,56 +4,34 @@
 
 #include "services/resource_coordinator/public/cpp/memory/memory_dump_manager_delegate_impl.h"
 
-#include "base/bind.h"
-#include "base/bind_helpers.h"
-#include "base/single_thread_task_runner.h"
-#include "base/synchronization/lock.h"
 #include "base/trace_event/memory_dump_request_args.h"
 #include "mojo/public/cpp/bindings/interface_request.h"
 #include "services/resource_coordinator/public/cpp/memory/coordinator.h"
-#include "services/resource_coordinator/public/interfaces/memory/constants.mojom.h"
 #include "services/resource_coordinator/public/interfaces/memory/memory_instrumentation.mojom.h"
 #include "services/service_manager/public/cpp/interface_provider.h"
 
 namespace memory_instrumentation {
 
-MemoryDumpManagerDelegateImpl::Config::~Config() {}
-
-// static
-MemoryDumpManagerDelegateImpl* MemoryDumpManagerDelegateImpl::Create(
-    const MemoryDumpManagerDelegateImpl::Config& config) {
-  static MemoryDumpManagerDelegateImpl* instance =
-      new MemoryDumpManagerDelegateImpl(config);
-  DCHECK_EQ(instance->config().interface_provider(),
-            config.interface_provider());
-  DCHECK_EQ(instance->config().coordinator(), config.coordinator());
-  return instance;
+MemoryDumpManagerDelegateImpl::MemoryDumpManagerDelegateImpl(
+    service_manager::InterfaceProvider* interface_provider)
+    : is_coordinator_(false), binding_(this) {
+  interface_provider->GetInterface(mojo::MakeRequest(&coordinator_));
+  coordinator_->RegisterProcessLocalDumpManager(
+      binding_.CreateInterfacePtrAndBind());
 }
 
 MemoryDumpManagerDelegateImpl::MemoryDumpManagerDelegateImpl(
-    const MemoryDumpManagerDelegateImpl::Config& config)
-    : binding_(this),
-      config_(config),
-      task_runner_(nullptr),
-      pending_memory_dump_guid_(0) {
-  if (config.interface_provider() != nullptr) {
-    config.interface_provider()->GetInterface(mojo::MakeRequest(&coordinator_));
-  } else {
-    task_runner_ = base::ThreadTaskRunnerHandle::Get();
-    config.coordinator()->BindCoordinatorRequest(
-        mojo::MakeRequest(&coordinator_));
-    base::trace_event::MemoryDumpManager::GetInstance()->set_tracing_process_id(
-        mojom::kServiceTracingProcessId);
-  }
+    Coordinator* coordinator)
+    : is_coordinator_(true), binding_(this) {
+  coordinator->BindCoordinatorRequest(mojo::MakeRequest(&coordinator_));
   coordinator_->RegisterProcessLocalDumpManager(
       binding_.CreateInterfacePtrAndBind());
-  base::trace_event::MemoryDumpManager::GetInstance()->Initialize(this);
 }
 
 MemoryDumpManagerDelegateImpl::~MemoryDumpManagerDelegateImpl() {}
 
 bool MemoryDumpManagerDelegateImpl::IsCoordinator() const {
-  return task_runner_ != nullptr;
+  return is_coordinator_;
 }
 
 void MemoryDumpManagerDelegateImpl::RequestProcessMemoryDump(
@@ -65,46 +43,7 @@
 void MemoryDumpManagerDelegateImpl::RequestGlobalMemoryDump(
     const base::trace_event::MemoryDumpRequestArgs& args,
     const base::trace_event::MemoryDumpCallback& callback) {
-  // Note: This condition is here to match the old behavior. If the delegate is
-  // in the browser process, we do not drop parallel requests in the delegate
-  // and so they will be queued by the Coordinator service (see
-  // CoordinatorImpl::RequestGlobalMemoryDump). If the delegate is in a child
-  // process, parallel requests will be cancelled.
-  //
-  // TODO(chiniforooshan): Unify the child and browser behavior.
-  if (IsCoordinator()) {
-    task_runner_->PostTask(
-        FROM_HERE,
-        base::Bind(&mojom::Coordinator::RequestGlobalMemoryDump,
-                   base::Unretained(coordinator_.get()), args, callback));
-    return;
-  }
-
-  {
-    base::AutoLock lock(pending_memory_dump_guid_lock_);
-    if (pending_memory_dump_guid_) {
-      callback.Run(args.dump_guid, false);
-      return;
-    }
-    pending_memory_dump_guid_ = args.dump_guid;
-  }
-  auto callback_proxy =
-      base::Bind(&MemoryDumpManagerDelegateImpl::MemoryDumpCallbackProxy,
-                 base::Unretained(this), callback);
-  coordinator_->RequestGlobalMemoryDump(args, callback_proxy);
-}
-
-void MemoryDumpManagerDelegateImpl::MemoryDumpCallbackProxy(
-    const base::trace_event::MemoryDumpCallback& callback,
-    uint64_t dump_guid,
-    bool success) {
-  DCHECK_NE(0U, pending_memory_dump_guid_);
-  pending_memory_dump_guid_ = 0;
-  callback.Run(dump_guid, success);
-}
-
-void MemoryDumpManagerDelegateImpl::SetAsNonCoordinatorForTesting() {
-  task_runner_ = nullptr;
+  coordinator_->RequestGlobalMemoryDump(args, callback);
 }
 
 }  // namespace memory_instrumentation
diff --git a/services/resource_coordinator/public/cpp/memory/memory_dump_manager_delegate_impl.h b/services/resource_coordinator/public/cpp/memory/memory_dump_manager_delegate_impl.h
index 60ff376..1176619 100644
--- a/services/resource_coordinator/public/cpp/memory/memory_dump_manager_delegate_impl.h
+++ b/services/resource_coordinator/public/cpp/memory/memory_dump_manager_delegate_impl.h
@@ -5,8 +5,6 @@
 #ifndef SERVICES_RESOURCE_COORDINATOR_PUBLIC_CPP_MEMORY_MEMORY_DUMP_MANAGER_DELEGATE_IMPL_H_
 #define SERVICES_RESOURCE_COORDINATOR_PUBLIC_CPP_MEMORY_MEMORY_DUMP_MANAGER_DELEGATE_IMPL_H_
 
-#include "base/single_thread_task_runner.h"
-#include "base/synchronization/lock.h"
 #include "base/trace_event/memory_dump_manager.h"
 #include "base/trace_event/memory_dump_request_args.h"
 #include "mojo/public/cpp/bindings/binding.h"
@@ -16,80 +14,34 @@
 
 namespace memory_instrumentation {
 
-// This is the bridge between MemoryDumpManager and the Coordinator service.
-// This indirection is needed to avoid a dependency from //base, where
-// MemoryDumpManager lives, to //services, where the Coordinator service lives.
-//
-// This cannot just be implemented by the Coordinator service, because there is
-// no Coordinator service in child processes. So, in a child process, the
-// delegate remotely connects to the Coordinator service. In the browser
-// process, the delegate locally connects to the Coordinator service.
 class MemoryDumpManagerDelegateImpl
     : public base::trace_event::MemoryDumpManagerDelegate,
       public mojom::ProcessLocalDumpManager {
  public:
-  class Config {
-   public:
-    Config(service_manager::InterfaceProvider* interface_provider)
-        : interface_provider_(interface_provider), coordinator_(nullptr) {}
-    Config(Coordinator* coordinator)
-        : interface_provider_(nullptr), coordinator_(coordinator) {}
-    ~Config();
+  // Use to bind to a remote coordinator.
+  MemoryDumpManagerDelegateImpl(
+      service_manager::InterfaceProvider* interface_provider);
 
-    service_manager::InterfaceProvider* interface_provider() const {
-      return interface_provider_;
-    }
+  // Use to bind to a coordinator in the same process.
+  MemoryDumpManagerDelegateImpl(Coordinator* coordinator);
+  ~MemoryDumpManagerDelegateImpl() override;
 
-    Coordinator* coordinator() const { return coordinator_; }
-
-   private:
-    service_manager::InterfaceProvider* interface_provider_;
-    Coordinator* coordinator_;
-    bool is_test_config_;
-  };
-
-  //
-  static MemoryDumpManagerDelegateImpl* Create(const Config& config);
-
-  bool IsCoordinator() const override;
+  bool IsCoordinator() const;
 
   // The base::trace_event::MemoryDumpManager calls this.
   void RequestGlobalMemoryDump(
       const base::trace_event::MemoryDumpRequestArgs& args,
       const base::trace_event::MemoryDumpCallback& callback) override;
 
-  Config config() { return config_; }
-  void SetAsNonCoordinatorForTesting();
-
  private:
-  friend std::default_delete<MemoryDumpManagerDelegateImpl>;  // For testing
-  friend class MemoryDumpManagerDelegateImplTest;             // For testing
-
-  MemoryDumpManagerDelegateImpl(const Config& config);
-  ~MemoryDumpManagerDelegateImpl() override;
-
   // The ProcessLocalDumpManager interface. The coordinator calls this.
   void RequestProcessMemoryDump(
       const base::trace_event::MemoryDumpRequestArgs& args,
       const RequestProcessMemoryDumpCallback& callback) override;
 
-  // A proxy callback for updating |pending_memory_dump_guid_|.
-  void MemoryDumpCallbackProxy(
-      const base::trace_event::MemoryDumpCallback& callback,
-      uint64_t dump_guid,
-      bool success);
-
+  bool is_coordinator_;
   mojom::CoordinatorPtr coordinator_;
   mojo::Binding<mojom::ProcessLocalDumpManager> binding_;
-  const Config config_;
-  scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
-  uint64_t pending_memory_dump_guid_;
-
-  // Prevents racy access to |pending_memory_dump_guid_|.
-  base::Lock pending_memory_dump_guid_lock_;
-
-  // Prevents racy access to |initialized_|.
-  base::Lock initialized_lock_;
 
   DISALLOW_COPY_AND_ASSIGN(MemoryDumpManagerDelegateImpl);
 };
diff --git a/services/resource_coordinator/public/cpp/memory/memory_dump_manager_delegate_impl_unittest.cc b/services/resource_coordinator/public/cpp/memory/memory_dump_manager_delegate_impl_unittest.cc
deleted file mode 100644
index fa89651..0000000
--- a/services/resource_coordinator/public/cpp/memory/memory_dump_manager_delegate_impl_unittest.cc
+++ /dev/null
@@ -1,136 +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 "services/resource_coordinator/public/cpp/memory/memory_dump_manager_delegate_impl.h"
-#include "base/bind.h"
-#include "base/bind_helpers.h"
-#include "base/message_loop/message_loop.h"
-#include "base/run_loop.h"
-#include "base/trace_event/memory_dump_manager.h"
-#include "base/trace_event/memory_dump_request_args.h"
-#include "base/trace_event/trace_config.h"
-#include "base/trace_event/trace_log.h"
-#include "mojo/public/cpp/bindings/binding_set.h"
-#include "services/resource_coordinator/public/cpp/memory/coordinator.h"
-#include "services/resource_coordinator/public/interfaces/memory/memory_instrumentation.mojom.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-using base::trace_event::MemoryDumpLevelOfDetail;
-using base::trace_event::MemoryDumpManager;
-using base::trace_event::MemoryDumpType;
-
-namespace memory_instrumentation {
-
-class MockCoordinator : public Coordinator, public mojom::Coordinator {
- public:
-  void BindCoordinatorRequest(mojom::CoordinatorRequest request) override {
-    bindings_.AddBinding(this, std::move(request));
-  }
-
-  void RegisterProcessLocalDumpManager(
-      mojom::ProcessLocalDumpManagerPtr process_manager) override {}
-
-  void RequestGlobalMemoryDump(
-      const base::trace_event::MemoryDumpRequestArgs& args,
-      const RequestGlobalMemoryDumpCallback& callback) override {
-    callback.Run(args.dump_guid, true);
-  }
-
- private:
-  mojo::BindingSet<mojom::Coordinator> bindings_;
-};
-
-class MemoryDumpManagerDelegateImplTest : public testing::Test {
- public:
-  void SetUp() override {
-    message_loop_.reset(new base::MessageLoop());
-    coordinator_.reset(new MockCoordinator());
-    mdm_.reset(new MemoryDumpManager());
-    MemoryDumpManager::SetInstanceForTesting(mdm_.get());
-    MemoryDumpManagerDelegateImpl::Config config(coordinator_.get());
-    delegate_.reset(new MemoryDumpManagerDelegateImpl(config));
-    delegate_->SetAsNonCoordinatorForTesting();
-
-    // Enable tracing.
-    std::string category_filter = "-*,";
-    category_filter += MemoryDumpManager::kTraceCategory;
-    base::trace_event::TraceConfig trace_config(category_filter, "");
-    base::trace_event::TraceLog::GetInstance()->SetEnabled(
-        trace_config, base::trace_event::TraceLog::RECORDING_MODE);
-
-    // Reset the counters.
-    expected_callback_calls_ = 0;
-    dump_requests_received_by_coordinator_ = 0;
-    quit_closure_.Reset();
-  }
-
-  void TearDown() override {
-    base::trace_event::TraceLog::GetInstance()->SetDisabled();
-    MemoryDumpManager::SetInstanceForTesting(nullptr);
-    mdm_.reset();
-    delegate_.reset();
-    coordinator_.reset();
-    message_loop_.reset();
-  }
-
-  void OnGlobalMemoryDumpDone(int more_requests,
-                              uint64_t dump_guid,
-                              bool success) {
-    EXPECT_GT(expected_callback_calls_, 0);
-    EXPECT_FALSE(quit_closure_.is_null());
-
-    dump_requests_received_by_coordinator_ += success ? 1 : 0;
-    expected_callback_calls_--;
-    if (expected_callback_calls_ == 0)
-      quit_closure_.Run();
-
-    if (more_requests > 0)
-      SequentiallyRequestGlobalDumps(more_requests);
-  }
-
-  void SequentiallyRequestGlobalDumps(int num_requests) {
-    MemoryDumpManager::GetInstance()->RequestGlobalDump(
-        MemoryDumpType::EXPLICITLY_TRIGGERED, MemoryDumpLevelOfDetail::LIGHT,
-        base::Bind(&MemoryDumpManagerDelegateImplTest::OnGlobalMemoryDumpDone,
-                   base::Unretained(this), num_requests - 1));
-  }
-
-  int expected_callback_calls_;
-  int dump_requests_received_by_coordinator_;
-  base::Closure quit_closure_;
-
- private:
-  std::unique_ptr<base::MessageLoop> message_loop_;
-  std::unique_ptr<MockCoordinator> coordinator_;
-  std::unique_ptr<MemoryDumpManagerDelegateImpl> delegate_;
-  std::unique_ptr<MemoryDumpManager> mdm_;
-};
-
-// Makes several global dump requests each after receiving the ACK for the
-// previous one. There should be no throttling and all requests should be
-// forwarded to the coordinator.
-TEST_F(MemoryDumpManagerDelegateImplTest, NonOverlappingMemoryDumpRequests) {
-  base::RunLoop run_loop;
-  expected_callback_calls_ = 3;
-  quit_closure_ = run_loop.QuitClosure();
-  SequentiallyRequestGlobalDumps(3);
-  run_loop.Run();
-  EXPECT_EQ(3, dump_requests_received_by_coordinator_);
-}
-
-// Makes several global dump requests without waiting for previous requests to
-// finish. Only the first request should make it to the coordinator. The rest
-// should be cancelled.
-TEST_F(MemoryDumpManagerDelegateImplTest, OverlappingMemoryDumpRequests) {
-  base::RunLoop run_loop;
-  expected_callback_calls_ = 3;
-  quit_closure_ = run_loop.QuitClosure();
-  SequentiallyRequestGlobalDumps(1);
-  SequentiallyRequestGlobalDumps(1);
-  SequentiallyRequestGlobalDumps(1);
-  run_loop.Run();
-  EXPECT_EQ(1, dump_requests_received_by_coordinator_);
-}
-
-}  // namespace memory_instrumentation
diff --git a/services/resource_coordinator/public/interfaces/BUILD.gn b/services/resource_coordinator/public/interfaces/BUILD.gn
index 2fcf102..1c735ef 100644
--- a/services/resource_coordinator/public/interfaces/BUILD.gn
+++ b/services/resource_coordinator/public/interfaces/BUILD.gn
@@ -6,7 +6,6 @@
 
 mojom("interfaces") {
   sources = [
-    "memory/constants.mojom",
     "memory/memory_instrumentation.mojom",
     "tracing/tracing.mojom",
   ]
diff --git a/services/resource_coordinator/public/interfaces/memory/constants.mojom b/services/resource_coordinator/public/interfaces/memory/constants.mojom
deleted file mode 100644
index 8a0387f..0000000
--- a/services/resource_coordinator/public/interfaces/memory/constants.mojom
+++ /dev/null
@@ -1,7 +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.
-
-module memory_instrumentation.mojom;
-
-const uint64 kServiceTracingProcessId = 0xffffffffffffffff;
diff --git a/services/service_manager/runner/host/service_process_launcher.cc b/services/service_manager/runner/host/service_process_launcher.cc
index 759df006..3b92bf6 100644
--- a/services/service_manager/runner/host/service_process_launcher.cc
+++ b/services/service_manager/runner/host/service_process_launcher.cc
@@ -196,8 +196,9 @@
 
     if (mojo_ipc_channel_.get()) {
       mojo_ipc_channel_->ChildProcessLaunched();
-      process_connection_.Connect(child_process_.Handle(),
-                                  mojo_ipc_channel_->PassServerHandle());
+      process_connection_.Connect(
+          child_process_.Handle(),
+          mojo::edk::ConnectionParams(mojo_ipc_channel_->PassServerHandle()));
     }
   }
   start_child_process_event_.Signal();
diff --git a/services/service_manager/tests/service_manager/service_manager_unittest.cc b/services/service_manager/tests/service_manager/service_manager_unittest.cc
index 8708856..08b3e94 100644
--- a/services/service_manager/tests/service_manager/service_manager_unittest.cc
+++ b/services/service_manager/tests/service_manager/service_manager_unittest.cc
@@ -202,8 +202,9 @@
     target_ = base::LaunchProcess(child_command_line, options);
     DCHECK(target_.IsValid());
     receiver->SetPID(target_.Pid());
-    process_connection.Connect(target_.Handle(),
-                               platform_channel_pair.PassServerHandle());
+    process_connection.Connect(
+        target_.Handle(),
+        mojo::edk::ConnectionParams(platform_channel_pair.PassServerHandle()));
   }
 
   void KillTarget() {
diff --git a/services/service_manager/tests/util.cc b/services/service_manager/tests/util.cc
index b12f6dbf..081ce6c 100644
--- a/services/service_manager/tests/util.cc
+++ b/services/service_manager/tests/util.cc
@@ -89,8 +89,9 @@
   *process = base::LaunchProcess(child_command_line, options);
   DCHECK(process->IsValid());
   receiver->SetPID(process->Pid());
-  pending_process.Connect(process->Handle(),
-                          platform_channel_pair.PassServerHandle());
+  pending_process.Connect(
+      process->Handle(),
+      mojo::edk::ConnectionParams(platform_channel_pair.PassServerHandle()));
   return connection;
 }
 
diff --git a/services/ui/ws/BUILD.gn b/services/ui/ws/BUILD.gn
index 7408a08..8bb2e24 100644
--- a/services/ui/ws/BUILD.gn
+++ b/services/ui/ws/BUILD.gn
@@ -9,6 +9,10 @@
 import("//services/service_manager/public/service_manifest.gni")
 import("//services/service_manager/public/tools/test/service_test.gni")
 
+# In external window mode, top-level windows are native platform windows i.e.
+# not children of a Chrome OS window manager's root window.
+is_external_mode = use_ozone && !is_chromeos
+
 static_library("lib") {
   sources = [
     "accelerator.cc",
@@ -171,10 +175,18 @@
     "test_change_tracker.h",
     "window_server_service_test_base.cc",
     "window_server_service_test_base.h",
-    "window_server_test_base.cc",
-    "window_server_test_base.h",
   ]
 
+  if (!is_external_mode) {
+    # WindowServerTestBase assumes an initial display (and root) is provided
+    # at startup, which is not the case on platforms running external window
+    # mode.
+    sources += [
+      "window_server_test_base.cc",
+      "window_server_test_base.h",
+    ]
+  }
+
   deps = [
     "//base",
     "//base/test:test_config",
@@ -223,12 +235,17 @@
     "user_display_manager_unittest.cc",
     "window_coordinate_conversions_unittest.cc",
     "window_finder_unittest.cc",
-    "window_manager_client_unittest.cc",
     "window_manager_state_unittest.cc",
     "window_tree_client_unittest.cc",
     "window_tree_unittest.cc",
   ]
 
+  if (!is_external_mode) {
+    # A window manager client is not needed on platforms running external
+    # window mode, since the host system is always the window manager.
+    sources += [ "window_manager_client_unittest.cc" ]
+  }
+
   catalog = ":mus_ws_unittests_catalog"
 
   deps = [
diff --git a/testing/buildbot/chromium.perf.json b/testing/buildbot/chromium.perf.json
index 392905cfc..acd9114 100644
--- a/testing/buildbot/chromium.perf.json
+++ b/testing/buildbot/chromium.perf.json
@@ -10,15 +10,6 @@
     "scripts": [
       {
         "args": [
-          "cc_perftests",
-          "--adb-path",
-          "src/third_party/catapult/devil/bin/deps/linux2/x86_64/bin/adb"
-        ],
-        "name": "cc_perftests",
-        "script": "gtest_perf_test.py"
-      },
-      {
-        "args": [
           "gpu_perftests",
           "--adb-path",
           "src/third_party/catapult/devil/bin/deps/linux2/x86_64/bin/adb"
@@ -41,15 +32,6 @@
     "scripts": [
       {
         "args": [
-          "cc_perftests",
-          "--adb-path",
-          "src/third_party/catapult/devil/bin/deps/linux2/x86_64/bin/adb"
-        ],
-        "name": "cc_perftests",
-        "script": "gtest_perf_test.py"
-      },
-      {
-        "args": [
           "tracing_perftests",
           "--adb-path",
           "src/third_party/catapult/devil/bin/deps/linux2/x86_64/bin/adb"
@@ -63,15 +45,6 @@
     "scripts": [
       {
         "args": [
-          "cc_perftests",
-          "--adb-path",
-          "src/third_party/catapult/devil/bin/deps/linux2/x86_64/bin/adb"
-        ],
-        "name": "cc_perftests",
-        "script": "gtest_perf_test.py"
-      },
-      {
-        "args": [
           "gpu_perftests",
           "--adb-path",
           "src/third_party/catapult/devil/bin/deps/linux2/x86_64/bin/adb"
@@ -1238,25 +1211,6 @@
         }
       },
       {
-        "args": [],
-        "isolate_name": "cc_perftests",
-        "name": "cc_perftests",
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "gpu": "102b:0534",
-              "id": "build150-m1",
-              "os": "Ubuntu-14.04",
-              "pool": "Chrome-perf"
-            }
-          ],
-          "expiration": 21600,
-          "hard_timeout": 7200,
-          "io_timeout": 3600
-        }
-      },
-      {
         "args": [
           "dromaeo.cssqueryjquery",
           "-v",
diff --git a/third_party/WebKit/LayoutTests/bluetooth/characteristic/getDescriptor/gen-descriptor-device-disconnects-before.html b/third_party/WebKit/LayoutTests/bluetooth/characteristic/getDescriptor/gen-descriptor-device-disconnects-before.html
new file mode 100644
index 0000000..370aa9d1
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/bluetooth/characteristic/getDescriptor/gen-descriptor-device-disconnects-before.html
@@ -0,0 +1,33 @@
+<!-- Generated by //third_party/WebKit/LayoutTests/bluetooth/generate.py -->
+<!DOCTYPE html>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script>
+'use strict';
+promise_test(() => {
+  let val = new Uint8Array([1]);
+  return setBluetoothFakeAdapter('DisconnectingHealthThermometerAdapter')
+      .then(() => requestDeviceWithKeyDown({
+              filters: [{services: ['health_thermometer']}],
+              optionalServices: [request_disconnection_service_uuid]
+            }))
+      .then(device => device.gatt.connect())
+      .then(gattServer => {
+        let measurement_interval;
+        return gattServer.getPrimaryService('health_thermometer')
+            .then(ht => ht.getCharacteristic('measurement_interval'))
+            .then(mi => measurement_interval = mi)
+            .then(() => get_request_disconnection(gattServer))
+            .then(requestDisconnection => requestDisconnection())
+            .then(
+                () => assert_promise_rejects_with_message(
+                    measurement_interval.getDescriptor(user_description.name),
+                    new DOMException(
+                        'GATT Server is disconnected. Cannot retrieve descriptors. ' +
+                            '(Re)connect first with `device.gatt.connect`.',
+                        'NetworkError')));
+      });
+}, 'Device disconnects before getDescriptor. Reject with NetworkError.');
+
+</script>
diff --git a/third_party/WebKit/LayoutTests/bluetooth/characteristic/getDescriptor/gen-descriptor-device-disconnects-during-error.html b/third_party/WebKit/LayoutTests/bluetooth/characteristic/getDescriptor/gen-descriptor-device-disconnects-during-error.html
new file mode 100644
index 0000000..3dd6bb8
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/bluetooth/characteristic/getDescriptor/gen-descriptor-device-disconnects-during-error.html
@@ -0,0 +1,37 @@
+<!-- Generated by //third_party/WebKit/LayoutTests/bluetooth/generate.py -->
+<!DOCTYPE html>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script>
+'use strict';
+promise_test(
+    () => {
+      let val = new Uint8Array([1]);
+      return setBluetoothFakeAdapter('FailingGATTOperationsAdapter')
+          .then(() => requestDeviceWithKeyDown({
+                  filters: [{services: [errorUUID(0xA0)]}],
+                  optionalServices: [request_disconnection_service_uuid]
+                }))
+          .then(device => device.gatt.connect())
+          .then(gattServer => {
+            let error_characteristic;
+            return gattServer.getPrimaryService(errorUUID(0xA0))
+                .then(es => es.getCharacteristic(errorUUID(0xA1)))
+                .then(ec => error_characteristic = ec)
+                .then(() => get_request_disconnection(gattServer))
+                .then(requestDisconnection => {
+                  requestDisconnection();
+                  return assert_promise_rejects_with_message(
+                      error_characteristic.getDescriptor(user_description.name),
+                      new DOMException(
+                          'GATT Server is disconnected. Cannot retrieve descriptors. ' +
+                              '(Re)connect first with `device.gatt.connect`.',
+                          'NetworkError'));
+                });
+          });
+    },
+    'Device disconnects during a getDescriptor call that fails. ' +
+        'Reject with NetworkError.');
+
+</script>
diff --git a/third_party/WebKit/LayoutTests/bluetooth/characteristic/getDescriptor/gen-descriptor-device-disconnects-during-success.html b/third_party/WebKit/LayoutTests/bluetooth/characteristic/getDescriptor/gen-descriptor-device-disconnects-during-success.html
new file mode 100644
index 0000000..49fbe6ab
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/bluetooth/characteristic/getDescriptor/gen-descriptor-device-disconnects-during-success.html
@@ -0,0 +1,37 @@
+<!-- Generated by //third_party/WebKit/LayoutTests/bluetooth/generate.py -->
+<!DOCTYPE html>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script>
+'use strict';
+promise_test(
+    () => {
+      let val = new Uint8Array([1]);
+      return setBluetoothFakeAdapter('DisconnectingHealthThermometerAdapter')
+          .then(() => requestDeviceWithKeyDown({
+                  filters: [{services: ['health_thermometer']}],
+                  optionalServices: [request_disconnection_service_uuid]
+                }))
+          .then(device => device.gatt.connect())
+          .then(gattServer => {
+            let measurement_interval;
+            return gattServer.getPrimaryService('health_thermometer')
+                .then(ht => ht.getCharacteristic('measurement_interval'))
+                .then(mi => measurement_interval = mi)
+                .then(() => get_request_disconnection(gattServer))
+                .then(requestDisconnection => {
+                  requestDisconnection();
+                  return assert_promise_rejects_with_message(
+                      measurement_interval.getDescriptor(user_description.name),
+                      new DOMException(
+                          'GATT Server is disconnected. Cannot retrieve descriptors. ' +
+                              '(Re)connect first with `device.gatt.connect`.',
+                          'NetworkError'));
+                });
+          });
+    },
+    'Device disconnects during a getDescriptor call that succeeds. ' +
+        'Reject with NetworkError.');
+
+</script>
diff --git a/third_party/WebKit/LayoutTests/bluetooth/characteristic/getDescriptor/gen-descriptor-disconnect-called-before.html b/third_party/WebKit/LayoutTests/bluetooth/characteristic/getDescriptor/gen-descriptor-disconnect-called-before.html
new file mode 100644
index 0000000..47b74dc
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/bluetooth/characteristic/getDescriptor/gen-descriptor-disconnect-called-before.html
@@ -0,0 +1,30 @@
+<!-- Generated by //third_party/WebKit/LayoutTests/bluetooth/generate.py -->
+<!DOCTYPE html>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script>
+'use strict';
+promise_test(() => {
+  let val = new Uint8Array([1]);
+  return setBluetoothFakeAdapter('DisconnectingHealthThermometerAdapter')
+      .then(
+          () => requestDeviceWithKeyDown(
+              {filters: [{services: ['health_thermometer']}]}))
+      .then(device => device.gatt.connect())
+      .then(gattServer => {
+        return gattServer.getPrimaryService('health_thermometer')
+            .then(service => service.getCharacteristic('measurement_interval'))
+            .then(measurement_interval => {
+              gattServer.disconnect();
+              return assert_promise_rejects_with_message(
+                  measurement_interval.getDescriptor(user_description.name),
+                  new DOMException(
+                      'GATT Server is disconnected. Cannot retrieve descriptors. ' +
+                          '(Re)connect first with `device.gatt.connect`.',
+                      'NetworkError'));
+            });
+      });
+}, 'disconnect() called before getDescriptor. Reject with NetworkError.');
+
+</script>
diff --git a/third_party/WebKit/LayoutTests/bluetooth/characteristic/getDescriptor/gen-descriptor-disconnect-called-during-error.html b/third_party/WebKit/LayoutTests/bluetooth/characteristic/getDescriptor/gen-descriptor-disconnect-called-during-error.html
new file mode 100644
index 0000000..a1f3c8c6
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/bluetooth/characteristic/getDescriptor/gen-descriptor-disconnect-called-during-error.html
@@ -0,0 +1,34 @@
+<!-- Generated by //third_party/WebKit/LayoutTests/bluetooth/generate.py -->
+<!DOCTYPE html>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script>
+'use strict';
+promise_test(
+    () => {
+      let val = new Uint8Array([1]);
+      return setBluetoothFakeAdapter('FailingGATTOperationsAdapter')
+          .then(
+              () => requestDeviceWithKeyDown(
+                  {filters: [{services: [errorUUID(0xA0)]}]}))
+          .then(device => device.gatt.connect())
+          .then(gattServer => {
+            return gattServer.getPrimaryService(errorUUID(0xA0))
+                .then(service => service.getCharacteristic(errorUUID(0xA1)))
+                .then(error_characteristic => {
+                  let promise = assert_promise_rejects_with_message(
+                      error_characteristic.getDescriptor(user_description.name),
+                      new DOMException(
+                          'GATT Server is disconnected. Cannot retrieve descriptors. ' +
+                              '(Re)connect first with `device.gatt.connect`.',
+                          'NetworkError'));
+                  gattServer.disconnect();
+                  return promise;
+                });
+          });
+    },
+    'disconnect() called during a getDescriptor call that fails. ' +
+        'Reject with NetworkError.');
+
+</script>
diff --git a/third_party/WebKit/LayoutTests/bluetooth/characteristic/getDescriptor/gen-descriptor-disconnect-called-during-success.html b/third_party/WebKit/LayoutTests/bluetooth/characteristic/getDescriptor/gen-descriptor-disconnect-called-during-success.html
new file mode 100644
index 0000000..b19e359
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/bluetooth/characteristic/getDescriptor/gen-descriptor-disconnect-called-during-success.html
@@ -0,0 +1,36 @@
+<!-- Generated by //third_party/WebKit/LayoutTests/bluetooth/generate.py -->
+<!DOCTYPE html>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script>
+'use strict';
+promise_test(
+    () => {
+      let val = new Uint8Array([1]);
+      return setBluetoothFakeAdapter('DisconnectingHealthThermometerAdapter')
+          .then(
+              () => requestDeviceWithKeyDown(
+                  {filters: [{services: ['health_thermometer']}]}))
+          .then(device => device.gatt.connect())
+          .then(gattServer => {
+            return gattServer.getPrimaryService('health_thermometer')
+                .then(
+                    service =>
+                        service.getCharacteristic('measurement_interval'))
+                .then(measurement_interval => {
+                  let promise = assert_promise_rejects_with_message(
+                      measurement_interval.getDescriptor(user_description.name),
+                      new DOMException(
+                          'GATT Server is disconnected. Cannot retrieve descriptors. ' +
+                              '(Re)connect first with `device.gatt.connect`.',
+                          'NetworkError'));
+                  gattServer.disconnect();
+                  return promise;
+                });
+          });
+    },
+    'disconnect() called during a getDescriptor call that succeeds. ' +
+        'Reject with NetworkError.');
+
+</script>
diff --git a/third_party/WebKit/LayoutTests/bluetooth/characteristic/getDescriptor/gen-descriptor-garbage-collection-ran-during-error.html b/third_party/WebKit/LayoutTests/bluetooth/characteristic/getDescriptor/gen-descriptor-garbage-collection-ran-during-error.html
index 2910e06..3ed0e2e 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/characteristic/getDescriptor/gen-descriptor-garbage-collection-ran-during-error.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/characteristic/getDescriptor/gen-descriptor-garbage-collection-ran-during-error.html
@@ -8,25 +8,26 @@
 promise_test(() => {
   let promise;
   return setBluetoothFakeAdapter('DisconnectingHealthThermometerAdapter')
-    .then(() => requestDeviceWithKeyDown({
-      filters: [{services: ['health_thermometer']}]}))
-    .then(device => device.gatt.connect())
-    .then(gattServer => gattServer.getPrimaryService('health_thermometer'))
-    .then(service => service.getCharacteristic('measurement_interval'))
-    .then(characteristic => {
-      promise = assert_promise_rejects_with_message(
-        /* 0x0101 doesn't exist in this characteristic */
-        characteristic.getDescriptor(0x0101),
-        new DOMException(
-          'GATT Server is disconnected. Cannot perform GATT operations. ' +
-          '(Re)connect first with `device.gatt.connect`.',
-          'NetworkError'));
-      // Disconnect called to clear attributeInstanceMap and allow the
-      // object to get garbage collected.
-      characteristic.service.device.gatt.disconnect();
-    })
-    .then(runGarbageCollection)
-    .then(() => promise);
+      .then(
+          () => requestDeviceWithKeyDown(
+              {filters: [{services: ['health_thermometer']}]}))
+      .then(device => device.gatt.connect())
+      .then(gattServer => gattServer.getPrimaryService('health_thermometer'))
+      .then(service => service.getCharacteristic('measurement_interval'))
+      .then(characteristic => {
+        promise = assert_promise_rejects_with_message(
+            /* 0x0101 doesn't exist in this characteristic */
+            characteristic.getDescriptor(0x0101),
+            new DOMException(
+                'GATT Server is disconnected. Cannot retrieve descriptors. ' +
+                    '(Re)connect first with `device.gatt.connect`.',
+                'NetworkError'));
+        // Disconnect called to clear attributeInstanceMap and allow the
+        // object to get garbage collected.
+        characteristic.service.device.gatt.disconnect();
+      })
+      .then(runGarbageCollection)
+      .then(() => promise);
 }, 'Garbage Collection ran during getDescriptor call that fails. ' +
    'Should not crash');
 
diff --git a/third_party/WebKit/LayoutTests/bluetooth/characteristic/getDescriptor/gen-descriptor-garbage-collection-ran-during-success.html b/third_party/WebKit/LayoutTests/bluetooth/characteristic/getDescriptor/gen-descriptor-garbage-collection-ran-during-success.html
index 044ffee6..849e4b0 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/characteristic/getDescriptor/gen-descriptor-garbage-collection-ran-during-success.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/characteristic/getDescriptor/gen-descriptor-garbage-collection-ran-during-success.html
@@ -8,24 +8,25 @@
 promise_test(() => {
   let promise;
   return setBluetoothFakeAdapter('DisconnectingHealthThermometerAdapter')
-    .then(() => requestDeviceWithKeyDown({
-      filters: [{services: ['health_thermometer']}]}))
-    .then(device => device.gatt.connect())
-    .then(gattServer => gattServer.getPrimaryService('health_thermometer'))
-    .then(service => service.getCharacteristic('measurement_interval'))
-    .then(characteristic => {
-      promise = assert_promise_rejects_with_message(
-        characteristic.getDescriptor(user_description.name),
-        new DOMException(
-          'GATT Server is disconnected. Cannot perform GATT operations. ' +
-          '(Re)connect first with `device.gatt.connect`.',
-          'NetworkError'));
-      // Disconnect called to clear attributeInstanceMap and allow the
-      // object to get garbage collected.
-      characteristic.service.device.gatt.disconnect();
-    })
-    .then(runGarbageCollection)
-    .then(() => promise);
+      .then(
+          () => requestDeviceWithKeyDown(
+              {filters: [{services: ['health_thermometer']}]}))
+      .then(device => device.gatt.connect())
+      .then(gattServer => gattServer.getPrimaryService('health_thermometer'))
+      .then(service => service.getCharacteristic('measurement_interval'))
+      .then(characteristic => {
+        promise = assert_promise_rejects_with_message(
+            characteristic.getDescriptor(user_description.name),
+            new DOMException(
+                'GATT Server is disconnected. Cannot retrieve descriptors. ' +
+                    '(Re)connect first with `device.gatt.connect`.',
+                'NetworkError'));
+        // Disconnect called to clear attributeInstanceMap and allow the
+        // object to get garbage collected.
+        characteristic.service.device.gatt.disconnect();
+      })
+      .then(runGarbageCollection)
+      .then(() => promise);
 }, 'Garbage Collection ran during a getDescriptor call that succeeds. ' +
    'Should not crash.');
 
diff --git a/third_party/WebKit/LayoutTests/bluetooth/characteristic/getDescriptor/gen-gatt-op-device-disconnects-before.html b/third_party/WebKit/LayoutTests/bluetooth/characteristic/getDescriptor/gen-gatt-op-device-disconnects-before.html
deleted file mode 100644
index aa44683..0000000
--- a/third_party/WebKit/LayoutTests/bluetooth/characteristic/getDescriptor/gen-gatt-op-device-disconnects-before.html
+++ /dev/null
@@ -1,32 +0,0 @@
-<!-- Generated by //third_party/WebKit/LayoutTests/bluetooth/generate.py -->
-<!DOCTYPE html>
-<script src="../../../resources/testharness.js"></script>
-<script src="../../../resources/testharnessreport.js"></script>
-<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
-<script>
-'use strict';
-promise_test(() => {
-  let val = new Uint8Array([1]);
-  return setBluetoothFakeAdapter('DisconnectingHealthThermometerAdapter')
-    .then(() => requestDeviceWithKeyDown({
-      filters: [{services: ['health_thermometer']}],
-      optionalServices: [request_disconnection_service_uuid]
-    }))
-    .then(device => device.gatt.connect())
-    .then(gattServer => {
-      let measurement_interval;
-      return gattServer.getPrimaryService('health_thermometer')
-        .then(ht=> ht.getCharacteristic('measurement_interval'))
-        .then(mi => measurement_interval = mi)
-        .then(() => get_request_disconnection(gattServer))
-        .then(requestDisconnection => requestDisconnection())
-        .then(() => assert_promise_rejects_with_message(
-          measurement_interval.getDescriptor(user_description.name),
-          new DOMException(
-            'GATT Server is disconnected. Cannot perform GATT operations. ' +
-            '(Re)connect first with `device.gatt.connect`.',
-            'NetworkError')));
-    });
-}, 'Device disconnects before getDescriptor. Reject with NetworkError.');
-
-</script>
diff --git a/third_party/WebKit/LayoutTests/bluetooth/characteristic/getDescriptor/gen-gatt-op-device-disconnects-during-error.html b/third_party/WebKit/LayoutTests/bluetooth/characteristic/getDescriptor/gen-gatt-op-device-disconnects-during-error.html
deleted file mode 100644
index 3e0d9686..0000000
--- a/third_party/WebKit/LayoutTests/bluetooth/characteristic/getDescriptor/gen-gatt-op-device-disconnects-during-error.html
+++ /dev/null
@@ -1,36 +0,0 @@
-<!-- Generated by //third_party/WebKit/LayoutTests/bluetooth/generate.py -->
-<!DOCTYPE html>
-<script src="../../../resources/testharness.js"></script>
-<script src="../../../resources/testharnessreport.js"></script>
-<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
-<script>
-'use strict';
-promise_test(() => {
-  let val = new Uint8Array([1]);
-  return setBluetoothFakeAdapter('FailingGATTOperationsAdapter')
-    .then(() => requestDeviceWithKeyDown({
-      filters: [{services: [errorUUID(0xA0)]}],
-      optionalServices: [request_disconnection_service_uuid]
-    }))
-    .then(device => device.gatt.connect())
-    .then(gattServer => {
-      let error_characteristic;
-      return gattServer
-        .getPrimaryService(errorUUID(0xA0))
-        .then(es => es.getCharacteristic(errorUUID(0xA1)))
-        .then(ec => error_characteristic = ec)
-        .then(() => get_request_disconnection(gattServer))
-        .then(requestDisconnection => {
-          requestDisconnection();
-          return assert_promise_rejects_with_message(
-            error_characteristic.getDescriptor(user_description.name),
-            new DOMException(
-              'GATT Server is disconnected. Cannot perform GATT operations. ' +
-              '(Re)connect first with `device.gatt.connect`.',
-              'NetworkError'));
-        });
-    });
-}, 'Device disconnects during a getDescriptor call that fails. ' +
-   'Reject with NetworkError.');
-
-</script>
diff --git a/third_party/WebKit/LayoutTests/bluetooth/characteristic/getDescriptor/gen-gatt-op-device-disconnects-during-success.html b/third_party/WebKit/LayoutTests/bluetooth/characteristic/getDescriptor/gen-gatt-op-device-disconnects-during-success.html
deleted file mode 100644
index 021a54f..0000000
--- a/third_party/WebKit/LayoutTests/bluetooth/characteristic/getDescriptor/gen-gatt-op-device-disconnects-during-success.html
+++ /dev/null
@@ -1,36 +0,0 @@
-<!-- Generated by //third_party/WebKit/LayoutTests/bluetooth/generate.py -->
-<!DOCTYPE html>
-<script src="../../../resources/testharness.js"></script>
-<script src="../../../resources/testharnessreport.js"></script>
-<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
-<script>
-'use strict';
-promise_test(() => {
-  let val = new Uint8Array([1]);
-  return setBluetoothFakeAdapter('DisconnectingHealthThermometerAdapter')
-    .then(() => requestDeviceWithKeyDown({
-      filters: [{services: ['health_thermometer']}],
-      optionalServices: [request_disconnection_service_uuid]
-    }))
-    .then(device => device.gatt.connect())
-    .then(gattServer => {
-      let measurement_interval;
-      return gattServer
-        .getPrimaryService('health_thermometer')
-        .then(ht=> ht.getCharacteristic('measurement_interval'))
-        .then(mi => measurement_interval = mi)
-        .then(() => get_request_disconnection(gattServer))
-        .then(requestDisconnection => {
-          requestDisconnection();
-          return assert_promise_rejects_with_message(
-            measurement_interval.getDescriptor(user_description.name),
-            new DOMException(
-              'GATT Server is disconnected. Cannot perform GATT operations. ' +
-              '(Re)connect first with `device.gatt.connect`.',
-              'NetworkError'));
-        });
-    });
-}, 'Device disconnects during a getDescriptor call that succeeds. ' +
-   'Reject with NetworkError.');
-
-</script>
diff --git a/third_party/WebKit/LayoutTests/bluetooth/characteristic/getDescriptor/gen-gatt-op-disconnect-called-before.html b/third_party/WebKit/LayoutTests/bluetooth/characteristic/getDescriptor/gen-gatt-op-disconnect-called-before.html
deleted file mode 100644
index f921b51..0000000
--- a/third_party/WebKit/LayoutTests/bluetooth/characteristic/getDescriptor/gen-gatt-op-disconnect-called-before.html
+++ /dev/null
@@ -1,29 +0,0 @@
-<!-- Generated by //third_party/WebKit/LayoutTests/bluetooth/generate.py -->
-<!DOCTYPE html>
-<script src="../../../resources/testharness.js"></script>
-<script src="../../../resources/testharnessreport.js"></script>
-<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
-<script>
-'use strict';
-promise_test(() => {
-  let val = new Uint8Array([1]);
-  return setBluetoothFakeAdapter('DisconnectingHealthThermometerAdapter')
-    .then(() => requestDeviceWithKeyDown({
-      filters: [{services: ['health_thermometer']}]}))
-    .then(device => device.gatt.connect())
-    .then(gattServer => {
-      return gattServer.getPrimaryService('health_thermometer')
-        .then(service => service.getCharacteristic('measurement_interval'))
-        .then(measurement_interval => {
-          gattServer.disconnect();
-          return assert_promise_rejects_with_message(
-            measurement_interval.getDescriptor(user_description.name),
-            new DOMException(
-              'GATT Server is disconnected. Cannot perform GATT operations. ' +
-              '(Re)connect first with `device.gatt.connect`.',
-              'NetworkError'));
-        });
-    });
-}, 'disconnect() called before getDescriptor. Reject with NetworkError.');
-
-</script>
diff --git a/third_party/WebKit/LayoutTests/bluetooth/characteristic/getDescriptor/gen-gatt-op-disconnect-called-during-error.html b/third_party/WebKit/LayoutTests/bluetooth/characteristic/getDescriptor/gen-gatt-op-disconnect-called-during-error.html
deleted file mode 100644
index 8f5ed5b..0000000
--- a/third_party/WebKit/LayoutTests/bluetooth/characteristic/getDescriptor/gen-gatt-op-disconnect-called-during-error.html
+++ /dev/null
@@ -1,31 +0,0 @@
-<!-- Generated by //third_party/WebKit/LayoutTests/bluetooth/generate.py -->
-<!DOCTYPE html>
-<script src="../../../resources/testharness.js"></script>
-<script src="../../../resources/testharnessreport.js"></script>
-<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
-<script>
-'use strict';
-promise_test(() => {
-  let val = new Uint8Array([1]);
-  return setBluetoothFakeAdapter('FailingGATTOperationsAdapter')
-    .then(() => requestDeviceWithKeyDown({
-      filters: [{services: [errorUUID(0xA0)]}]}))
-    .then(device => device.gatt.connect())
-    .then(gattServer => {
-      return gattServer.getPrimaryService(errorUUID(0xA0))
-        .then(service => service.getCharacteristic(errorUUID(0xA1)))
-        .then(error_characteristic => {
-          let promise = assert_promise_rejects_with_message(
-            error_characteristic.getDescriptor(user_description.name),
-            new DOMException(
-              'GATT Server is disconnected. Cannot perform GATT operations. ' +
-              '(Re)connect first with `device.gatt.connect`.',
-              'NetworkError'));
-          gattServer.disconnect();
-          return promise;
-        });
-    });
-}, 'disconnect() called during a getDescriptor call that fails. ' +
-   'Reject with NetworkError.');
-
-</script>
diff --git a/third_party/WebKit/LayoutTests/bluetooth/characteristic/getDescriptor/gen-gatt-op-disconnect-called-during-success.html b/third_party/WebKit/LayoutTests/bluetooth/characteristic/getDescriptor/gen-gatt-op-disconnect-called-during-success.html
deleted file mode 100644
index 9be307c..0000000
--- a/third_party/WebKit/LayoutTests/bluetooth/characteristic/getDescriptor/gen-gatt-op-disconnect-called-during-success.html
+++ /dev/null
@@ -1,32 +0,0 @@
-<!-- Generated by //third_party/WebKit/LayoutTests/bluetooth/generate.py -->
-<!DOCTYPE html>
-<script src="../../../resources/testharness.js"></script>
-<script src="../../../resources/testharnessreport.js"></script>
-<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
-<script>
-'use strict';
-promise_test(() => {
-  let val = new Uint8Array([1]);
-  return setBluetoothFakeAdapter('DisconnectingHealthThermometerAdapter')
-    .then(() => requestDeviceWithKeyDown({
-      filters: [{services: ['health_thermometer']}]}))
-    .then(device => device.gatt.connect())
-    .then(gattServer => {
-      return gattServer
-        .getPrimaryService('health_thermometer')
-        .then(service => service.getCharacteristic('measurement_interval'))
-        .then(measurement_interval => {
-          let promise = assert_promise_rejects_with_message(
-            measurement_interval.getDescriptor(user_description.name),
-            new DOMException(
-              'GATT Server is disconnected. Cannot perform GATT operations. ' +
-              '(Re)connect first with `device.gatt.connect`.',
-              'NetworkError'));
-          gattServer.disconnect();
-          return promise;
-        });
-    });
-}, 'disconnect() called during a getDescriptor call that succeeds. ' +
-   'Reject with NetworkError.');
-
-</script>
diff --git a/third_party/WebKit/LayoutTests/bluetooth/characteristic/getDescriptors/gen-descriptor-device-disconnects-before-with-uuid.html b/third_party/WebKit/LayoutTests/bluetooth/characteristic/getDescriptors/gen-descriptor-device-disconnects-before-with-uuid.html
new file mode 100644
index 0000000..3fe240d
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/bluetooth/characteristic/getDescriptors/gen-descriptor-device-disconnects-before-with-uuid.html
@@ -0,0 +1,33 @@
+<!-- Generated by //third_party/WebKit/LayoutTests/bluetooth/generate.py -->
+<!DOCTYPE html>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script>
+'use strict';
+promise_test(() => {
+  let val = new Uint8Array([1]);
+  return setBluetoothFakeAdapter('DisconnectingHealthThermometerAdapter')
+      .then(() => requestDeviceWithKeyDown({
+              filters: [{services: ['health_thermometer']}],
+              optionalServices: [request_disconnection_service_uuid]
+            }))
+      .then(device => device.gatt.connect())
+      .then(gattServer => {
+        let measurement_interval;
+        return gattServer.getPrimaryService('health_thermometer')
+            .then(ht => ht.getCharacteristic('measurement_interval'))
+            .then(mi => measurement_interval = mi)
+            .then(() => get_request_disconnection(gattServer))
+            .then(requestDisconnection => requestDisconnection())
+            .then(
+                () => assert_promise_rejects_with_message(
+                    measurement_interval.getDescriptors(user_description.name),
+                    new DOMException(
+                        'GATT Server is disconnected. Cannot retrieve descriptors. ' +
+                            '(Re)connect first with `device.gatt.connect`.',
+                        'NetworkError')));
+      });
+}, 'Device disconnects before getDescriptors. Reject with NetworkError.');
+
+</script>
diff --git a/third_party/WebKit/LayoutTests/bluetooth/characteristic/getDescriptors/gen-descriptor-device-disconnects-before.html b/third_party/WebKit/LayoutTests/bluetooth/characteristic/getDescriptors/gen-descriptor-device-disconnects-before.html
new file mode 100644
index 0000000..1452d0e
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/bluetooth/characteristic/getDescriptors/gen-descriptor-device-disconnects-before.html
@@ -0,0 +1,33 @@
+<!-- Generated by //third_party/WebKit/LayoutTests/bluetooth/generate.py -->
+<!DOCTYPE html>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script>
+'use strict';
+promise_test(() => {
+  let val = new Uint8Array([1]);
+  return setBluetoothFakeAdapter('DisconnectingHealthThermometerAdapter')
+      .then(() => requestDeviceWithKeyDown({
+              filters: [{services: ['health_thermometer']}],
+              optionalServices: [request_disconnection_service_uuid]
+            }))
+      .then(device => device.gatt.connect())
+      .then(gattServer => {
+        let measurement_interval;
+        return gattServer.getPrimaryService('health_thermometer')
+            .then(ht => ht.getCharacteristic('measurement_interval'))
+            .then(mi => measurement_interval = mi)
+            .then(() => get_request_disconnection(gattServer))
+            .then(requestDisconnection => requestDisconnection())
+            .then(
+                () => assert_promise_rejects_with_message(
+                    measurement_interval.getDescriptors(),
+                    new DOMException(
+                        'GATT Server is disconnected. Cannot retrieve descriptors. ' +
+                            '(Re)connect first with `device.gatt.connect`.',
+                        'NetworkError')));
+      });
+}, 'Device disconnects before getDescriptors. Reject with NetworkError.');
+
+</script>
diff --git a/third_party/WebKit/LayoutTests/bluetooth/characteristic/getDescriptors/gen-descriptor-device-disconnects-during-error-with-uuid.html b/third_party/WebKit/LayoutTests/bluetooth/characteristic/getDescriptors/gen-descriptor-device-disconnects-during-error-with-uuid.html
new file mode 100644
index 0000000..6c3dd143
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/bluetooth/characteristic/getDescriptors/gen-descriptor-device-disconnects-during-error-with-uuid.html
@@ -0,0 +1,37 @@
+<!-- Generated by //third_party/WebKit/LayoutTests/bluetooth/generate.py -->
+<!DOCTYPE html>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script>
+'use strict';
+promise_test(
+    () => {
+      let val = new Uint8Array([1]);
+      return setBluetoothFakeAdapter('FailingGATTOperationsAdapter')
+          .then(() => requestDeviceWithKeyDown({
+                  filters: [{services: [errorUUID(0xA0)]}],
+                  optionalServices: [request_disconnection_service_uuid]
+                }))
+          .then(device => device.gatt.connect())
+          .then(gattServer => {
+            let error_characteristic;
+            return gattServer.getPrimaryService(errorUUID(0xA0))
+                .then(es => es.getCharacteristic(errorUUID(0xA1)))
+                .then(ec => error_characteristic = ec)
+                .then(() => get_request_disconnection(gattServer))
+                .then(requestDisconnection => {
+                  requestDisconnection();
+                  return assert_promise_rejects_with_message(
+                      error_characteristic.getDescriptors(user_description.name),
+                      new DOMException(
+                          'GATT Server is disconnected. Cannot retrieve descriptors. ' +
+                              '(Re)connect first with `device.gatt.connect`.',
+                          'NetworkError'));
+                });
+          });
+    },
+    'Device disconnects during a getDescriptors call that fails. ' +
+        'Reject with NetworkError.');
+
+</script>
diff --git a/third_party/WebKit/LayoutTests/bluetooth/characteristic/getDescriptors/gen-descriptor-device-disconnects-during-error.html b/third_party/WebKit/LayoutTests/bluetooth/characteristic/getDescriptors/gen-descriptor-device-disconnects-during-error.html
new file mode 100644
index 0000000..36d922f
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/bluetooth/characteristic/getDescriptors/gen-descriptor-device-disconnects-during-error.html
@@ -0,0 +1,37 @@
+<!-- Generated by //third_party/WebKit/LayoutTests/bluetooth/generate.py -->
+<!DOCTYPE html>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script>
+'use strict';
+promise_test(
+    () => {
+      let val = new Uint8Array([1]);
+      return setBluetoothFakeAdapter('FailingGATTOperationsAdapter')
+          .then(() => requestDeviceWithKeyDown({
+                  filters: [{services: [errorUUID(0xA0)]}],
+                  optionalServices: [request_disconnection_service_uuid]
+                }))
+          .then(device => device.gatt.connect())
+          .then(gattServer => {
+            let error_characteristic;
+            return gattServer.getPrimaryService(errorUUID(0xA0))
+                .then(es => es.getCharacteristic(errorUUID(0xA1)))
+                .then(ec => error_characteristic = ec)
+                .then(() => get_request_disconnection(gattServer))
+                .then(requestDisconnection => {
+                  requestDisconnection();
+                  return assert_promise_rejects_with_message(
+                      error_characteristic.getDescriptors(),
+                      new DOMException(
+                          'GATT Server is disconnected. Cannot retrieve descriptors. ' +
+                              '(Re)connect first with `device.gatt.connect`.',
+                          'NetworkError'));
+                });
+          });
+    },
+    'Device disconnects during a getDescriptors call that fails. ' +
+        'Reject with NetworkError.');
+
+</script>
diff --git a/third_party/WebKit/LayoutTests/bluetooth/characteristic/getDescriptors/gen-descriptor-device-disconnects-during-success-with-uuid.html b/third_party/WebKit/LayoutTests/bluetooth/characteristic/getDescriptors/gen-descriptor-device-disconnects-during-success-with-uuid.html
new file mode 100644
index 0000000..bec4f24
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/bluetooth/characteristic/getDescriptors/gen-descriptor-device-disconnects-during-success-with-uuid.html
@@ -0,0 +1,37 @@
+<!-- Generated by //third_party/WebKit/LayoutTests/bluetooth/generate.py -->
+<!DOCTYPE html>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script>
+'use strict';
+promise_test(
+    () => {
+      let val = new Uint8Array([1]);
+      return setBluetoothFakeAdapter('DisconnectingHealthThermometerAdapter')
+          .then(() => requestDeviceWithKeyDown({
+                  filters: [{services: ['health_thermometer']}],
+                  optionalServices: [request_disconnection_service_uuid]
+                }))
+          .then(device => device.gatt.connect())
+          .then(gattServer => {
+            let measurement_interval;
+            return gattServer.getPrimaryService('health_thermometer')
+                .then(ht => ht.getCharacteristic('measurement_interval'))
+                .then(mi => measurement_interval = mi)
+                .then(() => get_request_disconnection(gattServer))
+                .then(requestDisconnection => {
+                  requestDisconnection();
+                  return assert_promise_rejects_with_message(
+                      measurement_interval.getDescriptors(user_description.name),
+                      new DOMException(
+                          'GATT Server is disconnected. Cannot retrieve descriptors. ' +
+                              '(Re)connect first with `device.gatt.connect`.',
+                          'NetworkError'));
+                });
+          });
+    },
+    'Device disconnects during a getDescriptors call that succeeds. ' +
+        'Reject with NetworkError.');
+
+</script>
diff --git a/third_party/WebKit/LayoutTests/bluetooth/characteristic/getDescriptors/gen-descriptor-device-disconnects-during-success.html b/third_party/WebKit/LayoutTests/bluetooth/characteristic/getDescriptors/gen-descriptor-device-disconnects-during-success.html
new file mode 100644
index 0000000..95e36b48
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/bluetooth/characteristic/getDescriptors/gen-descriptor-device-disconnects-during-success.html
@@ -0,0 +1,37 @@
+<!-- Generated by //third_party/WebKit/LayoutTests/bluetooth/generate.py -->
+<!DOCTYPE html>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script>
+'use strict';
+promise_test(
+    () => {
+      let val = new Uint8Array([1]);
+      return setBluetoothFakeAdapter('DisconnectingHealthThermometerAdapter')
+          .then(() => requestDeviceWithKeyDown({
+                  filters: [{services: ['health_thermometer']}],
+                  optionalServices: [request_disconnection_service_uuid]
+                }))
+          .then(device => device.gatt.connect())
+          .then(gattServer => {
+            let measurement_interval;
+            return gattServer.getPrimaryService('health_thermometer')
+                .then(ht => ht.getCharacteristic('measurement_interval'))
+                .then(mi => measurement_interval = mi)
+                .then(() => get_request_disconnection(gattServer))
+                .then(requestDisconnection => {
+                  requestDisconnection();
+                  return assert_promise_rejects_with_message(
+                      measurement_interval.getDescriptors(),
+                      new DOMException(
+                          'GATT Server is disconnected. Cannot retrieve descriptors. ' +
+                              '(Re)connect first with `device.gatt.connect`.',
+                          'NetworkError'));
+                });
+          });
+    },
+    'Device disconnects during a getDescriptors call that succeeds. ' +
+        'Reject with NetworkError.');
+
+</script>
diff --git a/third_party/WebKit/LayoutTests/bluetooth/characteristic/getDescriptors/gen-descriptor-disconnect-called-before-with-uuid.html b/third_party/WebKit/LayoutTests/bluetooth/characteristic/getDescriptors/gen-descriptor-disconnect-called-before-with-uuid.html
new file mode 100644
index 0000000..b0250d0
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/bluetooth/characteristic/getDescriptors/gen-descriptor-disconnect-called-before-with-uuid.html
@@ -0,0 +1,30 @@
+<!-- Generated by //third_party/WebKit/LayoutTests/bluetooth/generate.py -->
+<!DOCTYPE html>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script>
+'use strict';
+promise_test(() => {
+  let val = new Uint8Array([1]);
+  return setBluetoothFakeAdapter('DisconnectingHealthThermometerAdapter')
+      .then(
+          () => requestDeviceWithKeyDown(
+              {filters: [{services: ['health_thermometer']}]}))
+      .then(device => device.gatt.connect())
+      .then(gattServer => {
+        return gattServer.getPrimaryService('health_thermometer')
+            .then(service => service.getCharacteristic('measurement_interval'))
+            .then(measurement_interval => {
+              gattServer.disconnect();
+              return assert_promise_rejects_with_message(
+                  measurement_interval.getDescriptors(user_description.name),
+                  new DOMException(
+                      'GATT Server is disconnected. Cannot retrieve descriptors. ' +
+                          '(Re)connect first with `device.gatt.connect`.',
+                      'NetworkError'));
+            });
+      });
+}, 'disconnect() called before getDescriptors. Reject with NetworkError.');
+
+</script>
diff --git a/third_party/WebKit/LayoutTests/bluetooth/characteristic/getDescriptors/gen-descriptor-disconnect-called-before.html b/third_party/WebKit/LayoutTests/bluetooth/characteristic/getDescriptors/gen-descriptor-disconnect-called-before.html
new file mode 100644
index 0000000..c3b37a2
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/bluetooth/characteristic/getDescriptors/gen-descriptor-disconnect-called-before.html
@@ -0,0 +1,30 @@
+<!-- Generated by //third_party/WebKit/LayoutTests/bluetooth/generate.py -->
+<!DOCTYPE html>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script>
+'use strict';
+promise_test(() => {
+  let val = new Uint8Array([1]);
+  return setBluetoothFakeAdapter('DisconnectingHealthThermometerAdapter')
+      .then(
+          () => requestDeviceWithKeyDown(
+              {filters: [{services: ['health_thermometer']}]}))
+      .then(device => device.gatt.connect())
+      .then(gattServer => {
+        return gattServer.getPrimaryService('health_thermometer')
+            .then(service => service.getCharacteristic('measurement_interval'))
+            .then(measurement_interval => {
+              gattServer.disconnect();
+              return assert_promise_rejects_with_message(
+                  measurement_interval.getDescriptors(),
+                  new DOMException(
+                      'GATT Server is disconnected. Cannot retrieve descriptors. ' +
+                          '(Re)connect first with `device.gatt.connect`.',
+                      'NetworkError'));
+            });
+      });
+}, 'disconnect() called before getDescriptors. Reject with NetworkError.');
+
+</script>
diff --git a/third_party/WebKit/LayoutTests/bluetooth/characteristic/getDescriptors/gen-descriptor-disconnect-called-during-error-with-uuid.html b/third_party/WebKit/LayoutTests/bluetooth/characteristic/getDescriptors/gen-descriptor-disconnect-called-during-error-with-uuid.html
new file mode 100644
index 0000000..e58a85e9
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/bluetooth/characteristic/getDescriptors/gen-descriptor-disconnect-called-during-error-with-uuid.html
@@ -0,0 +1,34 @@
+<!-- Generated by //third_party/WebKit/LayoutTests/bluetooth/generate.py -->
+<!DOCTYPE html>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script>
+'use strict';
+promise_test(
+    () => {
+      let val = new Uint8Array([1]);
+      return setBluetoothFakeAdapter('FailingGATTOperationsAdapter')
+          .then(
+              () => requestDeviceWithKeyDown(
+                  {filters: [{services: [errorUUID(0xA0)]}]}))
+          .then(device => device.gatt.connect())
+          .then(gattServer => {
+            return gattServer.getPrimaryService(errorUUID(0xA0))
+                .then(service => service.getCharacteristic(errorUUID(0xA1)))
+                .then(error_characteristic => {
+                  let promise = assert_promise_rejects_with_message(
+                      error_characteristic.getDescriptors(user_description.name),
+                      new DOMException(
+                          'GATT Server is disconnected. Cannot retrieve descriptors. ' +
+                              '(Re)connect first with `device.gatt.connect`.',
+                          'NetworkError'));
+                  gattServer.disconnect();
+                  return promise;
+                });
+          });
+    },
+    'disconnect() called during a getDescriptors call that fails. ' +
+        'Reject with NetworkError.');
+
+</script>
diff --git a/third_party/WebKit/LayoutTests/bluetooth/characteristic/getDescriptors/gen-descriptor-disconnect-called-during-error.html b/third_party/WebKit/LayoutTests/bluetooth/characteristic/getDescriptors/gen-descriptor-disconnect-called-during-error.html
new file mode 100644
index 0000000..27858f2c
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/bluetooth/characteristic/getDescriptors/gen-descriptor-disconnect-called-during-error.html
@@ -0,0 +1,34 @@
+<!-- Generated by //third_party/WebKit/LayoutTests/bluetooth/generate.py -->
+<!DOCTYPE html>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script>
+'use strict';
+promise_test(
+    () => {
+      let val = new Uint8Array([1]);
+      return setBluetoothFakeAdapter('FailingGATTOperationsAdapter')
+          .then(
+              () => requestDeviceWithKeyDown(
+                  {filters: [{services: [errorUUID(0xA0)]}]}))
+          .then(device => device.gatt.connect())
+          .then(gattServer => {
+            return gattServer.getPrimaryService(errorUUID(0xA0))
+                .then(service => service.getCharacteristic(errorUUID(0xA1)))
+                .then(error_characteristic => {
+                  let promise = assert_promise_rejects_with_message(
+                      error_characteristic.getDescriptors(),
+                      new DOMException(
+                          'GATT Server is disconnected. Cannot retrieve descriptors. ' +
+                              '(Re)connect first with `device.gatt.connect`.',
+                          'NetworkError'));
+                  gattServer.disconnect();
+                  return promise;
+                });
+          });
+    },
+    'disconnect() called during a getDescriptors call that fails. ' +
+        'Reject with NetworkError.');
+
+</script>
diff --git a/third_party/WebKit/LayoutTests/bluetooth/characteristic/getDescriptors/gen-descriptor-disconnect-called-during-success-with-uuid.html b/third_party/WebKit/LayoutTests/bluetooth/characteristic/getDescriptors/gen-descriptor-disconnect-called-during-success-with-uuid.html
new file mode 100644
index 0000000..a26d8d6e
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/bluetooth/characteristic/getDescriptors/gen-descriptor-disconnect-called-during-success-with-uuid.html
@@ -0,0 +1,36 @@
+<!-- Generated by //third_party/WebKit/LayoutTests/bluetooth/generate.py -->
+<!DOCTYPE html>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script>
+'use strict';
+promise_test(
+    () => {
+      let val = new Uint8Array([1]);
+      return setBluetoothFakeAdapter('DisconnectingHealthThermometerAdapter')
+          .then(
+              () => requestDeviceWithKeyDown(
+                  {filters: [{services: ['health_thermometer']}]}))
+          .then(device => device.gatt.connect())
+          .then(gattServer => {
+            return gattServer.getPrimaryService('health_thermometer')
+                .then(
+                    service =>
+                        service.getCharacteristic('measurement_interval'))
+                .then(measurement_interval => {
+                  let promise = assert_promise_rejects_with_message(
+                      measurement_interval.getDescriptors(user_description.name),
+                      new DOMException(
+                          'GATT Server is disconnected. Cannot retrieve descriptors. ' +
+                              '(Re)connect first with `device.gatt.connect`.',
+                          'NetworkError'));
+                  gattServer.disconnect();
+                  return promise;
+                });
+          });
+    },
+    'disconnect() called during a getDescriptors call that succeeds. ' +
+        'Reject with NetworkError.');
+
+</script>
diff --git a/third_party/WebKit/LayoutTests/bluetooth/characteristic/getDescriptors/gen-descriptor-disconnect-called-during-success.html b/third_party/WebKit/LayoutTests/bluetooth/characteristic/getDescriptors/gen-descriptor-disconnect-called-during-success.html
new file mode 100644
index 0000000..1ee3ba3
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/bluetooth/characteristic/getDescriptors/gen-descriptor-disconnect-called-during-success.html
@@ -0,0 +1,36 @@
+<!-- Generated by //third_party/WebKit/LayoutTests/bluetooth/generate.py -->
+<!DOCTYPE html>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script>
+'use strict';
+promise_test(
+    () => {
+      let val = new Uint8Array([1]);
+      return setBluetoothFakeAdapter('DisconnectingHealthThermometerAdapter')
+          .then(
+              () => requestDeviceWithKeyDown(
+                  {filters: [{services: ['health_thermometer']}]}))
+          .then(device => device.gatt.connect())
+          .then(gattServer => {
+            return gattServer.getPrimaryService('health_thermometer')
+                .then(
+                    service =>
+                        service.getCharacteristic('measurement_interval'))
+                .then(measurement_interval => {
+                  let promise = assert_promise_rejects_with_message(
+                      measurement_interval.getDescriptors(),
+                      new DOMException(
+                          'GATT Server is disconnected. Cannot retrieve descriptors. ' +
+                              '(Re)connect first with `device.gatt.connect`.',
+                          'NetworkError'));
+                  gattServer.disconnect();
+                  return promise;
+                });
+          });
+    },
+    'disconnect() called during a getDescriptors call that succeeds. ' +
+        'Reject with NetworkError.');
+
+</script>
diff --git a/third_party/WebKit/LayoutTests/bluetooth/characteristic/getDescriptors/gen-descriptor-garbage-collection-ran-during-error-with-uuid.html b/third_party/WebKit/LayoutTests/bluetooth/characteristic/getDescriptors/gen-descriptor-garbage-collection-ran-during-error-with-uuid.html
index a4076d03..66689ec 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/characteristic/getDescriptors/gen-descriptor-garbage-collection-ran-during-error-with-uuid.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/characteristic/getDescriptors/gen-descriptor-garbage-collection-ran-during-error-with-uuid.html
@@ -8,25 +8,26 @@
 promise_test(() => {
   let promise;
   return setBluetoothFakeAdapter('DisconnectingHealthThermometerAdapter')
-    .then(() => requestDeviceWithKeyDown({
-      filters: [{services: ['health_thermometer']}]}))
-    .then(device => device.gatt.connect())
-    .then(gattServer => gattServer.getPrimaryService('health_thermometer'))
-    .then(service => service.getCharacteristic('measurement_interval'))
-    .then(characteristic => {
-      promise = assert_promise_rejects_with_message(
-        /* 0x0101 doesn't exist in this characteristic */
-        characteristic.getDescriptors(0x0101),
-        new DOMException(
-          'GATT Server is disconnected. Cannot perform GATT operations. ' +
-          '(Re)connect first with `device.gatt.connect`.',
-          'NetworkError'));
-      // Disconnect called to clear attributeInstanceMap and allow the
-      // object to get garbage collected.
-      characteristic.service.device.gatt.disconnect();
-    })
-    .then(runGarbageCollection)
-    .then(() => promise);
+      .then(
+          () => requestDeviceWithKeyDown(
+              {filters: [{services: ['health_thermometer']}]}))
+      .then(device => device.gatt.connect())
+      .then(gattServer => gattServer.getPrimaryService('health_thermometer'))
+      .then(service => service.getCharacteristic('measurement_interval'))
+      .then(characteristic => {
+        promise = assert_promise_rejects_with_message(
+            /* 0x0101 doesn't exist in this characteristic */
+            characteristic.getDescriptors(0x0101),
+            new DOMException(
+                'GATT Server is disconnected. Cannot retrieve descriptors. ' +
+                    '(Re)connect first with `device.gatt.connect`.',
+                'NetworkError'));
+        // Disconnect called to clear attributeInstanceMap and allow the
+        // object to get garbage collected.
+        characteristic.service.device.gatt.disconnect();
+      })
+      .then(runGarbageCollection)
+      .then(() => promise);
 }, 'Garbage Collection ran during getDescriptors call that fails. ' +
    'Should not crash');
 
diff --git a/third_party/WebKit/LayoutTests/bluetooth/characteristic/getDescriptors/gen-descriptor-garbage-collection-ran-during-error.html b/third_party/WebKit/LayoutTests/bluetooth/characteristic/getDescriptors/gen-descriptor-garbage-collection-ran-during-error.html
index fe5961e7..c31ea77 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/characteristic/getDescriptors/gen-descriptor-garbage-collection-ran-during-error.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/characteristic/getDescriptors/gen-descriptor-garbage-collection-ran-during-error.html
@@ -8,25 +8,26 @@
 promise_test(() => {
   let promise;
   return setBluetoothFakeAdapter('DisconnectingHealthThermometerAdapter')
-    .then(() => requestDeviceWithKeyDown({
-      filters: [{services: ['health_thermometer']}]}))
-    .then(device => device.gatt.connect())
-    .then(gattServer => gattServer.getPrimaryService('health_thermometer'))
-    .then(service => service.getCharacteristic('measurement_interval'))
-    .then(characteristic => {
-      promise = assert_promise_rejects_with_message(
-        /* 0x0101 doesn't exist in this characteristic */
-        characteristic.getDescriptors(),
-        new DOMException(
-          'GATT Server is disconnected. Cannot perform GATT operations. ' +
-          '(Re)connect first with `device.gatt.connect`.',
-          'NetworkError'));
-      // Disconnect called to clear attributeInstanceMap and allow the
-      // object to get garbage collected.
-      characteristic.service.device.gatt.disconnect();
-    })
-    .then(runGarbageCollection)
-    .then(() => promise);
+      .then(
+          () => requestDeviceWithKeyDown(
+              {filters: [{services: ['health_thermometer']}]}))
+      .then(device => device.gatt.connect())
+      .then(gattServer => gattServer.getPrimaryService('health_thermometer'))
+      .then(service => service.getCharacteristic('measurement_interval'))
+      .then(characteristic => {
+        promise = assert_promise_rejects_with_message(
+            /* 0x0101 doesn't exist in this characteristic */
+            characteristic.getDescriptors(),
+            new DOMException(
+                'GATT Server is disconnected. Cannot retrieve descriptors. ' +
+                    '(Re)connect first with `device.gatt.connect`.',
+                'NetworkError'));
+        // Disconnect called to clear attributeInstanceMap and allow the
+        // object to get garbage collected.
+        characteristic.service.device.gatt.disconnect();
+      })
+      .then(runGarbageCollection)
+      .then(() => promise);
 }, 'Garbage Collection ran during getDescriptors call that fails. ' +
    'Should not crash');
 
diff --git a/third_party/WebKit/LayoutTests/bluetooth/characteristic/getDescriptors/gen-descriptor-garbage-collection-ran-during-success-with-uuid.html b/third_party/WebKit/LayoutTests/bluetooth/characteristic/getDescriptors/gen-descriptor-garbage-collection-ran-during-success-with-uuid.html
index 6dd208d..8c39dfe 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/characteristic/getDescriptors/gen-descriptor-garbage-collection-ran-during-success-with-uuid.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/characteristic/getDescriptors/gen-descriptor-garbage-collection-ran-during-success-with-uuid.html
@@ -8,24 +8,25 @@
 promise_test(() => {
   let promise;
   return setBluetoothFakeAdapter('DisconnectingHealthThermometerAdapter')
-    .then(() => requestDeviceWithKeyDown({
-      filters: [{services: ['health_thermometer']}]}))
-    .then(device => device.gatt.connect())
-    .then(gattServer => gattServer.getPrimaryService('health_thermometer'))
-    .then(service => service.getCharacteristic('measurement_interval'))
-    .then(characteristic => {
-      promise = assert_promise_rejects_with_message(
-        characteristic.getDescriptors(user_description.name),
-        new DOMException(
-          'GATT Server is disconnected. Cannot perform GATT operations. ' +
-          '(Re)connect first with `device.gatt.connect`.',
-          'NetworkError'));
-      // Disconnect called to clear attributeInstanceMap and allow the
-      // object to get garbage collected.
-      characteristic.service.device.gatt.disconnect();
-    })
-    .then(runGarbageCollection)
-    .then(() => promise);
+      .then(
+          () => requestDeviceWithKeyDown(
+              {filters: [{services: ['health_thermometer']}]}))
+      .then(device => device.gatt.connect())
+      .then(gattServer => gattServer.getPrimaryService('health_thermometer'))
+      .then(service => service.getCharacteristic('measurement_interval'))
+      .then(characteristic => {
+        promise = assert_promise_rejects_with_message(
+            characteristic.getDescriptors(user_description.name),
+            new DOMException(
+                'GATT Server is disconnected. Cannot retrieve descriptors. ' +
+                    '(Re)connect first with `device.gatt.connect`.',
+                'NetworkError'));
+        // Disconnect called to clear attributeInstanceMap and allow the
+        // object to get garbage collected.
+        characteristic.service.device.gatt.disconnect();
+      })
+      .then(runGarbageCollection)
+      .then(() => promise);
 }, 'Garbage Collection ran during a getDescriptors call that succeeds. ' +
    'Should not crash.');
 
diff --git a/third_party/WebKit/LayoutTests/bluetooth/characteristic/getDescriptors/gen-descriptor-garbage-collection-ran-during-success.html b/third_party/WebKit/LayoutTests/bluetooth/characteristic/getDescriptors/gen-descriptor-garbage-collection-ran-during-success.html
index 41fa623..42c1860 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/characteristic/getDescriptors/gen-descriptor-garbage-collection-ran-during-success.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/characteristic/getDescriptors/gen-descriptor-garbage-collection-ran-during-success.html
@@ -8,24 +8,25 @@
 promise_test(() => {
   let promise;
   return setBluetoothFakeAdapter('DisconnectingHealthThermometerAdapter')
-    .then(() => requestDeviceWithKeyDown({
-      filters: [{services: ['health_thermometer']}]}))
-    .then(device => device.gatt.connect())
-    .then(gattServer => gattServer.getPrimaryService('health_thermometer'))
-    .then(service => service.getCharacteristic('measurement_interval'))
-    .then(characteristic => {
-      promise = assert_promise_rejects_with_message(
-        characteristic.getDescriptors(),
-        new DOMException(
-          'GATT Server is disconnected. Cannot perform GATT operations. ' +
-          '(Re)connect first with `device.gatt.connect`.',
-          'NetworkError'));
-      // Disconnect called to clear attributeInstanceMap and allow the
-      // object to get garbage collected.
-      characteristic.service.device.gatt.disconnect();
-    })
-    .then(runGarbageCollection)
-    .then(() => promise);
+      .then(
+          () => requestDeviceWithKeyDown(
+              {filters: [{services: ['health_thermometer']}]}))
+      .then(device => device.gatt.connect())
+      .then(gattServer => gattServer.getPrimaryService('health_thermometer'))
+      .then(service => service.getCharacteristic('measurement_interval'))
+      .then(characteristic => {
+        promise = assert_promise_rejects_with_message(
+            characteristic.getDescriptors(),
+            new DOMException(
+                'GATT Server is disconnected. Cannot retrieve descriptors. ' +
+                    '(Re)connect first with `device.gatt.connect`.',
+                'NetworkError'));
+        // Disconnect called to clear attributeInstanceMap and allow the
+        // object to get garbage collected.
+        characteristic.service.device.gatt.disconnect();
+      })
+      .then(runGarbageCollection)
+      .then(() => promise);
 }, 'Garbage Collection ran during a getDescriptors call that succeeds. ' +
    'Should not crash.');
 
diff --git a/third_party/WebKit/LayoutTests/bluetooth/characteristic/getDescriptors/gen-gatt-op-device-disconnects-before-with-uuid.html b/third_party/WebKit/LayoutTests/bluetooth/characteristic/getDescriptors/gen-gatt-op-device-disconnects-before-with-uuid.html
deleted file mode 100644
index 1b2bdc0a..0000000
--- a/third_party/WebKit/LayoutTests/bluetooth/characteristic/getDescriptors/gen-gatt-op-device-disconnects-before-with-uuid.html
+++ /dev/null
@@ -1,32 +0,0 @@
-<!-- Generated by //third_party/WebKit/LayoutTests/bluetooth/generate.py -->
-<!DOCTYPE html>
-<script src="../../../resources/testharness.js"></script>
-<script src="../../../resources/testharnessreport.js"></script>
-<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
-<script>
-'use strict';
-promise_test(() => {
-  let val = new Uint8Array([1]);
-  return setBluetoothFakeAdapter('DisconnectingHealthThermometerAdapter')
-    .then(() => requestDeviceWithKeyDown({
-      filters: [{services: ['health_thermometer']}],
-      optionalServices: [request_disconnection_service_uuid]
-    }))
-    .then(device => device.gatt.connect())
-    .then(gattServer => {
-      let measurement_interval;
-      return gattServer.getPrimaryService('health_thermometer')
-        .then(ht=> ht.getCharacteristic('measurement_interval'))
-        .then(mi => measurement_interval = mi)
-        .then(() => get_request_disconnection(gattServer))
-        .then(requestDisconnection => requestDisconnection())
-        .then(() => assert_promise_rejects_with_message(
-          measurement_interval.getDescriptors(user_description.name),
-          new DOMException(
-            'GATT Server is disconnected. Cannot perform GATT operations. ' +
-            '(Re)connect first with `device.gatt.connect`.',
-            'NetworkError')));
-    });
-}, 'Device disconnects before getDescriptors. Reject with NetworkError.');
-
-</script>
diff --git a/third_party/WebKit/LayoutTests/bluetooth/characteristic/getDescriptors/gen-gatt-op-device-disconnects-before.html b/third_party/WebKit/LayoutTests/bluetooth/characteristic/getDescriptors/gen-gatt-op-device-disconnects-before.html
deleted file mode 100644
index c687ca6..0000000
--- a/third_party/WebKit/LayoutTests/bluetooth/characteristic/getDescriptors/gen-gatt-op-device-disconnects-before.html
+++ /dev/null
@@ -1,32 +0,0 @@
-<!-- Generated by //third_party/WebKit/LayoutTests/bluetooth/generate.py -->
-<!DOCTYPE html>
-<script src="../../../resources/testharness.js"></script>
-<script src="../../../resources/testharnessreport.js"></script>
-<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
-<script>
-'use strict';
-promise_test(() => {
-  let val = new Uint8Array([1]);
-  return setBluetoothFakeAdapter('DisconnectingHealthThermometerAdapter')
-    .then(() => requestDeviceWithKeyDown({
-      filters: [{services: ['health_thermometer']}],
-      optionalServices: [request_disconnection_service_uuid]
-    }))
-    .then(device => device.gatt.connect())
-    .then(gattServer => {
-      let measurement_interval;
-      return gattServer.getPrimaryService('health_thermometer')
-        .then(ht=> ht.getCharacteristic('measurement_interval'))
-        .then(mi => measurement_interval = mi)
-        .then(() => get_request_disconnection(gattServer))
-        .then(requestDisconnection => requestDisconnection())
-        .then(() => assert_promise_rejects_with_message(
-          measurement_interval.getDescriptors(),
-          new DOMException(
-            'GATT Server is disconnected. Cannot perform GATT operations. ' +
-            '(Re)connect first with `device.gatt.connect`.',
-            'NetworkError')));
-    });
-}, 'Device disconnects before getDescriptors. Reject with NetworkError.');
-
-</script>
diff --git a/third_party/WebKit/LayoutTests/bluetooth/characteristic/getDescriptors/gen-gatt-op-device-disconnects-during-error-with-uuid.html b/third_party/WebKit/LayoutTests/bluetooth/characteristic/getDescriptors/gen-gatt-op-device-disconnects-during-error-with-uuid.html
deleted file mode 100644
index aef4d6f..0000000
--- a/third_party/WebKit/LayoutTests/bluetooth/characteristic/getDescriptors/gen-gatt-op-device-disconnects-during-error-with-uuid.html
+++ /dev/null
@@ -1,36 +0,0 @@
-<!-- Generated by //third_party/WebKit/LayoutTests/bluetooth/generate.py -->
-<!DOCTYPE html>
-<script src="../../../resources/testharness.js"></script>
-<script src="../../../resources/testharnessreport.js"></script>
-<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
-<script>
-'use strict';
-promise_test(() => {
-  let val = new Uint8Array([1]);
-  return setBluetoothFakeAdapter('FailingGATTOperationsAdapter')
-    .then(() => requestDeviceWithKeyDown({
-      filters: [{services: [errorUUID(0xA0)]}],
-      optionalServices: [request_disconnection_service_uuid]
-    }))
-    .then(device => device.gatt.connect())
-    .then(gattServer => {
-      let error_characteristic;
-      return gattServer
-        .getPrimaryService(errorUUID(0xA0))
-        .then(es => es.getCharacteristic(errorUUID(0xA1)))
-        .then(ec => error_characteristic = ec)
-        .then(() => get_request_disconnection(gattServer))
-        .then(requestDisconnection => {
-          requestDisconnection();
-          return assert_promise_rejects_with_message(
-            error_characteristic.getDescriptors(user_description.name),
-            new DOMException(
-              'GATT Server is disconnected. Cannot perform GATT operations. ' +
-              '(Re)connect first with `device.gatt.connect`.',
-              'NetworkError'));
-        });
-    });
-}, 'Device disconnects during a getDescriptors call that fails. ' +
-   'Reject with NetworkError.');
-
-</script>
diff --git a/third_party/WebKit/LayoutTests/bluetooth/characteristic/getDescriptors/gen-gatt-op-device-disconnects-during-error.html b/third_party/WebKit/LayoutTests/bluetooth/characteristic/getDescriptors/gen-gatt-op-device-disconnects-during-error.html
deleted file mode 100644
index e634f4b..0000000
--- a/third_party/WebKit/LayoutTests/bluetooth/characteristic/getDescriptors/gen-gatt-op-device-disconnects-during-error.html
+++ /dev/null
@@ -1,36 +0,0 @@
-<!-- Generated by //third_party/WebKit/LayoutTests/bluetooth/generate.py -->
-<!DOCTYPE html>
-<script src="../../../resources/testharness.js"></script>
-<script src="../../../resources/testharnessreport.js"></script>
-<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
-<script>
-'use strict';
-promise_test(() => {
-  let val = new Uint8Array([1]);
-  return setBluetoothFakeAdapter('FailingGATTOperationsAdapter')
-    .then(() => requestDeviceWithKeyDown({
-      filters: [{services: [errorUUID(0xA0)]}],
-      optionalServices: [request_disconnection_service_uuid]
-    }))
-    .then(device => device.gatt.connect())
-    .then(gattServer => {
-      let error_characteristic;
-      return gattServer
-        .getPrimaryService(errorUUID(0xA0))
-        .then(es => es.getCharacteristic(errorUUID(0xA1)))
-        .then(ec => error_characteristic = ec)
-        .then(() => get_request_disconnection(gattServer))
-        .then(requestDisconnection => {
-          requestDisconnection();
-          return assert_promise_rejects_with_message(
-            error_characteristic.getDescriptors(),
-            new DOMException(
-              'GATT Server is disconnected. Cannot perform GATT operations. ' +
-              '(Re)connect first with `device.gatt.connect`.',
-              'NetworkError'));
-        });
-    });
-}, 'Device disconnects during a getDescriptors call that fails. ' +
-   'Reject with NetworkError.');
-
-</script>
diff --git a/third_party/WebKit/LayoutTests/bluetooth/characteristic/getDescriptors/gen-gatt-op-device-disconnects-during-success-with-uuid.html b/third_party/WebKit/LayoutTests/bluetooth/characteristic/getDescriptors/gen-gatt-op-device-disconnects-during-success-with-uuid.html
deleted file mode 100644
index 1d6698f..0000000
--- a/third_party/WebKit/LayoutTests/bluetooth/characteristic/getDescriptors/gen-gatt-op-device-disconnects-during-success-with-uuid.html
+++ /dev/null
@@ -1,36 +0,0 @@
-<!-- Generated by //third_party/WebKit/LayoutTests/bluetooth/generate.py -->
-<!DOCTYPE html>
-<script src="../../../resources/testharness.js"></script>
-<script src="../../../resources/testharnessreport.js"></script>
-<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
-<script>
-'use strict';
-promise_test(() => {
-  let val = new Uint8Array([1]);
-  return setBluetoothFakeAdapter('DisconnectingHealthThermometerAdapter')
-    .then(() => requestDeviceWithKeyDown({
-      filters: [{services: ['health_thermometer']}],
-      optionalServices: [request_disconnection_service_uuid]
-    }))
-    .then(device => device.gatt.connect())
-    .then(gattServer => {
-      let measurement_interval;
-      return gattServer
-        .getPrimaryService('health_thermometer')
-        .then(ht=> ht.getCharacteristic('measurement_interval'))
-        .then(mi => measurement_interval = mi)
-        .then(() => get_request_disconnection(gattServer))
-        .then(requestDisconnection => {
-          requestDisconnection();
-          return assert_promise_rejects_with_message(
-            measurement_interval.getDescriptors(user_description.name),
-            new DOMException(
-              'GATT Server is disconnected. Cannot perform GATT operations. ' +
-              '(Re)connect first with `device.gatt.connect`.',
-              'NetworkError'));
-        });
-    });
-}, 'Device disconnects during a getDescriptors call that succeeds. ' +
-   'Reject with NetworkError.');
-
-</script>
diff --git a/third_party/WebKit/LayoutTests/bluetooth/characteristic/getDescriptors/gen-gatt-op-device-disconnects-during-success.html b/third_party/WebKit/LayoutTests/bluetooth/characteristic/getDescriptors/gen-gatt-op-device-disconnects-during-success.html
deleted file mode 100644
index 7c0a80c7..0000000
--- a/third_party/WebKit/LayoutTests/bluetooth/characteristic/getDescriptors/gen-gatt-op-device-disconnects-during-success.html
+++ /dev/null
@@ -1,36 +0,0 @@
-<!-- Generated by //third_party/WebKit/LayoutTests/bluetooth/generate.py -->
-<!DOCTYPE html>
-<script src="../../../resources/testharness.js"></script>
-<script src="../../../resources/testharnessreport.js"></script>
-<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
-<script>
-'use strict';
-promise_test(() => {
-  let val = new Uint8Array([1]);
-  return setBluetoothFakeAdapter('DisconnectingHealthThermometerAdapter')
-    .then(() => requestDeviceWithKeyDown({
-      filters: [{services: ['health_thermometer']}],
-      optionalServices: [request_disconnection_service_uuid]
-    }))
-    .then(device => device.gatt.connect())
-    .then(gattServer => {
-      let measurement_interval;
-      return gattServer
-        .getPrimaryService('health_thermometer')
-        .then(ht=> ht.getCharacteristic('measurement_interval'))
-        .then(mi => measurement_interval = mi)
-        .then(() => get_request_disconnection(gattServer))
-        .then(requestDisconnection => {
-          requestDisconnection();
-          return assert_promise_rejects_with_message(
-            measurement_interval.getDescriptors(),
-            new DOMException(
-              'GATT Server is disconnected. Cannot perform GATT operations. ' +
-              '(Re)connect first with `device.gatt.connect`.',
-              'NetworkError'));
-        });
-    });
-}, 'Device disconnects during a getDescriptors call that succeeds. ' +
-   'Reject with NetworkError.');
-
-</script>
diff --git a/third_party/WebKit/LayoutTests/bluetooth/characteristic/getDescriptors/gen-gatt-op-disconnect-called-before-with-uuid.html b/third_party/WebKit/LayoutTests/bluetooth/characteristic/getDescriptors/gen-gatt-op-disconnect-called-before-with-uuid.html
deleted file mode 100644
index 10a8487..0000000
--- a/third_party/WebKit/LayoutTests/bluetooth/characteristic/getDescriptors/gen-gatt-op-disconnect-called-before-with-uuid.html
+++ /dev/null
@@ -1,29 +0,0 @@
-<!-- Generated by //third_party/WebKit/LayoutTests/bluetooth/generate.py -->
-<!DOCTYPE html>
-<script src="../../../resources/testharness.js"></script>
-<script src="../../../resources/testharnessreport.js"></script>
-<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
-<script>
-'use strict';
-promise_test(() => {
-  let val = new Uint8Array([1]);
-  return setBluetoothFakeAdapter('DisconnectingHealthThermometerAdapter')
-    .then(() => requestDeviceWithKeyDown({
-      filters: [{services: ['health_thermometer']}]}))
-    .then(device => device.gatt.connect())
-    .then(gattServer => {
-      return gattServer.getPrimaryService('health_thermometer')
-        .then(service => service.getCharacteristic('measurement_interval'))
-        .then(measurement_interval => {
-          gattServer.disconnect();
-          return assert_promise_rejects_with_message(
-            measurement_interval.getDescriptors(user_description.name),
-            new DOMException(
-              'GATT Server is disconnected. Cannot perform GATT operations. ' +
-              '(Re)connect first with `device.gatt.connect`.',
-              'NetworkError'));
-        });
-    });
-}, 'disconnect() called before getDescriptors. Reject with NetworkError.');
-
-</script>
diff --git a/third_party/WebKit/LayoutTests/bluetooth/characteristic/getDescriptors/gen-gatt-op-disconnect-called-before.html b/third_party/WebKit/LayoutTests/bluetooth/characteristic/getDescriptors/gen-gatt-op-disconnect-called-before.html
deleted file mode 100644
index 99fd299..0000000
--- a/third_party/WebKit/LayoutTests/bluetooth/characteristic/getDescriptors/gen-gatt-op-disconnect-called-before.html
+++ /dev/null
@@ -1,29 +0,0 @@
-<!-- Generated by //third_party/WebKit/LayoutTests/bluetooth/generate.py -->
-<!DOCTYPE html>
-<script src="../../../resources/testharness.js"></script>
-<script src="../../../resources/testharnessreport.js"></script>
-<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
-<script>
-'use strict';
-promise_test(() => {
-  let val = new Uint8Array([1]);
-  return setBluetoothFakeAdapter('DisconnectingHealthThermometerAdapter')
-    .then(() => requestDeviceWithKeyDown({
-      filters: [{services: ['health_thermometer']}]}))
-    .then(device => device.gatt.connect())
-    .then(gattServer => {
-      return gattServer.getPrimaryService('health_thermometer')
-        .then(service => service.getCharacteristic('measurement_interval'))
-        .then(measurement_interval => {
-          gattServer.disconnect();
-          return assert_promise_rejects_with_message(
-            measurement_interval.getDescriptors(),
-            new DOMException(
-              'GATT Server is disconnected. Cannot perform GATT operations. ' +
-              '(Re)connect first with `device.gatt.connect`.',
-              'NetworkError'));
-        });
-    });
-}, 'disconnect() called before getDescriptors. Reject with NetworkError.');
-
-</script>
diff --git a/third_party/WebKit/LayoutTests/bluetooth/characteristic/getDescriptors/gen-gatt-op-disconnect-called-during-error-with-uuid.html b/third_party/WebKit/LayoutTests/bluetooth/characteristic/getDescriptors/gen-gatt-op-disconnect-called-during-error-with-uuid.html
deleted file mode 100644
index cf2e0509..0000000
--- a/third_party/WebKit/LayoutTests/bluetooth/characteristic/getDescriptors/gen-gatt-op-disconnect-called-during-error-with-uuid.html
+++ /dev/null
@@ -1,31 +0,0 @@
-<!-- Generated by //third_party/WebKit/LayoutTests/bluetooth/generate.py -->
-<!DOCTYPE html>
-<script src="../../../resources/testharness.js"></script>
-<script src="../../../resources/testharnessreport.js"></script>
-<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
-<script>
-'use strict';
-promise_test(() => {
-  let val = new Uint8Array([1]);
-  return setBluetoothFakeAdapter('FailingGATTOperationsAdapter')
-    .then(() => requestDeviceWithKeyDown({
-      filters: [{services: [errorUUID(0xA0)]}]}))
-    .then(device => device.gatt.connect())
-    .then(gattServer => {
-      return gattServer.getPrimaryService(errorUUID(0xA0))
-        .then(service => service.getCharacteristic(errorUUID(0xA1)))
-        .then(error_characteristic => {
-          let promise = assert_promise_rejects_with_message(
-            error_characteristic.getDescriptors(user_description.name),
-            new DOMException(
-              'GATT Server is disconnected. Cannot perform GATT operations. ' +
-              '(Re)connect first with `device.gatt.connect`.',
-              'NetworkError'));
-          gattServer.disconnect();
-          return promise;
-        });
-    });
-}, 'disconnect() called during a getDescriptors call that fails. ' +
-   'Reject with NetworkError.');
-
-</script>
diff --git a/third_party/WebKit/LayoutTests/bluetooth/characteristic/getDescriptors/gen-gatt-op-disconnect-called-during-error.html b/third_party/WebKit/LayoutTests/bluetooth/characteristic/getDescriptors/gen-gatt-op-disconnect-called-during-error.html
deleted file mode 100644
index 006f668..0000000
--- a/third_party/WebKit/LayoutTests/bluetooth/characteristic/getDescriptors/gen-gatt-op-disconnect-called-during-error.html
+++ /dev/null
@@ -1,31 +0,0 @@
-<!-- Generated by //third_party/WebKit/LayoutTests/bluetooth/generate.py -->
-<!DOCTYPE html>
-<script src="../../../resources/testharness.js"></script>
-<script src="../../../resources/testharnessreport.js"></script>
-<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
-<script>
-'use strict';
-promise_test(() => {
-  let val = new Uint8Array([1]);
-  return setBluetoothFakeAdapter('FailingGATTOperationsAdapter')
-    .then(() => requestDeviceWithKeyDown({
-      filters: [{services: [errorUUID(0xA0)]}]}))
-    .then(device => device.gatt.connect())
-    .then(gattServer => {
-      return gattServer.getPrimaryService(errorUUID(0xA0))
-        .then(service => service.getCharacteristic(errorUUID(0xA1)))
-        .then(error_characteristic => {
-          let promise = assert_promise_rejects_with_message(
-            error_characteristic.getDescriptors(),
-            new DOMException(
-              'GATT Server is disconnected. Cannot perform GATT operations. ' +
-              '(Re)connect first with `device.gatt.connect`.',
-              'NetworkError'));
-          gattServer.disconnect();
-          return promise;
-        });
-    });
-}, 'disconnect() called during a getDescriptors call that fails. ' +
-   'Reject with NetworkError.');
-
-</script>
diff --git a/third_party/WebKit/LayoutTests/bluetooth/characteristic/getDescriptors/gen-gatt-op-disconnect-called-during-success-with-uuid.html b/third_party/WebKit/LayoutTests/bluetooth/characteristic/getDescriptors/gen-gatt-op-disconnect-called-during-success-with-uuid.html
deleted file mode 100644
index fba944c..0000000
--- a/third_party/WebKit/LayoutTests/bluetooth/characteristic/getDescriptors/gen-gatt-op-disconnect-called-during-success-with-uuid.html
+++ /dev/null
@@ -1,32 +0,0 @@
-<!-- Generated by //third_party/WebKit/LayoutTests/bluetooth/generate.py -->
-<!DOCTYPE html>
-<script src="../../../resources/testharness.js"></script>
-<script src="../../../resources/testharnessreport.js"></script>
-<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
-<script>
-'use strict';
-promise_test(() => {
-  let val = new Uint8Array([1]);
-  return setBluetoothFakeAdapter('DisconnectingHealthThermometerAdapter')
-    .then(() => requestDeviceWithKeyDown({
-      filters: [{services: ['health_thermometer']}]}))
-    .then(device => device.gatt.connect())
-    .then(gattServer => {
-      return gattServer
-        .getPrimaryService('health_thermometer')
-        .then(service => service.getCharacteristic('measurement_interval'))
-        .then(measurement_interval => {
-          let promise = assert_promise_rejects_with_message(
-            measurement_interval.getDescriptors(user_description.name),
-            new DOMException(
-              'GATT Server is disconnected. Cannot perform GATT operations. ' +
-              '(Re)connect first with `device.gatt.connect`.',
-              'NetworkError'));
-          gattServer.disconnect();
-          return promise;
-        });
-    });
-}, 'disconnect() called during a getDescriptors call that succeeds. ' +
-   'Reject with NetworkError.');
-
-</script>
diff --git a/third_party/WebKit/LayoutTests/bluetooth/characteristic/getDescriptors/gen-gatt-op-disconnect-called-during-success.html b/third_party/WebKit/LayoutTests/bluetooth/characteristic/getDescriptors/gen-gatt-op-disconnect-called-during-success.html
deleted file mode 100644
index 3f50c9d..0000000
--- a/third_party/WebKit/LayoutTests/bluetooth/characteristic/getDescriptors/gen-gatt-op-disconnect-called-during-success.html
+++ /dev/null
@@ -1,32 +0,0 @@
-<!-- Generated by //third_party/WebKit/LayoutTests/bluetooth/generate.py -->
-<!DOCTYPE html>
-<script src="../../../resources/testharness.js"></script>
-<script src="../../../resources/testharnessreport.js"></script>
-<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
-<script>
-'use strict';
-promise_test(() => {
-  let val = new Uint8Array([1]);
-  return setBluetoothFakeAdapter('DisconnectingHealthThermometerAdapter')
-    .then(() => requestDeviceWithKeyDown({
-      filters: [{services: ['health_thermometer']}]}))
-    .then(device => device.gatt.connect())
-    .then(gattServer => {
-      return gattServer
-        .getPrimaryService('health_thermometer')
-        .then(service => service.getCharacteristic('measurement_interval'))
-        .then(measurement_interval => {
-          let promise = assert_promise_rejects_with_message(
-            measurement_interval.getDescriptors(),
-            new DOMException(
-              'GATT Server is disconnected. Cannot perform GATT operations. ' +
-              '(Re)connect first with `device.gatt.connect`.',
-              'NetworkError'));
-          gattServer.disconnect();
-          return promise;
-        });
-    });
-}, 'disconnect() called during a getDescriptors call that succeeds. ' +
-   'Reject with NetworkError.');
-
-</script>
diff --git a/third_party/WebKit/LayoutTests/bluetooth/script-tests/characteristic/descriptor-device-disconnects-before.js b/third_party/WebKit/LayoutTests/bluetooth/script-tests/characteristic/descriptor-device-disconnects-before.js
new file mode 100644
index 0000000..5aae9be
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/bluetooth/script-tests/characteristic/descriptor-device-disconnects-before.js
@@ -0,0 +1,28 @@
+'use strict';
+promise_test(() => {
+  let val = new Uint8Array([1]);
+  return setBluetoothFakeAdapter('DisconnectingHealthThermometerAdapter')
+      .then(() => requestDeviceWithKeyDown({
+              filters: [{services: ['health_thermometer']}],
+              optionalServices: [request_disconnection_service_uuid]
+            }))
+      .then(device => device.gatt.connect())
+      .then(gattServer => {
+        let measurement_interval;
+        return gattServer.getPrimaryService('health_thermometer')
+            .then(ht => ht.getCharacteristic('measurement_interval'))
+            .then(mi => measurement_interval = mi)
+            .then(() => get_request_disconnection(gattServer))
+            .then(requestDisconnection => requestDisconnection())
+            .then(
+                () => assert_promise_rejects_with_message(
+                    measurement_interval.CALLS(
+                        [getDescriptor(user_description.name) |
+                         getDescriptors(user_description.name)[UUID] |
+                         getDescriptors()]),
+                    new DOMException(
+                        'GATT Server is disconnected. Cannot retrieve descriptors. ' +
+                            '(Re)connect first with `device.gatt.connect`.',
+                        'NetworkError')));
+      });
+}, 'Device disconnects before FUNCTION_NAME. Reject with NetworkError.');
diff --git a/third_party/WebKit/LayoutTests/bluetooth/script-tests/characteristic/descriptor-device-disconnects-during-error.js b/third_party/WebKit/LayoutTests/bluetooth/script-tests/characteristic/descriptor-device-disconnects-during-error.js
new file mode 100644
index 0000000..8538ac93
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/bluetooth/script-tests/characteristic/descriptor-device-disconnects-during-error.js
@@ -0,0 +1,32 @@
+'use strict';
+promise_test(
+    () => {
+      let val = new Uint8Array([1]);
+      return setBluetoothFakeAdapter('FailingGATTOperationsAdapter')
+          .then(() => requestDeviceWithKeyDown({
+                  filters: [{services: [errorUUID(0xA0)]}],
+                  optionalServices: [request_disconnection_service_uuid]
+                }))
+          .then(device => device.gatt.connect())
+          .then(gattServer => {
+            let error_characteristic;
+            return gattServer.getPrimaryService(errorUUID(0xA0))
+                .then(es => es.getCharacteristic(errorUUID(0xA1)))
+                .then(ec => error_characteristic = ec)
+                .then(() => get_request_disconnection(gattServer))
+                .then(requestDisconnection => {
+                  requestDisconnection();
+                  return assert_promise_rejects_with_message(
+                      error_characteristic.CALLS(
+                          [getDescriptor(user_description.name) |
+                           getDescriptors(user_description.name)[UUID] |
+                           getDescriptors()]),
+                      new DOMException(
+                          'GATT Server is disconnected. Cannot retrieve descriptors. ' +
+                              '(Re)connect first with `device.gatt.connect`.',
+                          'NetworkError'));
+                });
+          });
+    },
+    'Device disconnects during a FUNCTION_NAME call that fails. ' +
+        'Reject with NetworkError.');
diff --git a/third_party/WebKit/LayoutTests/bluetooth/script-tests/characteristic/descriptor-device-disconnects-during-success.js b/third_party/WebKit/LayoutTests/bluetooth/script-tests/characteristic/descriptor-device-disconnects-during-success.js
new file mode 100644
index 0000000..65c85cd
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/bluetooth/script-tests/characteristic/descriptor-device-disconnects-during-success.js
@@ -0,0 +1,32 @@
+'use strict';
+promise_test(
+    () => {
+      let val = new Uint8Array([1]);
+      return setBluetoothFakeAdapter('DisconnectingHealthThermometerAdapter')
+          .then(() => requestDeviceWithKeyDown({
+                  filters: [{services: ['health_thermometer']}],
+                  optionalServices: [request_disconnection_service_uuid]
+                }))
+          .then(device => device.gatt.connect())
+          .then(gattServer => {
+            let measurement_interval;
+            return gattServer.getPrimaryService('health_thermometer')
+                .then(ht => ht.getCharacteristic('measurement_interval'))
+                .then(mi => measurement_interval = mi)
+                .then(() => get_request_disconnection(gattServer))
+                .then(requestDisconnection => {
+                  requestDisconnection();
+                  return assert_promise_rejects_with_message(
+                      measurement_interval.CALLS(
+                          [getDescriptor(user_description.name) |
+                           getDescriptors(user_description.name)[UUID] |
+                           getDescriptors()]),
+                      new DOMException(
+                          'GATT Server is disconnected. Cannot retrieve descriptors. ' +
+                              '(Re)connect first with `device.gatt.connect`.',
+                          'NetworkError'));
+                });
+          });
+    },
+    'Device disconnects during a FUNCTION_NAME call that succeeds. ' +
+        'Reject with NetworkError.');
diff --git a/third_party/WebKit/LayoutTests/bluetooth/script-tests/characteristic/descriptor-disconnect-called-before.js b/third_party/WebKit/LayoutTests/bluetooth/script-tests/characteristic/descriptor-disconnect-called-before.js
new file mode 100644
index 0000000..fce3bec
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/bluetooth/script-tests/characteristic/descriptor-disconnect-called-before.js
@@ -0,0 +1,25 @@
+'use strict';
+promise_test(() => {
+  let val = new Uint8Array([1]);
+  return setBluetoothFakeAdapter('DisconnectingHealthThermometerAdapter')
+      .then(
+          () => requestDeviceWithKeyDown(
+              {filters: [{services: ['health_thermometer']}]}))
+      .then(device => device.gatt.connect())
+      .then(gattServer => {
+        return gattServer.getPrimaryService('health_thermometer')
+            .then(service => service.getCharacteristic('measurement_interval'))
+            .then(measurement_interval => {
+              gattServer.disconnect();
+              return assert_promise_rejects_with_message(
+                  measurement_interval.CALLS(
+                      [getDescriptor(user_description.name) |
+                       getDescriptors(user_description.name)[UUID] |
+                       getDescriptors()]),
+                  new DOMException(
+                      'GATT Server is disconnected. Cannot retrieve descriptors. ' +
+                          '(Re)connect first with `device.gatt.connect`.',
+                      'NetworkError'));
+            });
+      });
+}, 'disconnect() called before FUNCTION_NAME. Reject with NetworkError.');
diff --git a/third_party/WebKit/LayoutTests/bluetooth/script-tests/characteristic/descriptor-disconnect-called-during-error.js b/third_party/WebKit/LayoutTests/bluetooth/script-tests/characteristic/descriptor-disconnect-called-during-error.js
new file mode 100644
index 0000000..13b4015
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/bluetooth/script-tests/characteristic/descriptor-disconnect-called-during-error.js
@@ -0,0 +1,29 @@
+'use strict';
+promise_test(
+    () => {
+      let val = new Uint8Array([1]);
+      return setBluetoothFakeAdapter('FailingGATTOperationsAdapter')
+          .then(
+              () => requestDeviceWithKeyDown(
+                  {filters: [{services: [errorUUID(0xA0)]}]}))
+          .then(device => device.gatt.connect())
+          .then(gattServer => {
+            return gattServer.getPrimaryService(errorUUID(0xA0))
+                .then(service => service.getCharacteristic(errorUUID(0xA1)))
+                .then(error_characteristic => {
+                  let promise = assert_promise_rejects_with_message(
+                      error_characteristic.CALLS(
+                          [getDescriptor(user_description.name) |
+                           getDescriptors(user_description.name)[UUID] |
+                           getDescriptors()]),
+                      new DOMException(
+                          'GATT Server is disconnected. Cannot retrieve descriptors. ' +
+                              '(Re)connect first with `device.gatt.connect`.',
+                          'NetworkError'));
+                  gattServer.disconnect();
+                  return promise;
+                });
+          });
+    },
+    'disconnect() called during a FUNCTION_NAME call that fails. ' +
+        'Reject with NetworkError.');
diff --git a/third_party/WebKit/LayoutTests/bluetooth/script-tests/characteristic/descriptor-disconnect-called-during-success.js b/third_party/WebKit/LayoutTests/bluetooth/script-tests/characteristic/descriptor-disconnect-called-during-success.js
new file mode 100644
index 0000000..3fa30d0
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/bluetooth/script-tests/characteristic/descriptor-disconnect-called-during-success.js
@@ -0,0 +1,31 @@
+'use strict';
+promise_test(
+    () => {
+      let val = new Uint8Array([1]);
+      return setBluetoothFakeAdapter('DisconnectingHealthThermometerAdapter')
+          .then(
+              () => requestDeviceWithKeyDown(
+                  {filters: [{services: ['health_thermometer']}]}))
+          .then(device => device.gatt.connect())
+          .then(gattServer => {
+            return gattServer.getPrimaryService('health_thermometer')
+                .then(
+                    service =>
+                        service.getCharacteristic('measurement_interval'))
+                .then(measurement_interval => {
+                  let promise = assert_promise_rejects_with_message(
+                      measurement_interval.CALLS(
+                          [getDescriptor(user_description.name) |
+                           getDescriptors(user_description.name)[UUID] |
+                           getDescriptors()]),
+                      new DOMException(
+                          'GATT Server is disconnected. Cannot retrieve descriptors. ' +
+                              '(Re)connect first with `device.gatt.connect`.',
+                          'NetworkError'));
+                  gattServer.disconnect();
+                  return promise;
+                });
+          });
+    },
+    'disconnect() called during a FUNCTION_NAME call that succeeds. ' +
+        'Reject with NetworkError.');
diff --git a/third_party/WebKit/LayoutTests/bluetooth/script-tests/characteristic/descriptor-garbage-collection-ran-during-error.js b/third_party/WebKit/LayoutTests/bluetooth/script-tests/characteristic/descriptor-garbage-collection-ran-during-error.js
index b7e0ccf..8b27f1b7 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/script-tests/characteristic/descriptor-garbage-collection-ran-during-error.js
+++ b/third_party/WebKit/LayoutTests/bluetooth/script-tests/characteristic/descriptor-garbage-collection-ran-during-error.js
@@ -2,27 +2,27 @@
 promise_test(() => {
   let promise;
   return setBluetoothFakeAdapter('DisconnectingHealthThermometerAdapter')
-    .then(() => requestDeviceWithKeyDown({
-      filters: [{services: ['health_thermometer']}]}))
-    .then(device => device.gatt.connect())
-    .then(gattServer => gattServer.getPrimaryService('health_thermometer'))
-    .then(service => service.getCharacteristic('measurement_interval'))
-    .then(characteristic => {
-      promise = assert_promise_rejects_with_message(
-        /* 0x0101 doesn't exist in this characteristic */
-        characteristic.CALLS([
-          getDescriptor(0x0101)|
-          getDescriptors()|
-          getDescriptors(0x0101)[UUID]]),
-        new DOMException(
-          'GATT Server is disconnected. Cannot perform GATT operations. ' +
-          '(Re)connect first with `device.gatt.connect`.',
-          'NetworkError'));
-      // Disconnect called to clear attributeInstanceMap and allow the
-      // object to get garbage collected.
-      characteristic.service.device.gatt.disconnect();
-    })
-    .then(runGarbageCollection)
-    .then(() => promise);
+      .then(
+          () => requestDeviceWithKeyDown(
+              {filters: [{services: ['health_thermometer']}]}))
+      .then(device => device.gatt.connect())
+      .then(gattServer => gattServer.getPrimaryService('health_thermometer'))
+      .then(service => service.getCharacteristic('measurement_interval'))
+      .then(characteristic => {
+        promise = assert_promise_rejects_with_message(
+            /* 0x0101 doesn't exist in this characteristic */
+            characteristic.CALLS(
+                [getDescriptor(0x0101) | getDescriptors() |
+                 getDescriptors(0x0101)[UUID]]),
+            new DOMException(
+                'GATT Server is disconnected. Cannot retrieve descriptors. ' +
+                    '(Re)connect first with `device.gatt.connect`.',
+                'NetworkError'));
+        // Disconnect called to clear attributeInstanceMap and allow the
+        // object to get garbage collected.
+        characteristic.service.device.gatt.disconnect();
+      })
+      .then(runGarbageCollection)
+      .then(() => promise);
 }, 'Garbage Collection ran during FUNCTION_NAME call that fails. ' +
    'Should not crash');
diff --git a/third_party/WebKit/LayoutTests/bluetooth/script-tests/characteristic/descriptor-garbage-collection-ran-during-success.js b/third_party/WebKit/LayoutTests/bluetooth/script-tests/characteristic/descriptor-garbage-collection-ran-during-success.js
index f72df0a..f81df1a 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/script-tests/characteristic/descriptor-garbage-collection-ran-during-success.js
+++ b/third_party/WebKit/LayoutTests/bluetooth/script-tests/characteristic/descriptor-garbage-collection-ran-during-success.js
@@ -2,26 +2,26 @@
 promise_test(() => {
   let promise;
   return setBluetoothFakeAdapter('DisconnectingHealthThermometerAdapter')
-    .then(() => requestDeviceWithKeyDown({
-      filters: [{services: ['health_thermometer']}]}))
-    .then(device => device.gatt.connect())
-    .then(gattServer => gattServer.getPrimaryService('health_thermometer'))
-    .then(service => service.getCharacteristic('measurement_interval'))
-    .then(characteristic => {
-      promise = assert_promise_rejects_with_message(
-        characteristic.CALLS([
-          getDescriptor(user_description.name)|
-          getDescriptors()|
-          getDescriptors(user_description.name)[UUID]]),
-        new DOMException(
-          'GATT Server is disconnected. Cannot perform GATT operations. ' +
-          '(Re)connect first with `device.gatt.connect`.',
-          'NetworkError'));
-      // Disconnect called to clear attributeInstanceMap and allow the
-      // object to get garbage collected.
-      characteristic.service.device.gatt.disconnect();
-    })
-    .then(runGarbageCollection)
-    .then(() => promise);
+      .then(
+          () => requestDeviceWithKeyDown(
+              {filters: [{services: ['health_thermometer']}]}))
+      .then(device => device.gatt.connect())
+      .then(gattServer => gattServer.getPrimaryService('health_thermometer'))
+      .then(service => service.getCharacteristic('measurement_interval'))
+      .then(characteristic => {
+        promise = assert_promise_rejects_with_message(
+            characteristic.CALLS(
+                [getDescriptor(user_description.name) | getDescriptors() |
+                 getDescriptors(user_description.name)[UUID]]),
+            new DOMException(
+                'GATT Server is disconnected. Cannot retrieve descriptors. ' +
+                    '(Re)connect first with `device.gatt.connect`.',
+                'NetworkError'));
+        // Disconnect called to clear attributeInstanceMap and allow the
+        // object to get garbage collected.
+        characteristic.service.device.gatt.disconnect();
+      })
+      .then(runGarbageCollection)
+      .then(() => promise);
 }, 'Garbage Collection ran during a FUNCTION_NAME call that succeeds. ' +
    'Should not crash.');
diff --git a/third_party/WebKit/LayoutTests/bluetooth/script-tests/characteristic/gatt-op-device-disconnects-before.js b/third_party/WebKit/LayoutTests/bluetooth/script-tests/characteristic/gatt-op-device-disconnects-before.js
index 30278d2..29f1689f9 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/script-tests/characteristic/gatt-op-device-disconnects-before.js
+++ b/third_party/WebKit/LayoutTests/bluetooth/script-tests/characteristic/gatt-op-device-disconnects-before.js
@@ -16,9 +16,6 @@
         .then(requestDisconnection => requestDisconnection())
         .then(() => assert_promise_rejects_with_message(
           measurement_interval.CALLS([
-            getDescriptor(user_description.name)|
-            getDescriptors(user_description.name)[UUID]|
-            getDescriptors()|
             readValue()|
             writeValue(val)|
             startNotifications()|
diff --git a/third_party/WebKit/LayoutTests/bluetooth/script-tests/characteristic/gatt-op-device-disconnects-during-error.js b/third_party/WebKit/LayoutTests/bluetooth/script-tests/characteristic/gatt-op-device-disconnects-during-error.js
index 77a0260..019373f 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/script-tests/characteristic/gatt-op-device-disconnects-during-error.js
+++ b/third_party/WebKit/LayoutTests/bluetooth/script-tests/characteristic/gatt-op-device-disconnects-during-error.js
@@ -18,9 +18,6 @@
           requestDisconnection();
           return assert_promise_rejects_with_message(
             error_characteristic.CALLS([
-              getDescriptor(user_description.name)|
-              getDescriptors(user_description.name)[UUID]|
-              getDescriptors()|
               readValue()|
               writeValue(val)|
               startNotifications()]),
diff --git a/third_party/WebKit/LayoutTests/bluetooth/script-tests/characteristic/gatt-op-device-disconnects-during-success.js b/third_party/WebKit/LayoutTests/bluetooth/script-tests/characteristic/gatt-op-device-disconnects-during-success.js
index af43404..026f2a7b9 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/script-tests/characteristic/gatt-op-device-disconnects-during-success.js
+++ b/third_party/WebKit/LayoutTests/bluetooth/script-tests/characteristic/gatt-op-device-disconnects-during-success.js
@@ -18,9 +18,6 @@
           requestDisconnection();
           return assert_promise_rejects_with_message(
             measurement_interval.CALLS([
-              getDescriptor(user_description.name)|
-              getDescriptors(user_description.name)[UUID]|
-              getDescriptors()|
               readValue()|
               writeValue(val)|
               startNotifications()|
diff --git a/third_party/WebKit/LayoutTests/bluetooth/script-tests/characteristic/gatt-op-disconnect-called-before.js b/third_party/WebKit/LayoutTests/bluetooth/script-tests/characteristic/gatt-op-disconnect-called-before.js
index c1b39a1..6b347c2 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/script-tests/characteristic/gatt-op-disconnect-called-before.js
+++ b/third_party/WebKit/LayoutTests/bluetooth/script-tests/characteristic/gatt-op-disconnect-called-before.js
@@ -12,9 +12,6 @@
           gattServer.disconnect();
           return assert_promise_rejects_with_message(
             measurement_interval.CALLS([
-              getDescriptor(user_description.name)|
-              getDescriptors(user_description.name)[UUID]|
-              getDescriptors()|
               readValue()|
               writeValue(val)|
               startNotifications()|
diff --git a/third_party/WebKit/LayoutTests/bluetooth/script-tests/characteristic/gatt-op-disconnect-called-during-error.js b/third_party/WebKit/LayoutTests/bluetooth/script-tests/characteristic/gatt-op-disconnect-called-during-error.js
index 261021f..0e4428a7 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/script-tests/characteristic/gatt-op-disconnect-called-during-error.js
+++ b/third_party/WebKit/LayoutTests/bluetooth/script-tests/characteristic/gatt-op-disconnect-called-during-error.js
@@ -11,9 +11,6 @@
         .then(error_characteristic => {
           let promise = assert_promise_rejects_with_message(
             error_characteristic.CALLS([
-              getDescriptor(user_description.name)|
-              getDescriptors(user_description.name)[UUID]|
-              getDescriptors()|
               readValue()|
               writeValue(val)|
               startNotifications()]),
diff --git a/third_party/WebKit/LayoutTests/bluetooth/script-tests/characteristic/gatt-op-disconnect-called-during-success.js b/third_party/WebKit/LayoutTests/bluetooth/script-tests/characteristic/gatt-op-disconnect-called-during-success.js
index 620e19d..ac26b9ef 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/script-tests/characteristic/gatt-op-disconnect-called-during-success.js
+++ b/third_party/WebKit/LayoutTests/bluetooth/script-tests/characteristic/gatt-op-disconnect-called-during-success.js
@@ -12,9 +12,6 @@
         .then(measurement_interval => {
           let promise = assert_promise_rejects_with_message(
             measurement_interval.CALLS([
-              getDescriptor(user_description.name)|
-              getDescriptors(user_description.name)[UUID]|
-              getDescriptors()|
               readValue()|
               writeValue(val)|
               startNotifications()|
diff --git a/third_party/WebKit/LayoutTests/http/tests/inspector/bindings/bindings-main-frame-navigated-expected.txt b/third_party/WebKit/LayoutTests/http/tests/inspector/bindings/bindings-main-frame-navigated-expected.txt
index 0dc62c4..2582a0c 100644
--- a/third_party/WebKit/LayoutTests/http/tests/inspector/bindings/bindings-main-frame-navigated-expected.txt
+++ b/third_party/WebKit/LayoutTests/http/tests/inspector/bindings/bindings-main-frame-navigated-expected.txt
@@ -33,7 +33,7 @@
 Page reloaded.
 Workspace: 2 uiSourceCodes.
     debugger:///VM[XXX] empty-page.html
-    http://localhost:8000/inspector/bindings/resources/empty-page.html
+    http://127.0.0.1:8000/inspector/bindings/resources/empty-page.html
 
 Running: navigateBack
 Page reloaded.
diff --git a/third_party/WebKit/LayoutTests/http/tests/inspector/bindings/bindings-main-frame-navigated.html b/third_party/WebKit/LayoutTests/http/tests/inspector/bindings/bindings-main-frame-navigated.html
index a23e20d..0db6ef4 100644
--- a/third_party/WebKit/LayoutTests/http/tests/inspector/bindings/bindings-main-frame-navigated.html
+++ b/third_party/WebKit/LayoutTests/http/tests/inspector/bindings/bindings-main-frame-navigated.html
@@ -19,12 +19,12 @@
     InspectorTest.dumpWorkspace();
 
     InspectorTest.markStep('navigateMainFrame');
-    var url = "http://localhost:8000/inspector/bindings/resources/empty-page.html";
+    var url = "http://127.0.0.1:8000/inspector/bindings/resources/empty-page.html";
     await InspectorTest.navigatePromise(url);
     InspectorTest.dumpWorkspace();
 
     InspectorTest.markStep('navigateBack');
-    var originalUrl = "http://localhost:8000/inspector/bindings/bindings-main-frame-navigated.html";
+    var originalUrl = "http://127.0.0.1:8000/inspector/bindings/bindings-main-frame-navigated.html";
     await InspectorTest.navigatePromise(originalUrl);
 
     InspectorTest.completeTest();
diff --git a/third_party/WebKit/LayoutTests/http/tests/inspector/bindings/navigator-main-frame-navigated-expected.txt b/third_party/WebKit/LayoutTests/http/tests/inspector/bindings/navigator-main-frame-navigated-expected.txt
index 6f9d09b5..e1b2a5e 100644
--- a/third_party/WebKit/LayoutTests/http/tests/inspector/bindings/navigator-main-frame-navigated-expected.txt
+++ b/third_party/WebKit/LayoutTests/http/tests/inspector/bindings/navigator-main-frame-navigated-expected.txt
@@ -30,7 +30,7 @@
 Running: navigateMainFrame
 Page reloaded.
 top
-  localhost:8000
+  127.0.0.1:8000
     inspector/bindings/resources
       empty-page.html
 
diff --git a/third_party/WebKit/LayoutTests/http/tests/inspector/bindings/navigator-main-frame-navigated.html b/third_party/WebKit/LayoutTests/http/tests/inspector/bindings/navigator-main-frame-navigated.html
index 95a879c..ed72ab0 100644
--- a/third_party/WebKit/LayoutTests/http/tests/inspector/bindings/navigator-main-frame-navigated.html
+++ b/third_party/WebKit/LayoutTests/http/tests/inspector/bindings/navigator-main-frame-navigated.html
@@ -22,12 +22,12 @@
     InspectorTest.dumpNavigatorView(sourcesNavigator, false);
 
     InspectorTest.markStep('navigateMainFrame');
-    var url = "http://localhost:8000/inspector/bindings/resources/empty-page.html";
+    var url = "http://127.0.0.1:8000/inspector/bindings/resources/empty-page.html";
     await InspectorTest.navigatePromise(url);
     InspectorTest.dumpNavigatorView(sourcesNavigator, false);
 
     InspectorTest.markStep('navigateBack');
-    var originalUrl = "http://localhost:8000/inspector/bindings/navigator-main-frame-navigated.html";
+    var originalUrl = "http://127.0.0.1:8000/inspector/bindings/navigator-main-frame-navigated.html";
     await InspectorTest.navigatePromise(originalUrl);
     InspectorTest.completeTest();
 }
diff --git a/third_party/WebKit/LayoutTests/http/tests/media/controls/controls-list-add-hide.html b/third_party/WebKit/LayoutTests/http/tests/media/controls/controls-list-add-hide.html
new file mode 100644
index 0000000..ff3ec55
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/http/tests/media/controls/controls-list-add-hide.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<title>Test adding keywords to controlsList hides buttons</title>
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
+<script src="../../media-resources/media-file.js"></script>
+<script src="../../media-resources/media-controls.js"></script>
+<video controls id="enabled-controls" width="500px"></video>
+<script>
+async_test(t => {
+  var v = document.getElementById('enabled-controls');
+
+  v.addEventListener('canplaythrough', t.step_func(e => {
+    assert_not_equals(getComputedStyle(fullscreenButton(v)).display, 'none');
+    assert_not_equals(getComputedStyle(downloadButton(v)).display, 'none');
+
+    v.controlsList.add('nodownload');
+
+    testRunner.layoutAndPaintAsyncThen(t.step_func(() => {
+      assert_not_equals(getComputedStyle(fullscreenButton(v)).display, 'none');
+      assert_equals(getComputedStyle(downloadButton(v)).display, 'none');
+      v.controlsList.add('nofullscreen');
+
+      testRunner.layoutAndPaintAsyncThen(t.step_func_done(() => {
+        assert_equals(getComputedStyle(fullscreenButton(v)).display, 'none');
+        assert_equals(getComputedStyle(downloadButton(v)).display, 'none');
+      }));
+    }));
+  }));
+
+  v.src = findMediaFile('video', '../resources/test');
+}, 'Test disabling controls on the video element with all controls.');
+</script>
diff --git a/third_party/WebKit/LayoutTests/http/tests/media/controls/controls-list-remove-show.html b/third_party/WebKit/LayoutTests/http/tests/media/controls/controls-list-remove-show.html
new file mode 100644
index 0000000..2244b722
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/http/tests/media/controls/controls-list-remove-show.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<title>Test removing keywords from controlsList shows buttons</title>
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
+<script src="../../media-resources/media-file.js"></script>
+<script src="../../media-resources/media-controls.js"></script>
+<video controlslist="nodownload nofullscreen" id="disabled-controls" width="500px"></video>
+<script>
+async_test(t => {
+  var v = document.getElementById('disabled-controls');
+
+  v.addEventListener('canplaythrough', t.step_func(e => {
+    assert_equals(getComputedStyle(fullscreenButton(v)).display, 'none');
+    assert_equals(getComputedStyle(downloadButton(v)).display, 'none');
+
+    v.controlsList.remove('nodownload');
+
+    testRunner.layoutAndPaintAsyncThen(t.step_func(() => {
+      assert_equals(getComputedStyle(fullscreenButton(v)).display, 'none');
+      assert_not_equals(getComputedStyle(downloadButton(v)).display, 'none');
+
+      v.controlsList.remove('nofullscreen');
+
+      testRunner.layoutAndPaintAsyncThen(t.step_func_done(() => {
+        assert_not_equals(getComputedStyle(fullscreenButton(v)).display, 'none');
+        assert_not_equals(getComputedStyle(downloadButton(v)).display, 'none');
+      }));
+    }));
+  }));
+
+  v.src = findMediaFile('video', '../resources/test');
+}, 'Test enabling controls on the video element with them enabled.');
+</script>
+
diff --git a/third_party/WebKit/LayoutTests/media/media-controls.js b/third_party/WebKit/LayoutTests/media/media-controls.js
index 77eae33..3fa54e6 100644
--- a/third_party/WebKit/LayoutTests/media/media-controls.js
+++ b/third_party/WebKit/LayoutTests/media/media-controls.js
@@ -17,6 +17,22 @@
     return button;
 }
 
+function downloadButton(videoElement) {
+    var controlID = '-internal-media-controls-download-button';
+    var button = mediaControlsElement(window.internals.shadowRoot(videoElement).firstChild, controlID);
+    if (!button)
+        throw 'Failed to find download button';
+    return button;
+}
+
+function fullscreenButton(videoElement) {
+    var controlID = '-webkit-media-controls-fullscreen-button';
+    var button = mediaControlsElement(window.internals.shadowRoot(videoElement).firstChild, controlID);
+    if (!button)
+        throw 'Failed to find fullscreen button';
+    return button;
+}
+
 function overlayCastButton(videoElement)
 {
     var controlID = '-internal-media-controls-overlay-cast-button';
diff --git a/third_party/WebKit/LayoutTests/mojo/binding.html b/third_party/WebKit/LayoutTests/mojo/binding.html
new file mode 100644
index 0000000..f04de81e
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/mojo/binding.html
@@ -0,0 +1,122 @@
+<!DOCTYPE html>
+<script src="../resources/testharness.js"></script>
+<script src="../resources/testharnessreport.js"></script>
+<script src="../resources/mojo-helpers.js"></script>
+<script>
+'use strict';
+
+function CalculatorImpl() {
+  this.total = 0;
+}
+
+CalculatorImpl.prototype.clear = function() {
+  this.total = 0;
+  return Promise.resolve({value: this.total});
+};
+
+CalculatorImpl.prototype.add = function(value) {
+  this.total += value;
+  return Promise.resolve({value: this.total});
+};
+
+CalculatorImpl.prototype.multiply = function(value) {
+  this.total *= value;
+  return Promise.resolve({value: this.total});
+};
+
+function loadModules(name, func) {
+  return define(
+      name,
+      [
+        'mojo/public/js/bindings',
+        'mojo/public/interfaces/bindings/tests/math_calculator.mojom'
+      ],
+      func);
+};
+
+function binding_test(func, name, properties) {
+  promise_test(() => {
+    return loadModules(name, func);
+  }, name, properties);
+}
+
+binding_test((bindings, math) => {
+  var binding = new bindings.Binding(math.Calculator, new CalculatorImpl());
+  assert_false(binding.isBound());
+
+  var calc = new math.CalculatorPtr();
+  var request = bindings.makeRequest(calc);
+  binding.bind(request);
+  assert_true(binding.isBound());
+
+  binding.close();
+  assert_false(binding.isBound());
+}, 'is bound');
+
+binding_test(async (bindings, math) => {
+  var calc1 = new math.CalculatorPtr();
+  var calcBinding = new bindings.Binding(math.Calculator,
+                                         new CalculatorImpl(),
+                                         bindings.makeRequest(calc1));
+  assert_equals((await calc1.add(2)).value, 2);
+
+  var calc2 = new math.CalculatorPtr();
+  calcBinding.bind(bindings.makeRequest(calc2));
+  assert_equals((await calc2.add(2)).value, 4);
+}, 'reusable');
+
+binding_test(async (bindings, math) => {
+  var calc = new math.CalculatorPtr();
+  var calcBinding = new bindings.Binding(math.Calculator,
+                                         new CalculatorImpl(),
+                                         bindings.makeRequest(calc));
+
+  await new Promise((resolve, reject) => {
+    calcBinding.setConnectionErrorHandler(() => { resolve(); });
+    calc.ptr.reset();
+  });
+}, 'connection error');
+
+binding_test(async (bindings, math) => {
+  var calc = new math.CalculatorPtr();
+  var calcBinding = new bindings.Binding(math.Calculator,
+                                         new CalculatorImpl(),
+                                         bindings.makeRequest(calc));
+  assert_equals((await calc.add(2)).value, 2);
+
+  var interfaceRequest = calcBinding.unbind();
+  assert_false(calcBinding.isBound());
+
+  var newCalcBinding = new bindings.Binding(math.Calculator,
+                                            new CalculatorImpl(),
+                                            interfaceRequest);
+  assert_equals((await calc.add(2)).value, 2);
+}, 'unbind');
+
+binding_test(async (bindings, math) => {
+  var calc1 = new math.CalculatorPtr();
+  var calc2 = new math.CalculatorPtr();
+  var calcImpl = new CalculatorImpl();
+
+  var bindingSet = new bindings.BindingSet(math.Calculator);
+  assert_true(bindingSet.isEmpty());
+
+  bindingSet.addBinding(calcImpl, bindings.makeRequest(calc1));
+  bindingSet.addBinding(calcImpl, bindings.makeRequest(calc2));
+  assert_false(bindingSet.isEmpty());
+
+  assert_equals((await calc1.add(3)).value, 3);
+  assert_equals((await calc2.add(4)).value, 7);
+
+  await new Promise((resolve, reject) => {
+    bindingSet.setConnectionErrorHandler(() => { resolve(); });
+    calc1.ptr.reset();
+  });
+
+  assert_equals((await calc2.add(5)).value, 12);
+
+  bindingSet.closeAllBindings();
+  assert_true(bindingSet.isEmpty());
+}, 'binding set');
+
+</script>
diff --git a/third_party/WebKit/LayoutTests/platform/mac/virtual/stable/webexposed/global-interface-listing-expected.txt b/third_party/WebKit/LayoutTests/platform/mac/virtual/stable/webexposed/global-interface-listing-expected.txt
index 19191793..0570525f 100644
--- a/third_party/WebKit/LayoutTests/platform/mac/virtual/stable/webexposed/global-interface-listing-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/mac/virtual/stable/webexposed/global-interface-listing-expected.txt
@@ -2339,6 +2339,7 @@
     getter autoplay
     getter buffered
     getter controls
+    getter controlsList
     getter crossOrigin
     getter currentSrc
     getter currentTime
@@ -2379,6 +2380,7 @@
     method setSinkId
     setter autoplay
     setter controls
+    setter controlsList
     setter crossOrigin
     setter currentTime
     setter defaultMuted
diff --git a/third_party/WebKit/LayoutTests/platform/win/virtual/stable/webexposed/global-interface-listing-expected.txt b/third_party/WebKit/LayoutTests/platform/win/virtual/stable/webexposed/global-interface-listing-expected.txt
index e018d60..c3d52c3 100644
--- a/third_party/WebKit/LayoutTests/platform/win/virtual/stable/webexposed/global-interface-listing-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/win/virtual/stable/webexposed/global-interface-listing-expected.txt
@@ -2268,6 +2268,7 @@
     getter autoplay
     getter buffered
     getter controls
+    getter controlsList
     getter crossOrigin
     getter currentSrc
     getter currentTime
@@ -2308,6 +2309,7 @@
     method setSinkId
     setter autoplay
     setter controls
+    setter controlsList
     setter crossOrigin
     setter currentTime
     setter defaultMuted
diff --git a/third_party/WebKit/LayoutTests/virtual/stable/webexposed/element-instance-property-listing-expected.txt b/third_party/WebKit/LayoutTests/virtual/stable/webexposed/element-instance-property-listing-expected.txt
index e6ac08f2..3a7637bf 100644
--- a/third_party/WebKit/LayoutTests/virtual/stable/webexposed/element-instance-property-listing-expected.txt
+++ b/third_party/WebKit/LayoutTests/virtual/stable/webexposed/element-instance-property-listing-expected.txt
@@ -304,6 +304,7 @@
     property buffered
     property canPlayType
     property controls
+    property controlsList
     property crossOrigin
     property currentSrc
     property currentTime
@@ -1019,6 +1020,7 @@
     property buffered
     property canPlayType
     property controls
+    property controlsList
     property crossOrigin
     property currentSrc
     property currentTime
diff --git a/third_party/WebKit/LayoutTests/webexposed/element-instance-property-listing-expected.txt b/third_party/WebKit/LayoutTests/webexposed/element-instance-property-listing-expected.txt
index 25eb37d..639df5f 100644
--- a/third_party/WebKit/LayoutTests/webexposed/element-instance-property-listing-expected.txt
+++ b/third_party/WebKit/LayoutTests/webexposed/element-instance-property-listing-expected.txt
@@ -316,6 +316,7 @@
     property canPlayType
     property captureStream
     property controls
+    property controlsList
     property crossOrigin
     property currentSrc
     property currentTime
@@ -1049,6 +1050,7 @@
     property canPlayType
     property captureStream
     property controls
+    property controlsList
     property crossOrigin
     property currentSrc
     property currentTime
diff --git a/third_party/WebKit/LayoutTests/webexposed/global-interface-listing-expected.txt b/third_party/WebKit/LayoutTests/webexposed/global-interface-listing-expected.txt
index e5c4deb9..9b23b1a 100644
--- a/third_party/WebKit/LayoutTests/webexposed/global-interface-listing-expected.txt
+++ b/third_party/WebKit/LayoutTests/webexposed/global-interface-listing-expected.txt
@@ -2884,6 +2884,7 @@
     getter autoplay
     getter buffered
     getter controls
+    getter controlsList
     getter crossOrigin
     getter currentSrc
     getter currentTime
@@ -2926,6 +2927,7 @@
     method setSinkId
     setter autoplay
     setter controls
+    setter controlsList
     setter crossOrigin
     setter currentTime
     setter defaultMuted
diff --git a/third_party/WebKit/Source/bindings/core/v8/serialization/V8ScriptValueDeserializer.cpp b/third_party/WebKit/Source/bindings/core/v8/serialization/V8ScriptValueDeserializer.cpp
index 516ec90..1d9fc82d 100644
--- a/third_party/WebKit/Source/bindings/core/v8/serialization/V8ScriptValueDeserializer.cpp
+++ b/third_party/WebKit/Source/bindings/core/v8/serialization/V8ScriptValueDeserializer.cpp
@@ -208,8 +208,8 @@
       const uint32_t validPropertiesMask = static_cast<uint32_t>(
           (1u << CompositorMutableProperty::kNumProperties) - 1);
       if (!RuntimeEnabledFeatures::compositorWorkerEnabled() ||
-          !readUint64(&element) || !readUint32(&properties) || !properties ||
-          (properties & ~validPropertiesMask))
+          !readUint64(&element) || !readUint32(&properties) || element == 0 ||
+          !properties || (properties & ~validPropertiesMask))
         return nullptr;
       return CompositorProxy::create(m_scriptState->getExecutionContext(),
                                      element, properties);
diff --git a/third_party/WebKit/Source/bindings/core/v8/serialization/V8ScriptValueSerializerTest.cpp b/third_party/WebKit/Source/bindings/core/v8/serialization/V8ScriptValueSerializerTest.cpp
index 3a65dea..40d3e36 100644
--- a/third_party/WebKit/Source/bindings/core/v8/serialization/V8ScriptValueSerializerTest.cpp
+++ b/third_party/WebKit/Source/bindings/core/v8/serialization/V8ScriptValueSerializerTest.cpp
@@ -1034,6 +1034,22 @@
             newProxy->compositorMutableProperties());
 }
 
+TEST(V8ScriptValueSerializerTest, CompositorProxyBadElementDeserialization) {
+  ScopedEnableCompositorWorker enableCompositorWorker;
+  V8TestingScope scope;
+
+  // Normally a CompositorProxy will not be constructed with an element id of 0,
+  // but we force it here to test the deserialization code.
+  uint8_t elementId = 0;
+  uint8_t mutableProperties = CompositorMutableProperty::kOpacity;
+  RefPtr<SerializedScriptValue> input = serializedValue(
+      {0xff, 0x09, 0x3f, 0x00, 0x43, elementId, mutableProperties, 0x00});
+  v8::Local<v8::Value> result =
+      V8ScriptValueDeserializer(scope.getScriptState(), input).deserialize();
+
+  EXPECT_TRUE(result->IsNull());
+}
+
 // Decode tests aren't included here because they're slightly non-trivial (an
 // element with the right ID must actually exist) and this feature is both
 // unshipped and likely to not use this mechanism when it does.
diff --git a/third_party/WebKit/Source/core/BUILD.gn b/third_party/WebKit/Source/core/BUILD.gn
index 7c77693..d0d87348 100644
--- a/third_party/WebKit/Source/core/BUILD.gn
+++ b/third_party/WebKit/Source/core/BUILD.gn
@@ -535,6 +535,7 @@
     "$blink_core_output_dir/css/properties/CSSPropertyAPIWebkitBoxFlex.h",
     "$blink_core_output_dir/css/properties/CSSPropertyAPIGridAutoLine.h",
     "$blink_core_output_dir/css/properties/CSSPropertyAPIOffsetAnchor.h",
+    "$blink_core_output_dir/css/properties/CSSPropertyAPIFontSize.h",
   ]
 }
 
diff --git a/third_party/WebKit/Source/core/css/BUILD.gn b/third_party/WebKit/Source/core/css/BUILD.gn
index 985612e1..a339e23 100644
--- a/third_party/WebKit/Source/core/css/BUILD.gn
+++ b/third_party/WebKit/Source/core/css/BUILD.gn
@@ -373,6 +373,7 @@
     "properties/CSSPropertyAPIFilter.cpp",
     "properties/CSSPropertyAPIFlexBasis.cpp",
     "properties/CSSPropertyAPIFlexGrowOrShrink.cpp",
+    "properties/CSSPropertyAPIFontSize.cpp",
     "properties/CSSPropertyAPIFontSizeAdjust.cpp",
     "properties/CSSPropertyAPIFontVariantCaps.cpp",
     "properties/CSSPropertyAPIFontVariantLigatures.cpp",
diff --git a/third_party/WebKit/Source/core/css/CSSProperties.json5 b/third_party/WebKit/Source/core/css/CSSProperties.json5
index f8f1398f..9e50dfea 100644
--- a/third_party/WebKit/Source/core/css/CSSProperties.json5
+++ b/third_party/WebKit/Source/core/css/CSSProperties.json5
@@ -353,6 +353,8 @@
     },
     {
       name: "font-size",
+      api_class: true,
+      api_methods: ["parseSingleValue"],
       converter: "convertFontSize",
       font: true,
       getter: "getSize",
diff --git a/third_party/WebKit/Source/core/css/parser/CSSPropertyParser.cpp b/third_party/WebKit/Source/core/css/parser/CSSPropertyParser.cpp
index fece72e..5356ba49 100644
--- a/third_party/WebKit/Source/core/css/parser/CSSPropertyParser.cpp
+++ b/third_party/WebKit/Source/core/css/parser/CSSPropertyParser.cpp
@@ -1912,9 +1912,6 @@
       return CSSPropertyFontUtils::consumeFontFamily(m_range);
     case CSSPropertyFontWeight:
       return CSSPropertyFontUtils::consumeFontWeight(m_range);
-    case CSSPropertyFontSize:
-      return CSSPropertyFontUtils::consumeFontSize(m_range, m_context->mode(),
-                                                   UnitlessQuirk::Allow);
     case CSSPropertyLineHeight:
       return CSSPropertyFontUtils::consumeLineHeight(m_range,
                                                      m_context->mode());
diff --git a/third_party/WebKit/Source/core/css/properties/CSSPropertyAPIFontSize.cpp b/third_party/WebKit/Source/core/css/properties/CSSPropertyAPIFontSize.cpp
new file mode 100644
index 0000000..ae0f48aa
--- /dev/null
+++ b/third_party/WebKit/Source/core/css/properties/CSSPropertyAPIFontSize.cpp
@@ -0,0 +1,20 @@
+// 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 "core/css/properties/CSSPropertyAPIFontSize.h"
+
+#include "core/css/parser/CSSParserContext.h"
+#include "core/css/parser/CSSPropertyParserHelpers.h"
+#include "core/css/properties/CSSPropertyFontUtils.h"
+
+namespace blink {
+
+const CSSValue* CSSPropertyAPIFontSize::parseSingleValue(
+    CSSParserTokenRange& range,
+    const CSSParserContext* context) {
+  return CSSPropertyFontUtils::consumeFontSize(
+      range, context->mode(), CSSPropertyParserHelpers::UnitlessQuirk::Allow);
+}
+
+}  // namespace blink
diff --git a/third_party/WebKit/Source/core/frame/UseCounter.h b/third_party/WebKit/Source/core/frame/UseCounter.h
index 7f4d5aa3..2648c73 100644
--- a/third_party/WebKit/Source/core/frame/UseCounter.h
+++ b/third_party/WebKit/Source/core/frame/UseCounter.h
@@ -1479,6 +1479,10 @@
     GetCanvas2DContextAttributes = 1850,
     V8HTMLInputElement_Capture_AttributeGetter = 1851,
     V8HTMLInputElement_Capture_AttributeSetter = 1852,
+    HTMLMediaElementControlsListAttribute = 1853,
+    HTMLMediaElementControlsListNoDownload = 1854,
+    HTMLMediaElementControlsListNoFullscreen = 1855,
+    HTMLMediaElementControlsListNoRemotePlayback = 1856,
 
     // Add new features immediately above this line. Don't change assigned
     // numbers of any item, and don't reuse removed slots.
diff --git a/third_party/WebKit/Source/core/html/BUILD.gn b/third_party/WebKit/Source/core/html/BUILD.gn
index 4ab90bb..28b883f 100644
--- a/third_party/WebKit/Source/core/html/BUILD.gn
+++ b/third_party/WebKit/Source/core/html/BUILD.gn
@@ -111,6 +111,8 @@
     "HTMLMarqueeElement.cpp",
     "HTMLMarqueeElement.h",
     "HTMLMediaElement.cpp",
+    "HTMLMediaElementControlsList.cpp",
+    "HTMLMediaElementControlsList.h",
     "HTMLMediaSource.cpp",
     "HTMLMediaSource.h",
     "HTMLMenuElement.cpp",
diff --git a/third_party/WebKit/Source/core/html/HTMLAttributeNames.json5 b/third_party/WebKit/Source/core/html/HTMLAttributeNames.json5
index 29ee8464..81c2d4d 100644
--- a/third_party/WebKit/Source/core/html/HTMLAttributeNames.json5
+++ b/third_party/WebKit/Source/core/html/HTMLAttributeNames.json5
@@ -106,6 +106,7 @@
     "contenteditable",
     "contextmenu",
     "controls",
+    "controlslist",
     "coords",
     "crossorigin",
     "csp",
diff --git a/third_party/WebKit/Source/core/html/HTMLMediaElement.cpp b/third_party/WebKit/Source/core/html/HTMLMediaElement.cpp
index d5100e4e..12bdeea 100644
--- a/third_party/WebKit/Source/core/html/HTMLMediaElement.cpp
+++ b/third_party/WebKit/Source/core/html/HTMLMediaElement.cpp
@@ -464,7 +464,8 @@
       m_autoplayUmaHelper(AutoplayUmaHelper::create(this)),
       m_remotePlaybackClient(nullptr),
       m_autoplayVisibilityObserver(nullptr),
-      m_mediaControls(nullptr) {
+      m_mediaControls(nullptr),
+      m_controlsList(HTMLMediaElementControlsList::create(this)) {
   BLINK_MEDIA_LOG << "HTMLMediaElement(" << (void*)this << ")";
 
   m_lockedPendingUserGesture = computeLockedPendingUserGesture(document);
@@ -598,6 +599,14 @@
     UseCounter::count(document(),
                       UseCounter::HTMLMediaElementControlsAttribute);
     updateControlsVisibility();
+  } else if (name == controlslistAttr) {
+    UseCounter::count(document(),
+                      UseCounter::HTMLMediaElementControlsListAttribute);
+    if (params.oldValue != params.newValue) {
+      m_controlsList->setValue(params.newValue);
+      if (mediaControls())
+        mediaControls()->onControlsListUpdated();
+    }
   } else if (name == preloadAttr) {
     setPlayerPreload();
   } else if (name == disableremoteplaybackAttr) {
@@ -2437,6 +2446,19 @@
   return false;
 }
 
+HTMLMediaElementControlsList* HTMLMediaElement::controlsList() const {
+  return m_controlsList.get();
+}
+
+void HTMLMediaElement::controlsListValueWasSet() {
+  if (fastGetAttribute(controlslistAttr) == m_controlsList->value())
+    return;
+
+  setSynchronizedLazyAttribute(controlslistAttr, m_controlsList->value());
+  if (mediaControls())
+    mediaControls()->onControlsListUpdated();
+}
+
 double HTMLMediaElement::volume() const {
   return m_volume;
 }
@@ -3823,6 +3845,7 @@
   visitor->trace(m_srcObject);
   visitor->trace(m_autoplayVisibilityObserver);
   visitor->trace(m_mediaControls);
+  visitor->trace(m_controlsList);
   visitor->template registerWeakMembers<HTMLMediaElement,
                                         &HTMLMediaElement::clearWeakMembers>(
       this);
diff --git a/third_party/WebKit/Source/core/html/HTMLMediaElement.h b/third_party/WebKit/Source/core/html/HTMLMediaElement.h
index 8d64be5b..780d824 100644
--- a/third_party/WebKit/Source/core/html/HTMLMediaElement.h
+++ b/third_party/WebKit/Source/core/html/HTMLMediaElement.h
@@ -27,6 +27,7 @@
 #ifndef HTMLMediaElement_h
 #define HTMLMediaElement_h
 
+#include <memory>
 #include "bindings/core/v8/ActiveScriptWrappable.h"
 #include "bindings/core/v8/ScriptPromise.h"
 #include "bindings/core/v8/TraceWrapperMember.h"
@@ -35,6 +36,7 @@
 #include "core/dom/SuspendableObject.h"
 #include "core/events/GenericEventQueue.h"
 #include "core/html/HTMLElement.h"
+#include "core/html/HTMLMediaElementControlsList.h"
 #include "core/html/track/TextTrack.h"
 #include "platform/Supplementable.h"
 #include "platform/WebTaskRunner.h"
@@ -42,7 +44,6 @@
 #include "platform/network/mime/MIMETypeRegistry.h"
 #include "public/platform/WebAudioSourceProviderClient.h"
 #include "public/platform/WebMediaPlayerClient.h"
-#include <memory>
 
 namespace blink {
 
@@ -202,6 +203,8 @@
   // controls
   bool shouldShowControls(
       const RecordMetricsBehavior = RecordMetricsBehavior::DoNotRecord) const;
+  HTMLMediaElementControlsList* controlsList() const;
+  void controlsListValueWasSet();
   double volume() const;
   void setVolume(double, ExceptionState& = ASSERT_NO_EXCEPTION);
   bool muted() const;
@@ -749,6 +752,7 @@
   IntRect m_currentIntersectRect;
 
   Member<MediaControls> m_mediaControls;
+  Member<HTMLMediaElementControlsList> m_controlsList;
 
   static URLRegistry* s_mediaStreamRegistry;
 };
diff --git a/third_party/WebKit/Source/core/html/HTMLMediaElement.idl b/third_party/WebKit/Source/core/html/HTMLMediaElement.idl
index 2789e9a..6c88c1c4 100644
--- a/third_party/WebKit/Source/core/html/HTMLMediaElement.idl
+++ b/third_party/WebKit/Source/core/html/HTMLMediaElement.idl
@@ -76,6 +76,8 @@
 
     // controls
     [CEReactions, Reflect] attribute boolean controls;
+    // Spec: https://wicg.github.io/controls-list/html-output/multipage/embedded-content.html#attr-media-controlslist
+    [SameObject, PutForwards=value] readonly attribute DOMTokenList controlsList;
     [RaisesException=Setter] attribute double volume;
     attribute boolean muted;
     [CEReactions, Reflect=muted] attribute boolean defaultMuted;
diff --git a/third_party/WebKit/Source/core/html/HTMLMediaElementControlsList.cpp b/third_party/WebKit/Source/core/html/HTMLMediaElementControlsList.cpp
new file mode 100644
index 0000000..45ddcbbb
--- /dev/null
+++ b/third_party/WebKit/Source/core/html/HTMLMediaElementControlsList.cpp
@@ -0,0 +1,60 @@
+// 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 "core/html/HTMLMediaElementControlsList.h"
+
+#include "core/html/HTMLMediaElement.h"
+
+namespace blink {
+
+namespace {
+
+const char kNoDownload[] = "nodownload";
+const char kNoFullscreen[] = "nofullscreen";
+const char kNoRemotePlayback[] = "noremoteplayback";
+
+const char* kSupportedTokens[] = {kNoDownload, kNoFullscreen,
+                                  kNoRemotePlayback};
+
+}  // namespace
+
+HTMLMediaElementControlsList::HTMLMediaElementControlsList(
+    HTMLMediaElement* element)
+    : DOMTokenList(this), m_element(element) {}
+
+HTMLMediaElementControlsList::~HTMLMediaElementControlsList() = default;
+
+DEFINE_TRACE(HTMLMediaElementControlsList) {
+  visitor->trace(m_element);
+  DOMTokenList::trace(visitor);
+  DOMTokenListObserver::trace(visitor);
+}
+
+bool HTMLMediaElementControlsList::validateTokenValue(
+    const AtomicString& tokenValue,
+    ExceptionState&) const {
+  for (const char* supportedToken : kSupportedTokens) {
+    if (tokenValue == supportedToken)
+      return true;
+  }
+  return false;
+}
+
+void HTMLMediaElementControlsList::valueWasSet() {
+  m_element->controlsListValueWasSet();
+}
+
+bool HTMLMediaElementControlsList::shouldHideDownload() const {
+  return tokens().contains(kNoDownload);
+}
+
+bool HTMLMediaElementControlsList::shouldHideFullscreen() const {
+  return tokens().contains(kNoFullscreen);
+}
+
+bool HTMLMediaElementControlsList::shouldHideRemotePlayback() const {
+  return tokens().contains(kNoRemotePlayback);
+}
+
+}  // namespace blink
diff --git a/third_party/WebKit/Source/core/html/HTMLMediaElementControlsList.h b/third_party/WebKit/Source/core/html/HTMLMediaElementControlsList.h
new file mode 100644
index 0000000..8b4d0d6
--- /dev/null
+++ b/third_party/WebKit/Source/core/html/HTMLMediaElementControlsList.h
@@ -0,0 +1,45 @@
+// 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 HTMLMediaElementControlsList_h
+#define HTMLMediaElementControlsList_h
+
+#include "core/dom/DOMTokenList.h"
+#include "platform/heap/Handle.h"
+
+namespace blink {
+
+class HTMLMediaElement;
+
+class HTMLMediaElementControlsList final : public DOMTokenList,
+                                           public DOMTokenListObserver {
+  USING_GARBAGE_COLLECTED_MIXIN(HTMLMediaElementControlsList);
+
+ public:
+  static HTMLMediaElementControlsList* create(HTMLMediaElement* element) {
+    return new HTMLMediaElementControlsList(element);
+  }
+
+  ~HTMLMediaElementControlsList() override;
+
+  DECLARE_VIRTUAL_TRACE();
+
+  // Whether the list dictates to hide a certain control.
+  bool shouldHideDownload() const;
+  bool shouldHideFullscreen() const;
+  bool shouldHideRemotePlayback() const;
+
+ private:
+  explicit HTMLMediaElementControlsList(HTMLMediaElement*);
+  bool validateTokenValue(const AtomicString&, ExceptionState&) const override;
+
+  // DOMTokenListObserver.
+  void valueWasSet() override;
+
+  Member<HTMLMediaElement> m_element;
+};
+
+}  // namespace blink
+
+#endif
diff --git a/third_party/WebKit/Source/core/html/HTMLMediaElementEventListenersTest.cpp b/third_party/WebKit/Source/core/html/HTMLMediaElementEventListenersTest.cpp
index 2d525dc..208cadb 100644
--- a/third_party/WebKit/Source/core/html/HTMLMediaElementEventListenersTest.cpp
+++ b/third_party/WebKit/Source/core/html/HTMLMediaElementEventListenersTest.cpp
@@ -129,8 +129,9 @@
   EXPECT_EQ(controls(), video()->mediaControls());
 }
 
+// crbug.com/700207
 TEST_F(HTMLMediaElementEventListenersTest,
-       FullscreenDetectorTimerCancelledOnContextDestroy) {
+       DISABLED_FullscreenDetectorTimerCancelledOnContextDestroy) {
   EXPECT_EQ(video(), nullptr);
   document().body()->setInnerHTML("<body><video></video</body>");
   video()->setSrc("http://example.com");
diff --git a/third_party/WebKit/Source/core/html/shadow/MediaControlElements.cpp b/third_party/WebKit/Source/core/html/shadow/MediaControlElements.cpp
index 167398c..5eb7dca 100644
--- a/third_party/WebKit/Source/core/html/shadow/MediaControlElements.cpp
+++ b/third_party/WebKit/Source/core/html/shadow/MediaControlElements.cpp
@@ -38,6 +38,7 @@
 #include "core/events/MouseEvent.h"
 #include "core/frame/LocalFrame.h"
 #include "core/frame/Settings.h"
+#include "core/frame/UseCounter.h"
 #include "core/html/HTMLAnchorElement.h"
 #include "core/html/HTMLLabelElement.h"
 #include "core/html/HTMLMediaSource.h"
@@ -706,6 +707,13 @@
   if (mediaElement().duration() == std::numeric_limits<double>::infinity())
     return false;
 
+  // The attribute disables the download button.
+  if (mediaElement().controlsList()->shouldHideDownload()) {
+    UseCounter::count(mediaElement().document(),
+                      UseCounter::HTMLMediaElementControlsListNoDownload);
+    return false;
+  }
+
   return true;
 }
 
diff --git a/third_party/WebKit/Source/core/html/shadow/MediaControls.cpp b/third_party/WebKit/Source/core/html/shadow/MediaControls.cpp
index 21cea2d..0c808b0 100644
--- a/third_party/WebKit/Source/core/html/shadow/MediaControls.cpp
+++ b/third_party/WebKit/Source/core/html/shadow/MediaControls.cpp
@@ -32,6 +32,7 @@
 #include "core/dom/TaskRunnerHelper.h"
 #include "core/events/MouseEvent.h"
 #include "core/frame/Settings.h"
+#include "core/frame/UseCounter.h"
 #include "core/html/HTMLMediaElement.h"
 #include "core/html/HTMLVideoElement.h"
 #include "core/html/shadow/MediaControlsMediaEventListener.h"
@@ -67,6 +68,12 @@
   if (!Fullscreen::fullscreenEnabled(mediaElement.document()))
     return false;
 
+  if (mediaElement.controlsList()->shouldHideFullscreen()) {
+    UseCounter::count(mediaElement.document(),
+                      UseCounter::HTMLMediaElementControlsListNoFullscreen);
+    return false;
+  }
+
   return true;
 }
 
@@ -80,6 +87,13 @@
   if (document.settings() && !document.settings()->getMediaControlsEnabled())
     return false;
 
+  // The page disabled the button via the attribute.
+  if (mediaElement.controlsList()->shouldHideRemotePlayback()) {
+    UseCounter::count(mediaElement.document(),
+                      UseCounter::HTMLMediaElementControlsListNoRemotePlayback);
+    return false;
+  }
+
   return mediaElement.hasRemoteRoutes();
 }
 
@@ -397,6 +411,12 @@
   onVolumeChange();
   onTextTracksAddedOrRemoved();
 
+  onControlsListUpdated();
+}
+
+void MediaControls::onControlsListUpdated() {
+  BatchedControlUpdate batch(this);
+
   m_fullscreenButton->setIsWanted(shouldShowFullscreenButton(mediaElement()));
 
   refreshCastButtonVisibilityWithoutUpdate();
diff --git a/third_party/WebKit/Source/core/html/shadow/MediaControls.h b/third_party/WebKit/Source/core/html/shadow/MediaControls.h
index 5ced7c1..3b313fb 100644
--- a/third_party/WebKit/Source/core/html/shadow/MediaControls.h
+++ b/third_party/WebKit/Source/core/html/shadow/MediaControls.h
@@ -49,6 +49,7 @@
   void removedFrom(ContainerNode*) override;
 
   void reset();
+  void onControlsListUpdated();
 
   void show();
   void hide();
diff --git a/third_party/WebKit/Source/core/layout/ng/geometry/ng_logical_rect.cc b/third_party/WebKit/Source/core/layout/ng/geometry/ng_logical_rect.cc
index 90a5672f2..aeaf7c3 100644
--- a/third_party/WebKit/Source/core/layout/ng/geometry/ng_logical_rect.cc
+++ b/third_party/WebKit/Source/core/layout/ng/geometry/ng_logical_rect.cc
@@ -25,11 +25,13 @@
 }
 
 String NGLogicalRect::ToString() const {
-  return String::format("%s,%s %sx%s",
-                        offset.inline_offset.toString().ascii().data(),
-                        offset.block_offset.toString().ascii().data(),
-                        size.inline_size.toString().ascii().data(),
-                        size.block_size.toString().ascii().data());
+  return IsEmpty()
+             ? "(empty)"
+             : String::format("%sx%s at (%s,%s)",
+                              size.inline_size.toString().ascii().data(),
+                              size.block_size.toString().ascii().data(),
+                              offset.inline_offset.toString().ascii().data(),
+                              offset.block_offset.toString().ascii().data());
 }
 
 std::ostream& operator<<(std::ostream& os, const NGLogicalRect& value) {
diff --git a/third_party/WebKit/Source/core/layout/ng/geometry/ng_logical_rect.h b/third_party/WebKit/Source/core/layout/ng/geometry/ng_logical_rect.h
index 1256793..c81451e 100644
--- a/third_party/WebKit/Source/core/layout/ng/geometry/ng_logical_rect.h
+++ b/third_party/WebKit/Source/core/layout/ng/geometry/ng_logical_rect.h
@@ -38,6 +38,10 @@
     return offset.block_offset + size.block_size;
   }
 
+  NGLogicalOffset InlineEndBlockStartOffset() const {
+    return {InlineEndOffset(), BlockStartOffset()};
+  }
+
   LayoutUnit BlockSize() const { return size.block_size; }
   LayoutUnit InlineSize() const { return size.inline_size; }
 
diff --git a/third_party/WebKit/Source/core/layout/ng/ng_constraint_space_test.cc b/third_party/WebKit/Source/core/layout/ng/ng_constraint_space_test.cc
index 7097591..31513884 100644
--- a/third_party/WebKit/Source/core/layout/ng/ng_constraint_space_test.cc
+++ b/third_party/WebKit/Source/core/layout/ng/ng_constraint_space_test.cc
@@ -13,9 +13,11 @@
 
 namespace {
 
-RefPtr<NGConstraintSpace> ConstructConstraintSpace(NGWritingMode writing_mode,
-                                                   TextDirection direction,
-                                                   NGLogicalSize size) {
+RefPtr<NGConstraintSpace> ConstructConstraintSpace(
+    NGWritingMode writing_mode,
+    TextDirection direction,
+    NGLogicalSize size,
+    const NGLogicalOffset& bfc_offset = {}) {
   return NGConstraintSpaceBuilder(writing_mode)
       .SetTextDirection(direction)
       .SetAvailableSize(size)
@@ -23,13 +25,10 @@
       .SetIsFixedSizeInline(true)
       .SetIsInlineDirectionTriggersScrollbar(true)
       .SetFragmentationType(NGFragmentationType::kFragmentColumn)
+      .SetBfcOffset(bfc_offset)
       .ToConstraintSpace(writing_mode);
 }
 
-static String OpportunityToString(const NGLayoutOpportunity& opportunity) {
-  return opportunity.IsEmpty() ? String("(empty)") : opportunity.ToString();
-}
-
 TEST(NGConstraintSpaceTest, LayoutOpportunitiesNoExclusions) {
   NGLogicalSize size;
   size.inline_size = LayoutUnit(600);
@@ -38,8 +37,11 @@
       ConstructConstraintSpace(kHorizontalTopBottom, TextDirection::kLtr, size);
 
   NGLayoutOpportunityIterator iterator(space.get());
-  EXPECT_EQ("0,0 600x400", OpportunityToString(iterator.Next()));
-  EXPECT_EQ("(empty)", OpportunityToString(iterator.Next()));
+  // 600x400 at (0,0)
+  NGLayoutOpportunity opp1 = {{}, {LayoutUnit(600), LayoutUnit(400)}};
+  EXPECT_EQ(opp1, iterator.Next());
+
+  EXPECT_EQ(NGLayoutOpportunity(), iterator.Next());
 }
 
 TEST(NGConstraintSpaceTest, LayoutOpportunitiesTopRightExclusion) {
@@ -50,19 +52,22 @@
   RefPtr<NGConstraintSpace> space =
       ConstructConstraintSpace(kHorizontalTopBottom, TextDirection::kLtr, size);
   NGExclusion exclusion;
-  exclusion.rect.size = {/* inline_size */ LayoutUnit(100),
-                         /* block_size */ LayoutUnit(100)};
-  exclusion.rect.offset = {/* inline_offset */ LayoutUnit(500),
-                           /* block_offset */ LayoutUnit(0)};
+  exclusion.rect.size = {LayoutUnit(100), LayoutUnit(100)};
+  exclusion.rect.offset = {LayoutUnit(500), LayoutUnit()};
   space->AddExclusion(exclusion);
 
   NGLayoutOpportunityIterator iterator(space.get());
-  // First opportunity should be to the left of the exclusion.
-  EXPECT_EQ("0,0 500x400", OpportunityToString(iterator.Next()));
-  // Second opportunity should be below the exclusion.
-  EXPECT_EQ("0,100 600x300", OpportunityToString(iterator.Next()));
-  // There should be no third opportunity.
-  EXPECT_EQ("(empty)", OpportunityToString(iterator.Next()));
+
+  // First opportunity should be to the left of the exclusion: 500x400 at (0,0)
+  NGLayoutOpportunity opp1 = {{}, {LayoutUnit(500), LayoutUnit(400)}};
+  EXPECT_EQ(opp1, iterator.Next());
+
+  // Second opportunity should be below the exclusion: 600x300 at (0,100)
+  NGLayoutOpportunity opp2 = {{LayoutUnit(), LayoutUnit(100)},
+                              {LayoutUnit(600), LayoutUnit(300)}};
+  EXPECT_EQ(opp2, iterator.Next());
+
+  EXPECT_EQ(NGLayoutOpportunity(), iterator.Next());
 }
 
 TEST(NGConstraintSpaceTest, LayoutOpportunitiesTopLeftExclusion) {
@@ -73,19 +78,22 @@
   RefPtr<NGConstraintSpace> space =
       ConstructConstraintSpace(kHorizontalTopBottom, TextDirection::kLtr, size);
   NGExclusion exclusion;
-  exclusion.rect.size = {/* inline_size */ LayoutUnit(100),
-                         /* block_size */ LayoutUnit(100)};
-  exclusion.rect.offset = {/* inline_offset */ LayoutUnit(0),
-                           /* block_offset */ LayoutUnit(0)};
+  exclusion.rect.size = {LayoutUnit(100), LayoutUnit(100)};
   space->AddExclusion(exclusion);
 
   NGLayoutOpportunityIterator iterator(space.get());
-  // First opportunity should be to the right of the exclusion.
-  EXPECT_EQ("100,0 500x400", OpportunityToString(iterator.Next()));
-  // Second opportunity should be below the exclusion.
-  EXPECT_EQ("0,100 600x300", OpportunityToString(iterator.Next()));
-  // There should be no third opportunity.
-  EXPECT_EQ("(empty)", OpportunityToString(iterator.Next()));
+  // First opportunity should be to the right of the exclusion:
+  // 500x400 at (100, 0)
+  NGLayoutOpportunity opp1 = {{LayoutUnit(100), LayoutUnit()},
+                              {LayoutUnit(500), LayoutUnit(400)}};
+  EXPECT_EQ(opp1, iterator.Next());
+
+  // Second opportunity should be below the exclusion: 600x300 at (0,100)
+  NGLayoutOpportunity opp2 = {{LayoutUnit(), LayoutUnit(100)},
+                              {LayoutUnit(600), LayoutUnit(300)}};
+  EXPECT_EQ(opp2, iterator.Next());
+
+  EXPECT_EQ(NGLayoutOpportunity(), iterator.Next());
 }
 
 // Verifies that Layout Opportunity iterator produces 7 layout opportunities
@@ -119,32 +127,45 @@
       ConstructConstraintSpace(kHorizontalTopBottom, TextDirection::kLtr, size);
   // Add exclusions
   NGExclusion exclusion1;
-  exclusion1.rect.size = {/* inline_size */ LayoutUnit(100),
-                          /* block_size */ LayoutUnit(100)};
-  exclusion1.rect.offset = {/* inline_offset */ LayoutUnit(150),
-                            /* block_offset */ LayoutUnit(200)};
+  exclusion1.rect.size = {LayoutUnit(100), LayoutUnit(100)};
+  exclusion1.rect.offset = {LayoutUnit(150), LayoutUnit(200)};
   space->AddExclusion(exclusion1);
   NGExclusion exclusion2;
-  exclusion2.rect.size = {/* inline_size */ LayoutUnit(50),
-                          /* block_size */ LayoutUnit(50)};
-  exclusion2.rect.offset = {/* inline_offset */ LayoutUnit(500),
-                            /* block_offset */ LayoutUnit(350)};
+  exclusion2.rect.size = {LayoutUnit(50), LayoutUnit(50)};
+  exclusion2.rect.offset = {LayoutUnit(500), LayoutUnit(350)};
   space->AddExclusion(exclusion2);
 
   NGLayoutOpportunityIterator iterator(space.get());
-  // 1st Start point
-  EXPECT_EQ("0,0 600x200", OpportunityToString(iterator.Next()));
-  EXPECT_EQ("0,0 150x400", OpportunityToString(iterator.Next()));
-  // 2nd Start point
-  EXPECT_EQ("250,0 350x350", OpportunityToString(iterator.Next()));
-  EXPECT_EQ("250,0 250x400", OpportunityToString(iterator.Next()));
-  // 3rd Start point
-  EXPECT_EQ("550,0 50x400", OpportunityToString(iterator.Next()));
-  // 4th Start point
-  EXPECT_EQ("0,300 600x50", OpportunityToString(iterator.Next()));
-  EXPECT_EQ("0,300 500x100", OpportunityToString(iterator.Next()));
-  // Iterator is exhausted.
-  EXPECT_EQ("(empty)", OpportunityToString(iterator.Next()));
+  NGLogicalOffset start_point1;
+  // 600x200 at (0,0)
+  NGLayoutOpportunity opp1 = {start_point1, {LayoutUnit(600), LayoutUnit(200)}};
+  EXPECT_EQ(opp1, (iterator.Next()));
+  // 150x400 at (0,0)
+  NGLayoutOpportunity opp2 = {start_point1, {LayoutUnit(150), LayoutUnit(400)}};
+  EXPECT_EQ(opp2, (iterator.Next()));
+
+  NGLogicalOffset start_point2 = {LayoutUnit(250), LayoutUnit()};
+  // 350x350 at (250,0)
+  NGLayoutOpportunity opp3 = {start_point2, {LayoutUnit(350), LayoutUnit(350)}};
+  EXPECT_EQ(opp3, (iterator.Next()));
+  // 250x400 at (250,0)
+  NGLayoutOpportunity opp4 = {start_point2, {LayoutUnit(250), LayoutUnit(400)}};
+  EXPECT_EQ(opp4, (iterator.Next()));
+
+  NGLogicalOffset start_point3 = {LayoutUnit(550), LayoutUnit()};
+  // 50x400 at (550,0)
+  NGLayoutOpportunity opp5 = {start_point3, {LayoutUnit(50), LayoutUnit(400)}};
+  EXPECT_EQ(opp5, (iterator.Next()));
+
+  NGLogicalOffset start_point4 = {LayoutUnit(), LayoutUnit(300)};
+  // 600x50 at (0,300)
+  NGLayoutOpportunity opp6 = {start_point4, {LayoutUnit(600), LayoutUnit(50)}};
+  EXPECT_EQ(opp6, (iterator.Next()));
+  // 500x100 at (0,300)
+  NGLayoutOpportunity opp7 = {start_point4, {LayoutUnit(500), LayoutUnit(100)}};
+  EXPECT_EQ(opp7, (iterator.Next()));
+
+  EXPECT_EQ(NGLayoutOpportunity(), iterator.Next());
 }
 
 // This test is the same as LayoutOpportunitiesTwoInMiddle with the only
@@ -169,36 +190,51 @@
       ConstructConstraintSpace(kHorizontalTopBottom, TextDirection::kLtr, size);
   // Add exclusions
   NGExclusion exclusion1;
-  exclusion1.rect.size = {/* inline_size */ LayoutUnit(100),
-                          /* block_size */ LayoutUnit(100)};
-  exclusion1.rect.offset = {/* inline_offset */ LayoutUnit(150),
-                            /* block_offset */ LayoutUnit(200)};
+  exclusion1.rect.size = {LayoutUnit(100), LayoutUnit(100)};
+  exclusion1.rect.offset = {LayoutUnit(150), LayoutUnit(200)};
   space->AddExclusion(exclusion1);
   NGExclusion exclusion2;
-  exclusion2.rect.size = {/* inline_size */ LayoutUnit(50),
-                          /* block_size */ LayoutUnit(50)};
-  exclusion2.rect.offset = {/* inline_offset */ LayoutUnit(500),
-                            /* block_offset */ LayoutUnit(350)};
+  exclusion2.rect.size = {LayoutUnit(50), LayoutUnit(50)};
+  exclusion2.rect.offset = {LayoutUnit(500), LayoutUnit(350)};
   space->AddExclusion(exclusion2);
 
-  const NGLogicalOffset origin_point = {LayoutUnit(0), LayoutUnit(200)};
+  const NGLogicalOffset origin_point = {LayoutUnit(), LayoutUnit(200)};
   const NGLogicalOffset leader_point = {LayoutUnit(250), LayoutUnit(300)};
   NGLayoutOpportunityIterator iterator(space.get(), origin_point, leader_point);
-  // 1st Start Point
-  EXPECT_EQ("250,200 350x150", OpportunityToString(iterator.Next()));
-  EXPECT_EQ("250,200 250x400", OpportunityToString(iterator.Next()));
-  // 2nd Start Point
-  EXPECT_EQ("550,200 50x400", OpportunityToString(iterator.Next()));
-  // 3rd Start Point
-  EXPECT_EQ("0,300 600x50", OpportunityToString(iterator.Next()));
-  EXPECT_EQ("0,300 500x300", OpportunityToString(iterator.Next()));
+
+  NGLogicalOffset start_point1 = {LayoutUnit(250), LayoutUnit(200)};
+  // 350x150 at (250,200)
+  NGLayoutOpportunity opp1 = {start_point1, {LayoutUnit(350), LayoutUnit(150)}};
+  EXPECT_EQ(opp1, iterator.Next());
+  // 250x400 at (250,200)
+  NGLayoutOpportunity opp2 = {start_point1, {LayoutUnit(250), LayoutUnit(400)}};
+  EXPECT_EQ(opp2, iterator.Next());
+
+  NGLogicalOffset start_point2 = {LayoutUnit(550), LayoutUnit(200)};
+  // 50x400 at (550,200)
+  NGLayoutOpportunity opp3 = {start_point2, {LayoutUnit(50), LayoutUnit(400)}};
+  EXPECT_EQ(opp3, iterator.Next());
+
+  NGLogicalOffset start_point3 = {LayoutUnit(), LayoutUnit(300)};
+  // 600x50 at (0,300)
+  NGLayoutOpportunity opp4 = {start_point3, {LayoutUnit(600), LayoutUnit(50)}};
+  EXPECT_EQ(opp4, iterator.Next());
+  // 500x300 at (0,300)
+  NGLayoutOpportunity opp5 = {start_point3, {LayoutUnit(500), LayoutUnit(300)}};
+  EXPECT_EQ(opp5, iterator.Next());
+
   // 4th Start Point
-  EXPECT_EQ("0,400 600x200", OpportunityToString(iterator.Next()));
+  NGLogicalOffset start_point4 = {LayoutUnit(), LayoutUnit(400)};
+  // 600x200 at (0,400)
+  NGLayoutOpportunity opp6 = {start_point4, {LayoutUnit(600), LayoutUnit(200)}};
+  EXPECT_EQ(opp6, iterator.Next());
+
   // TODO(glebl): The opportunity below should not be generated.
-  EXPECT_EQ("250,400 350x200", OpportunityToString(iterator.Next()));
-  // Iterator is exhausted.
-  EXPECT_EQ("(empty)", OpportunityToString(iterator.Next()));
+  EXPECT_EQ("350x200 at (250,400)", iterator.Next().ToString());
+
+  EXPECT_EQ(NGLayoutOpportunity(), iterator.Next());
 }
+
 // Verifies that Layout Opportunity iterator ignores the exclusion that is not
 // within constraint space.
 //
@@ -215,21 +251,103 @@
 //   Layout opportunity iterator generates only one opportunity that equals to
 //   available constraint space, i.e. 0,0 600x200
 TEST(NGConstraintSpaceTest, LayoutOpportunitiesWithOutOfBoundsExclusions) {
-  NGLogicalSize size;
-  size.inline_size = LayoutUnit(600);
-  size.block_size = LayoutUnit(100);
+  NGLogicalSize size = {LayoutUnit(600), LayoutUnit(100)};
   RefPtr<NGConstraintSpace> space =
       ConstructConstraintSpace(kHorizontalTopBottom, TextDirection::kLtr, size);
   NGExclusion exclusion;
-  exclusion.rect.size = {/* inline_size */ LayoutUnit(100),
-                         /* block_size */ LayoutUnit(100)};
-  exclusion.rect.offset = {/* inline_offset */ LayoutUnit(0),
-                           /* block_offset */ LayoutUnit(150)};
+  exclusion.rect.size = {LayoutUnit(100), LayoutUnit(100)};
+  exclusion.rect.offset = {LayoutUnit(), LayoutUnit(150)};
   space->AddExclusion(exclusion);
 
   NGLayoutOpportunityIterator iterator(space.get());
-  EXPECT_EQ("0,0 600x100", OpportunityToString(iterator.Next()));
-  EXPECT_EQ("(empty)", OpportunityToString(iterator.Next()));
+  // 600x100 at (0,0)
+  NGLayoutOpportunity opp = {{}, size};
+  EXPECT_EQ(opp, iterator.Next());
+
+  EXPECT_EQ(NGLayoutOpportunity(), iterator.Next());
+}
+
+// Verifies that we combine 2 adjoining left exclusions into one left exclusion.
+TEST(NGConstraintSpaceTest, TwoLeftExclusionsShadowEachOther) {
+  NGLogicalOffset bfc_offset = {LayoutUnit(8), LayoutUnit(8)};
+  RefPtr<NGConstraintSpace> space =
+      ConstructConstraintSpace(kHorizontalTopBottom, TextDirection::kLtr,
+                               {LayoutUnit(200), LayoutUnit(200)}, bfc_offset);
+
+  NGExclusion small_left;
+  small_left.rect.size = {LayoutUnit(10), LayoutUnit(10)};
+  small_left.rect.offset = bfc_offset;
+  small_left.type = NGExclusion::kFloatLeft;
+  space->AddExclusion(small_left);
+
+  NGExclusion big_left;
+  big_left.rect.size = {LayoutUnit(20), LayoutUnit(20)};
+  big_left.rect.offset = bfc_offset;
+  big_left.rect.offset.inline_offset += small_left.rect.InlineSize();
+  big_left.type = NGExclusion::kFloatLeft;
+  space->AddExclusion(big_left);
+
+  NGLayoutOpportunityIterator iterator(space.get(), bfc_offset);
+
+  NGLogicalOffset start_point1 = bfc_offset;
+  start_point1.inline_offset +=
+      small_left.rect.InlineSize() + big_left.rect.InlineSize();
+  // 170x200 at (38, 8)
+  NGLayoutOpportunity opportunity1 = {start_point1,
+                                      {LayoutUnit(170), LayoutUnit(200)}};
+  EXPECT_EQ(opportunity1, iterator.Next());
+
+  NGLogicalOffset start_point2 = bfc_offset;
+  start_point2.block_offset += big_left.rect.BlockSize();
+  // 200x180 at (8, 28)
+  NGLayoutOpportunity opportunity2 = {start_point2,
+                                      {LayoutUnit(200), LayoutUnit(180)}};
+  EXPECT_EQ(opportunity2, iterator.Next());
+
+  EXPECT_EQ(NGLayoutOpportunity(), iterator.Next());
+}
+
+// Verifies that we combine 2 adjoining right exclusions into one right
+// exclusion.
+TEST(NGConstraintSpaceTest, TwoRightExclusionsShadowEachOther) {
+  NGLogicalOffset bfc_offset = {LayoutUnit(8), LayoutUnit(8)};
+  RefPtr<NGConstraintSpace> space =
+      ConstructConstraintSpace(kHorizontalTopBottom, TextDirection::kLtr,
+                               {LayoutUnit(200), LayoutUnit(200)}, bfc_offset);
+
+  NGExclusion small_right;
+  small_right.rect.size = {LayoutUnit(10), LayoutUnit(10)};
+  small_right.rect.offset = bfc_offset;
+  small_right.rect.offset.inline_offset +=
+      space->AvailableSize().inline_size - small_right.rect.InlineSize();
+  small_right.type = NGExclusion::kFloatRight;
+  space->AddExclusion(small_right);
+
+  NGExclusion big_right;
+  big_right.rect.size = {LayoutUnit(20), LayoutUnit(20)};
+  big_right.rect.offset = bfc_offset;
+  big_right.rect.offset.inline_offset += space->AvailableSize().inline_size -
+                                         small_right.rect.InlineSize() -
+                                         big_right.rect.InlineSize();
+  big_right.type = NGExclusion::kFloatRight;
+  space->AddExclusion(big_right);
+
+  NGLayoutOpportunityIterator iterator(space.get(), bfc_offset);
+
+  NGLogicalOffset start_point1 = bfc_offset;
+  // 170x200 at (8, 8)
+  NGLayoutOpportunity opportunity1 = {start_point1,
+                                      {LayoutUnit(170), LayoutUnit(200)}};
+  EXPECT_EQ(opportunity1, iterator.Next());
+
+  NGLogicalOffset start_point2 = bfc_offset;
+  start_point2.block_offset += big_right.rect.BlockSize();
+  // 200x180 at (8, 28)
+  NGLayoutOpportunity opportunity2 = {start_point2,
+                                      {LayoutUnit(200), LayoutUnit(180)}};
+  EXPECT_EQ(opportunity2, iterator.Next());
+
+  EXPECT_EQ(NGLayoutOpportunity(), iterator.Next());
 }
 
 }  // namespace
diff --git a/third_party/WebKit/Source/core/layout/ng/ng_exclusion.cc b/third_party/WebKit/Source/core/layout/ng/ng_exclusion.cc
index e5f711b..f31568ad 100644
--- a/third_party/WebKit/Source/core/layout/ng/ng_exclusion.cc
+++ b/third_party/WebKit/Source/core/layout/ng/ng_exclusion.cc
@@ -17,6 +17,34 @@
                         type);
 }
 
+bool NGExclusion::MaybeCombineWith(const NGExclusion& other) {
+  if (other.rect.BlockEndOffset() < rect.BlockEndOffset())
+    return false;
+
+  if (other.type != type)
+    return false;
+
+  switch (other.type) {
+    case NGExclusion::kFloatLeft: {
+      if (other.rect.offset == rect.InlineEndBlockStartOffset()) {
+        rect.size = {other.rect.InlineSize() + rect.InlineSize(),
+                     other.rect.BlockSize()};
+        return true;
+      }
+    }
+    case NGExclusion::kFloatRight: {
+      if (rect.offset == other.rect.InlineEndBlockStartOffset()) {
+        rect.offset = other.rect.offset;
+        rect.size = {other.rect.InlineSize() + rect.InlineSize(),
+                     other.rect.BlockSize()};
+        return true;
+      }
+    }
+    default:
+      return false;
+  }
+}
+
 std::ostream& operator<<(std::ostream& stream, const NGExclusion& value) {
   return stream << value.ToString();
 }
diff --git a/third_party/WebKit/Source/core/layout/ng/ng_exclusion.h b/third_party/WebKit/Source/core/layout/ng/ng_exclusion.h
index b8b86edd..b5a19ef8 100644
--- a/third_party/WebKit/Source/core/layout/ng/ng_exclusion.h
+++ b/third_party/WebKit/Source/core/layout/ng/ng_exclusion.h
@@ -31,7 +31,25 @@
   Type type;
 
   bool operator==(const NGExclusion& other) const;
+
   String ToString() const;
+
+  // Tries to combine the current exclusion with {@code other} exclusion and
+  // returns true if successful.
+  // We can combine 2 exclusions if they
+  // - adjoining to each other and have the same exclusion type
+  // - {@code other} exclusion shadows the current one.
+  //   That's because it's not allowed to position anything in the shadowed
+  //   area.
+  //
+  //   Example:
+  //   <div id="SS" style="float: left; height: 10px; width: 10px"></div>
+  //   <div id="BB" style="float: left; height: 20px; width: 20px"></div>
+  //   +----------------+
+  //   |SSBB
+  //   |**BB
+  //   We combine SS and BB exclusions including the shadowed area (**).
+  bool MaybeCombineWith(const NGExclusion& other);
 };
 
 CORE_EXPORT std::ostream& operator<<(std::ostream&, const NGExclusion&);
diff --git a/third_party/WebKit/Source/core/layout/ng/ng_layout_opportunity_iterator.cc b/third_party/WebKit/Source/core/layout/ng/ng_layout_opportunity_iterator.cc
index f95d51d..d2956df 100644
--- a/third_party/WebKit/Source/core/layout/ng/ng_layout_opportunity_iterator.cc
+++ b/third_party/WebKit/Source/core/layout/ng/ng_layout_opportunity_iterator.cc
@@ -28,7 +28,7 @@
   for (unsigned i = 0; i < indent; i++)
     indent_builder.append("\t");
 
-  if (!node->exclusion)
+  if (node->IsLeafNode())
     return;
 
   string_builder->append(indent_builder.toString());
@@ -160,6 +160,13 @@
   return nullptr;
 }
 
+void SplitNGLayoutOpportunityTreeNode(const NGLogicalRect& rect,
+                                      NGLayoutOpportunityTreeNode* node) {
+  node->left = CreateLeftNGLayoutOpportunityTreeNode(node, rect);
+  node->right = CreateRightNGLayoutOpportunityTreeNode(node, rect);
+  node->bottom = CreateBottomNGLayoutOpportunityTreeNode(node, rect);
+}
+
 // Gets/Creates the "TOP" positioned constraint space by splitting
 // the parent node with the exclusion.
 //
@@ -197,24 +204,29 @@
   if (!exclusion->rect.IsContained(node->opportunity))
     return;
 
-  if (node->exclusion) {
-    InsertExclusion(node->left, exclusion, opportunities);
-    InsertExclusion(node->bottom, exclusion, opportunities);
-    InsertExclusion(node->right, exclusion, opportunities);
+  if (node->exclusions.isEmpty()) {
+    SplitNGLayoutOpportunityTreeNode(exclusion->rect, node);
+
+    NGLayoutOpportunity top_layout_opp =
+        GetTopSpace(node->opportunity, exclusion->rect);
+    if (!top_layout_opp.IsEmpty())
+      opportunities.push_back(top_layout_opp);
+
+    node->exclusions.push_back(exclusion);
+    node->combined_exclusion = WTF::makeUnique<NGExclusion>(*exclusion);
     return;
   }
 
-  // Split the current node.
-  node->left = CreateLeftNGLayoutOpportunityTreeNode(node, exclusion->rect);
-  node->right = CreateRightNGLayoutOpportunityTreeNode(node, exclusion->rect);
-  node->bottom = CreateBottomNGLayoutOpportunityTreeNode(node, exclusion->rect);
+  DCHECK(!node->exclusions.isEmpty());
 
-  NGLayoutOpportunity top_layout_opp =
-      GetTopSpace(node->opportunity, exclusion->rect);
-  if (!top_layout_opp.IsEmpty())
-    opportunities.push_back(top_layout_opp);
-
-  node->exclusion = exclusion;
+  if (node->combined_exclusion->MaybeCombineWith(*exclusion)) {
+    SplitNGLayoutOpportunityTreeNode(node->combined_exclusion->rect, node);
+    node->exclusions.push_back(exclusion);
+  } else {
+    InsertExclusion(node->left, exclusion, opportunities);
+    InsertExclusion(node->bottom, exclusion, opportunities);
+    InsertExclusion(node->right, exclusion, opportunities);
+  }
 }
 
 // Compares exclusions by their top position.
diff --git a/third_party/WebKit/Source/core/layout/ng/ng_layout_opportunity_tree_node.cc b/third_party/WebKit/Source/core/layout/ng/ng_layout_opportunity_tree_node.cc
index 49b04cd..b737001 100644
--- a/third_party/WebKit/Source/core/layout/ng/ng_layout_opportunity_tree_node.cc
+++ b/third_party/WebKit/Source/core/layout/ng/ng_layout_opportunity_tree_node.cc
@@ -8,7 +8,7 @@
 
 NGLayoutOpportunityTreeNode::NGLayoutOpportunityTreeNode(
     const NGLogicalRect opportunity)
-    : opportunity(opportunity), exclusion(nullptr) {
+    : opportunity(opportunity), combined_exclusion(nullptr) {
   exclusion_edge.start = opportunity.offset.inline_offset;
   exclusion_edge.end = exclusion_edge.start + opportunity.size.inline_size;
 }
@@ -18,13 +18,14 @@
     NGEdge exclusion_edge)
     : opportunity(opportunity),
       exclusion_edge(exclusion_edge),
-      exclusion(nullptr) {}
+      combined_exclusion(nullptr) {}
 
 String NGLayoutOpportunityTreeNode::ToString() const {
-  return String::format(
-      "Opportunity: '%s' Exclusion: '%s'",
-      opportunity.ToString().ascii().data(),
-      exclusion ? exclusion->ToString().ascii().data() : "null");
+  return String::format("Opportunity: '%s' Exclusion: '%s'",
+                        opportunity.ToString().ascii().data(),
+                        combined_exclusion
+                            ? combined_exclusion->ToString().ascii().data()
+                            : "null");
 }
 
 DEFINE_TRACE(NGLayoutOpportunityTreeNode) {
diff --git a/third_party/WebKit/Source/core/layout/ng/ng_layout_opportunity_tree_node.h b/third_party/WebKit/Source/core/layout/ng/ng_layout_opportunity_tree_node.h
index ea12337..037768f 100644
--- a/third_party/WebKit/Source/core/layout/ng/ng_layout_opportunity_tree_node.h
+++ b/third_party/WebKit/Source/core/layout/ng/ng_layout_opportunity_tree_node.h
@@ -16,7 +16,7 @@
 // layout opportunity after the parent spatial rectangle is split by the
 // exclusion rectangle.
 struct CORE_EXPORT NGLayoutOpportunityTreeNode
-    : public GarbageCollected<NGLayoutOpportunityTreeNode> {
+    : public GarbageCollectedFinalized<NGLayoutOpportunityTreeNode> {
  public:
   // Default constructor.
   // Creates a Layout Opportunity tree node that is limited by it's own edge
@@ -41,12 +41,16 @@
   // Edge that limits this layout opportunity from above.
   NGEdge exclusion_edge;
 
-  // Exclusion that splits apart this layout opportunity.
-  const NGExclusion* exclusion;  // Not owned.
+  // Exclusions that splits apart this layout opportunity.
+  Vector<const NGExclusion*> exclusions;  // Not owned.
+
+  // Exclusion that represent all combined exclusions that
+  // split this node.
+  std::unique_ptr<NGExclusion> combined_exclusion;
 
   // Whether this node is a leaf node.
-  // The node is a leaf if it doesn't have an exclusion that splits it apart.
-  bool IsLeafNode() const { return !exclusion; }
+  // The node is a leaf if it doesn't have exclusions that split it apart.
+  bool IsLeafNode() const { return exclusions.isEmpty(); }
 
   String ToString() const;
 
diff --git a/third_party/WebKit/Source/modules/bluetooth/BluetoothError.cpp b/third_party/WebKit/Source/modules/bluetooth/BluetoothError.cpp
index 78e59fc..bb1cad02 100644
--- a/third_party/WebKit/Source/modules/bluetooth/BluetoothError.cpp
+++ b/third_party/WebKit/Source/modules/bluetooth/BluetoothError.cpp
@@ -28,6 +28,9 @@
     case BluetoothOperation::CharacteristicsRetrieval:
       operationString = "retrieve characteristics";
       break;
+    case BluetoothOperation::DescriptorsRetrieval:
+      operationString = "retrieve descriptors";
+      break;
     case BluetoothOperation::GATT:
       operationString = "perform GATT operations";
       break;
diff --git a/third_party/WebKit/Source/modules/bluetooth/BluetoothError.h b/third_party/WebKit/Source/modules/bluetooth/BluetoothError.h
index c6c5c5a..1e3f9075 100644
--- a/third_party/WebKit/Source/modules/bluetooth/BluetoothError.h
+++ b/third_party/WebKit/Source/modules/bluetooth/BluetoothError.h
@@ -16,6 +16,7 @@
 enum class BluetoothOperation {
   ServicesRetrieval,
   CharacteristicsRetrieval,
+  DescriptorsRetrieval,
   GATT,
 };
 
diff --git a/third_party/WebKit/Source/modules/bluetooth/BluetoothRemoteGATTCharacteristic.cpp b/third_party/WebKit/Source/modules/bluetooth/BluetoothRemoteGATTCharacteristic.cpp
index 44d4666..03f5795f 100644
--- a/third_party/WebKit/Source/modules/bluetooth/BluetoothRemoteGATTCharacteristic.cpp
+++ b/third_party/WebKit/Source/modules/bluetooth/BluetoothRemoteGATTCharacteristic.cpp
@@ -335,9 +335,8 @@
     const String& descriptorsUUID) {
   if (!getGatt()->connected()) {
     return ScriptPromise::rejectWithDOMException(
-        scriptState,
-        // TODO(crbug.com/684445): Change to DescriptorsRetrieval.
-        BluetoothError::createNotConnectedException(BluetoothOperation::GATT));
+        scriptState, BluetoothError::createNotConnectedException(
+                         BluetoothOperation::DescriptorsRetrieval));
   }
 
   if (!getGatt()->device()->isValidCharacteristic(
@@ -377,9 +376,8 @@
 
   // If the device is disconnected, reject.
   if (!m_service->device()->gatt()->RemoveFromActiveAlgorithms(resolver)) {
-    // TODO(crbug.com/684445): Change to DescriptorsRetrieval.
-    resolver->reject(
-        BluetoothError::createNotConnectedException(BluetoothOperation::GATT));
+    resolver->reject(BluetoothError::createNotConnectedException(
+        BluetoothOperation::DescriptorsRetrieval));
     return;
   }
 
diff --git a/third_party/mt19937ar/OWNERS b/third_party/mt19937ar/OWNERS
index b9e8da9..3dc913a 100644
--- a/third_party/mt19937ar/OWNERS
+++ b/third_party/mt19937ar/OWNERS
@@ -1 +1,3 @@
 asvitkine@chromium.org
+
+# COMPONENT: Internals>Metrics
diff --git a/tools/chrome_proxy/common/inspector_network.py b/tools/chrome_proxy/common/inspector_network.py
index 8f4d463..01349d00 100644
--- a/tools/chrome_proxy/common/inspector_network.py
+++ b/tools/chrome_proxy/common/inspector_network.py
@@ -4,8 +4,8 @@
 import logging
 
 from telemetry.core import exceptions
-from telemetry.timeline import trace_data
 from telemetry.timeline import model
+from tracing.trace_data import trace_data
 
 
 class InspectorNetworkException(Exception):
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index 51f70d9eb..930201cc 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -51827,6 +51827,28 @@
   </summary>
 </histogram>
 
+<histogram name="Precache.CacheStatus.NonPrefetch.NonTopHosts"
+    enum="HttpCachePattern">
+  <owner>twifkak@chromium.org</owner>
+  <owner>jamartin@chromium.org</owner>
+  <summary>
+    Like Precache.CacheStatus.NonPrefetch, but only for requests with a referer
+    not in the user's top visited hosts. See History.TopHostsVisitsByRank for
+    details on the top hosts computation.
+  </summary>
+</histogram>
+
+<histogram name="Precache.CacheStatus.NonPrefetch.TopHosts"
+    enum="HttpCachePattern">
+  <owner>twifkak@chromium.org</owner>
+  <owner>jamartin@chromium.org</owner>
+  <summary>
+    Like Precache.CacheStatus.NonPrefetch, but only for requests with a referer
+    in the user's top visited hosts. See History.TopHostsVisitsByRank for
+    details on the top hosts computation.
+  </summary>
+</histogram>
+
 <histogram name="Precache.CacheStatus.Prefetch" enum="HttpCachePattern">
   <owner>jamartin@chromium.org</owner>
   <owner>twifkak@chromium.org</owner>
@@ -51961,6 +51983,9 @@
 </histogram>
 
 <histogram name="Precache.Latency.NonPrefetch" units="ms">
+  <obsolete>
+    Deprecated March 7 2017.
+  </obsolete>
   <owner>twifkak@chromium.org</owner>
   <owner>bengr@chromium.org</owner>
   <summary>
@@ -51970,6 +51995,9 @@
 </histogram>
 
 <histogram name="Precache.Latency.NonPrefetch.NonTopHosts" units="ms">
+  <obsolete>
+    Deprecated March 7 2017.
+  </obsolete>
   <owner>twifkak@chromium.org</owner>
   <owner>bengr@chromium.org</owner>
   <summary>
@@ -51980,6 +52008,9 @@
 </histogram>
 
 <histogram name="Precache.Latency.NonPrefetch.TopHosts" units="ms">
+  <obsolete>
+    Deprecated March 7 2017.
+  </obsolete>
   <owner>twifkak@chromium.org</owner>
   <owner>bengr@chromium.org</owner>
   <summary>
@@ -51990,6 +52021,9 @@
 </histogram>
 
 <histogram name="Precache.Latency.Prefetch" units="ms">
+  <obsolete>
+    Deprecated March 7 2017.
+  </obsolete>
   <owner>twifkak@chromium.org</owner>
   <owner>bengr@chromium.org</owner>
   <summary>
@@ -93237,6 +93271,10 @@
   <int value="1850" label="GetCanvas2DContextAttributes"/>
   <int value="1851" label="V8HTMLInputElement_Capture_AttributeGetter"/>
   <int value="1852" label="V8HTMLInputElement_Capture_AttributeSetter"/>
+  <int value="1853" label="HTMLMediaElementControlsListAttribute"/>
+  <int value="1854" label="HTMLMediaElementControlsListNoDownload"/>
+  <int value="1855" label="HTMLMediaElementControlsListNoFullscreen"/>
+  <int value="1856" label="HTMLMediaElementControlsListNoRemotePlayback"/>
 </enum>
 
 <enum name="FetchRequestMode" type="int">
@@ -98945,6 +98983,7 @@
   <int value="-1583728573" label="AutofillCreditCardSigninPromo:disabled"/>
   <int value="-1581724231" label="ModalPermissionPrompts:enabled"/>
   <int value="-1578295753" label="UserMediaScreenCapturing:disabled"/>
+  <int value="-1575554415" label="AndroidPaymentAppsFilter:enabled"/>
   <int value="-1572010356" label="enable-privet-v3"/>
   <int value="-1571841513" label="enable-devtools-experiments"/>
   <int value="-1559789642" label="RunAllFlashInAllowMode:enabled"/>
@@ -99048,6 +99087,7 @@
   <int value="-1137442543" label="enable-slimming-paint"/>
   <int value="-1136627751" label="ignore-autocomplete-off-autofill"/>
   <int value="-1136509631" label="ssl-interstitial-v1"/>
+  <int value="-1132704128" label="AndroidPaymentAppsFilter:disabled"/>
   <int value="-1127996427" label="enable-files-details-panel"/>
   <int value="-1125133283" label="disable-threaded-scrolling"/>
   <int value="-1119700637" label="ui-disable-partial-swap"/>
diff --git a/tools/perf/PRESUBMIT.py b/tools/perf/PRESUBMIT.py
index 13815ee7..fb84812 100644
--- a/tools/perf/PRESUBMIT.py
+++ b/tools/perf/PRESUBMIT.py
@@ -32,10 +32,13 @@
       chromium_src_dir, 'third_party', 'catapult', 'telemetry')
   experimental_dir = input_api.os_path.join(
       chromium_src_dir, 'third_party', 'catapult', 'experimental')
+  tracing_dir = input_api.os_path.join(
+      chromium_src_dir, 'third_party', 'catapult', 'tracing')
   return [
       telemetry_dir,
       input_api.os_path.join(telemetry_dir, 'third_party', 'mock'),
       experimental_dir,
+      tracing_dir,
   ]
 
 
diff --git a/tools/perf/generate_perf_json.py b/tools/perf/generate_perf_json.py
index 49e739f0..6339808b 100755
--- a/tools/perf/generate_perf_json.py
+++ b/tools/perf/generate_perf_json.py
@@ -57,18 +57,19 @@
     'script': 'gtest_perf_test.py',
     'testers': {
       'chromium.perf': [
-        {
-          'name': 'Android Nexus5 Perf',
-          'shards': [2]
-        },
-        {
-          'name': 'Android Nexus6 Perf',
-          'shards': [2]
-        },
-        {
-          'name': 'Android Nexus7v2 Perf',
-          'shards': [2]
-        },
+        # crbug.com/698831
+        # {
+        #   'name': 'Android Nexus5 Perf',
+        #   'shards': [2]
+        # },
+        # {
+        #   'name': 'Android Nexus6 Perf',
+        #   'shards': [2]
+        # },
+        # {
+        #   'name': 'Android Nexus7v2 Perf',
+        #   'shards': [2]
+        # },
         {
           'name': 'Android Nexus9 Perf',
           'shards': [2]
@@ -462,7 +463,8 @@
            'build150-m1', 'build151-m1', 'build152-m1'
           ],
        'perf_tests': [
-         ('cc_perftests', 2),
+         # crbug.com/698831
+         # ('cc_perftests', 2),
          ('load_library_perf_tests', 2),
          ('tracing_perftests', 2),
          ('media_perftests', 3)]
diff --git a/tools/perf/measurements/clock_domain_test.py b/tools/perf/measurements/clock_domain_test.py
index 3658d6f9..b24b23f4 100644
--- a/tools/perf/measurements/clock_domain_test.py
+++ b/tools/perf/measurements/clock_domain_test.py
@@ -4,8 +4,9 @@
 
 from telemetry import benchmark
 from telemetry.testing import tab_test_case
-from telemetry.timeline import trace_data
 from telemetry.timeline import tracing_config
+from tracing.trace_data import trace_data
+
 
 def GetSyncEvents(trace_part):
   return [x for x in trace_part if x['ph'] == 'c']
@@ -15,9 +16,7 @@
   # Don't run this test on Android; it's not supposed to work on Android
   # (since when doing Android tracing there are two different devices,
   # so the clock domains will be different)
-  # TODO(rnephew): Revert change from android to all once
-  # https://codereview.chromium.org/2741533003/ lands.
-  @benchmark.Disabled('all')
+  @benchmark.Disabled('android')
   def testTelemetryUsesChromeClockDomain(self):
 
     tracing_controller = self._browser.platform.tracing_controller
diff --git a/tools/variations/OWNERS b/tools/variations/OWNERS
index 5468518..986aba6 100644
--- a/tools/variations/OWNERS
+++ b/tools/variations/OWNERS
@@ -1,2 +1,4 @@
 asvitkine@chromium.org
 danduong@chromium.org
+
+# COMPONENT: Internals>Metrics
diff --git a/ui/android/BUILD.gn b/ui/android/BUILD.gn
index 8fde1069..515c97c6d 100644
--- a/ui/android/BUILD.gn
+++ b/ui/android/BUILD.gn
@@ -21,6 +21,8 @@
     "edge_effect_base.h",
     "edge_effect_l.cc",
     "edge_effect_l.h",
+    "event_forwarder.cc",
+    "event_forwarder.h",
     "overscroll_glow.cc",
     "overscroll_glow.h",
     "overscroll_refresh.cc",
@@ -40,6 +42,8 @@
     "ui_android_jni_registrar.h",
     "view_android.cc",
     "view_android.h",
+    "view_client.cc",
+    "view_client.h",
     "window_android.cc",
     "window_android.h",
     "window_android_compositor.h",
@@ -56,7 +60,9 @@
     "//cc/surfaces",
     "//cc/surfaces:surface_id",
     "//skia",
+    "//ui/base",
     "//ui/display",
+    "//ui/events",
     "//ui/gfx",
     "//ui/gfx/geometry",
     "//url",
@@ -79,6 +85,7 @@
 generate_jni("ui_android_jni_headers") {
   sources = [
     "java/src/org/chromium/ui/OverscrollRefreshHandler.java",
+    "java/src/org/chromium/ui/base/EventForwarder.java",
     "java/src/org/chromium/ui/base/ViewAndroidDelegate.java",
     "java/src/org/chromium/ui/base/WindowAndroid.java",
     "java/src/org/chromium/ui/display/DisplayAndroidManager.java",
@@ -170,9 +177,11 @@
     "java/src/org/chromium/ui/base/AndroidPermissionDelegate.java",
     "java/src/org/chromium/ui/base/Clipboard.java",
     "java/src/org/chromium/ui/base/DeviceFormFactor.java",
+    "java/src/org/chromium/ui/base/EventForwarder.java",
     "java/src/org/chromium/ui/base/LocalizationUtils.java",
     "java/src/org/chromium/ui/base/ResourceBundle.java",
     "java/src/org/chromium/ui/base/SelectFileDialog.java",
+    "java/src/org/chromium/ui/base/SPenSupport.java",
     "java/src/org/chromium/ui/base/TouchDevice.java",
     "java/src/org/chromium/ui/base/ViewAndroidDelegate.java",
     "java/src/org/chromium/ui/base/WindowAndroid.java",
@@ -245,6 +254,7 @@
     "overscroll_refresh_unittest.cc",
     "resources/resource_manager_impl_unittest.cc",
     "run_all_unittests.cc",
+    "view_android_unittests.cc",
   ]
   deps = [
     ":android",
@@ -258,6 +268,7 @@
     "//testing/gmock",
     "//testing/gtest",
     "//ui/base",
+    "//ui/events",
     "//ui/gfx",
     "//ui/resources:ui_test_pak",
   ]
diff --git a/ui/android/DEPS b/ui/android/DEPS
index 54add97..55642a35 100644
--- a/ui/android/DEPS
+++ b/ui/android/DEPS
@@ -10,8 +10,10 @@
   "+jni",
   "+skia/ext",
   "+third_party/skia",
+  "+ui/base/layout.h",
   "+ui/base/resource/resource_bundle.h",
   "+ui/base/ui_base_paths.h",
   "+ui/display",
+  "+ui/events/android/motion_event_android.h",
   "+ui/gfx",
 ]
diff --git a/ui/android/event_forwarder.cc b/ui/android/event_forwarder.cc
new file mode 100644
index 0000000..361ce342
--- /dev/null
+++ b/ui/android/event_forwarder.cc
@@ -0,0 +1,112 @@
+// 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/android/event_forwarder.h"
+
+#include "jni/EventForwarder_jni.h"
+#include "ui/android/view_android.h"
+#include "ui/android/view_client.h"
+#include "ui/events/android/motion_event_android.h"
+
+namespace ui {
+
+using base::android::JavaParamRef;
+using base::android::ScopedJavaLocalRef;
+
+EventForwarder::EventForwarder(ViewAndroid* view) : view_(view) {}
+
+EventForwarder::~EventForwarder() {
+  if (!java_obj_.is_null()) {
+    Java_EventForwarder_destroy(base::android::AttachCurrentThread(),
+                                java_obj_);
+    java_obj_.Reset();
+  }
+}
+
+ScopedJavaLocalRef<jobject> EventForwarder::GetJavaObject() {
+  if (java_obj_.is_null()) {
+    java_obj_.Reset(
+        Java_EventForwarder_create(base::android::AttachCurrentThread(),
+                                   reinterpret_cast<intptr_t>(this)));
+  }
+  return ScopedJavaLocalRef<jobject>(java_obj_);
+}
+
+jboolean EventForwarder::OnTouchEvent(JNIEnv* env,
+                                      const JavaParamRef<jobject>& obj,
+                                      const JavaParamRef<jobject>& motion_event,
+                                      jlong time_ms,
+                                      jint android_action,
+                                      jint pointer_count,
+                                      jint history_size,
+                                      jint action_index,
+                                      jfloat pos_x_0,
+                                      jfloat pos_y_0,
+                                      jfloat pos_x_1,
+                                      jfloat pos_y_1,
+                                      jint pointer_id_0,
+                                      jint pointer_id_1,
+                                      jfloat touch_major_0,
+                                      jfloat touch_major_1,
+                                      jfloat touch_minor_0,
+                                      jfloat touch_minor_1,
+                                      jfloat orientation_0,
+                                      jfloat orientation_1,
+                                      jfloat tilt_0,
+                                      jfloat tilt_1,
+                                      jfloat raw_pos_x,
+                                      jfloat raw_pos_y,
+                                      jint android_tool_type_0,
+                                      jint android_tool_type_1,
+                                      jint android_button_state,
+                                      jint android_meta_state,
+                                      jboolean is_touch_handle_event) {
+  ui::MotionEventAndroid::Pointer pointer0(
+      pointer_id_0, pos_x_0, pos_y_0, touch_major_0, touch_minor_0,
+      orientation_0, tilt_0, android_tool_type_0);
+  ui::MotionEventAndroid::Pointer pointer1(
+      pointer_id_1, pos_x_1, pos_y_1, touch_major_1, touch_minor_1,
+      orientation_1, tilt_1, android_tool_type_1);
+  ui::MotionEventAndroid event(
+      1.f / view_->GetDipScale(), env, motion_event.obj(), time_ms,
+      android_action, pointer_count, history_size, action_index,
+      0 /* action_button */, android_button_state, android_meta_state,
+      raw_pos_x - pos_x_0, raw_pos_y - pos_y_0, &pointer0, &pointer1);
+  return view_->OnTouchEvent(event, is_touch_handle_event);
+}
+
+void EventForwarder::OnMouseEvent(JNIEnv* env,
+                                  const JavaParamRef<jobject>& obj,
+                                  jlong time_ms,
+                                  jint android_action,
+                                  jfloat x,
+                                  jfloat y,
+                                  jint pointer_id,
+                                  jfloat orientation,
+                                  jfloat pressure,
+                                  jfloat tilt,
+                                  jint android_action_button,
+                                  jint android_button_state,
+                                  jint android_meta_state,
+                                  jint android_tool_type) {
+  // Construct a motion_event object minimally, only to convert the raw
+  // parameters to ui::MotionEvent values. Since we used only the cached values
+  // at index=0, it is okay to even pass a null event to the constructor.
+  ui::MotionEventAndroid::Pointer pointer(
+      pointer_id, x, y, 0.0f /* touch_major */, 0.0f /* touch_minor */,
+      orientation, tilt, android_tool_type);
+  ui::MotionEventAndroid event(
+      1.f / view_->GetDipScale(), env, nullptr /* event */, time_ms,
+      android_action, 1 /* pointer_count */, 0 /* history_size */,
+      0 /* action_index */, android_action_button, android_button_state,
+      android_meta_state, 0 /* raw_offset_x_pixels */,
+      0 /* raw_offset_y_pixels */, &pointer, nullptr);
+  view_->OnMouseEvent(event);
+}
+
+bool RegisterEventForwarder(JNIEnv* env) {
+  return RegisterNativesImpl(env);
+}
+
+}  // namespace ui
diff --git a/ui/android/event_forwarder.h b/ui/android/event_forwarder.h
new file mode 100644
index 0000000..9d9c2c0
--- /dev/null
+++ b/ui/android/event_forwarder.h
@@ -0,0 +1,81 @@
+// 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_ANDROID_EVENT_FORWARDER_H_
+#define UI_ANDROID_EVENT_FORWARDER_H_
+
+#include "base/android/scoped_java_ref.h"
+
+namespace ui {
+
+class ViewAndroid;
+
+class EventForwarder {
+ public:
+  ~EventForwarder();
+
+  jboolean OnTouchEvent(
+      JNIEnv* env,
+      const base::android::JavaParamRef<jobject>& obj,
+      const base::android::JavaParamRef<jobject>& motion_event,
+      jlong time_ms,
+      jint android_action,
+      jint pointer_count,
+      jint history_size,
+      jint action_index,
+      jfloat pos_x_0,
+      jfloat pos_y_0,
+      jfloat pos_x_1,
+      jfloat pos_y_1,
+      jint pointer_id_0,
+      jint pointer_id_1,
+      jfloat touch_major_0,
+      jfloat touch_major_1,
+      jfloat touch_minor_0,
+      jfloat touch_minor_1,
+      jfloat orientation_0,
+      jfloat orientation_1,
+      jfloat tilt_0,
+      jfloat tilt_1,
+      jfloat raw_pos_x,
+      jfloat raw_pos_y,
+      jint android_tool_type_0,
+      jint android_tool_type_1,
+      jint android_button_state,
+      jint android_meta_state,
+      jboolean is_touch_handle_event);
+
+  void OnMouseEvent(JNIEnv* env,
+                    const base::android::JavaParamRef<jobject>& obj,
+                    jlong time_ms,
+                    jint android_action,
+                    jfloat x,
+                    jfloat y,
+                    jint pointer_id,
+                    jfloat pressure,
+                    jfloat orientation,
+                    jfloat tilt,
+                    jint android_changed_button,
+                    jint android_button_state,
+                    jint android_meta_state,
+                    jint tool_type);
+
+ private:
+  friend class ViewAndroid;
+
+  explicit EventForwarder(ViewAndroid* view);
+
+  base::android::ScopedJavaLocalRef<jobject> GetJavaObject();
+
+  ViewAndroid* const view_;
+  base::android::ScopedJavaGlobalRef<jobject> java_obj_;
+
+  DISALLOW_COPY_AND_ASSIGN(EventForwarder);
+};
+
+bool RegisterEventForwarder(JNIEnv* env);
+
+}  // namespace ui
+
+#endif  // UI_ANDROID_EVENT_FORWARDER_H_
diff --git a/ui/android/java/src/org/chromium/ui/base/EventForwarder.java b/ui/android/java/src/org/chromium/ui/base/EventForwarder.java
new file mode 100644
index 0000000..ee52e3c
--- /dev/null
+++ b/ui/android/java/src/org/chromium/ui/base/EventForwarder.java
@@ -0,0 +1,195 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.ui.base;
+
+import android.annotation.TargetApi;
+import android.os.Build;
+import android.view.MotionEvent;
+
+import org.chromium.base.TraceEvent;
+import org.chromium.base.annotations.CalledByNative;
+import org.chromium.base.annotations.JNINamespace;
+
+/**
+ * Class used to forward view, input events down to native.
+ */
+@JNINamespace("ui")
+public class EventForwarder {
+    private long mNativeEventForwarder;
+
+    // Offsets for the events that passes through.
+    private float mCurrentTouchOffsetX;
+    private float mCurrentTouchOffsetY;
+
+    @CalledByNative
+    private static EventForwarder create(long nativeEventForwarder) {
+        return new EventForwarder(nativeEventForwarder);
+    }
+
+    private EventForwarder(long nativeEventForwarder) {
+        mNativeEventForwarder = nativeEventForwarder;
+    }
+
+    @CalledByNative
+    private void destroy() {
+        mNativeEventForwarder = 0;
+    }
+
+    /**
+     * @see View#onTouchEvent(MotionEvent)
+     */
+    public boolean onTouchEvent(MotionEvent event) {
+        // TODO(mustaq): Should we include MotionEvent.TOOL_TYPE_STYLUS here?
+        // crbug.com/592082
+        if (event.getToolType(0) == MotionEvent.TOOL_TYPE_MOUSE) {
+            // Mouse button info is incomplete on L and below
+            int apiVersion = Build.VERSION.SDK_INT;
+            if (apiVersion >= android.os.Build.VERSION_CODES.M) {
+                return onMouseEvent(event);
+            }
+        }
+
+        final boolean isTouchHandleEvent = false;
+        return sendTouchEvent(event, isTouchHandleEvent);
+    }
+
+    /**
+     * Called by PopupWindow-based touch handles.
+     * @param event the MotionEvent targeting the handle.
+     */
+    public boolean onTouchHandleEvent(MotionEvent event) {
+        final boolean isTouchHandleEvent = true;
+        return sendTouchEvent(event, isTouchHandleEvent);
+    }
+
+    private boolean sendTouchEvent(MotionEvent event, boolean isTouchHandleEvent) {
+        assert mNativeEventForwarder != 0;
+
+        TraceEvent.begin("sendTouchEvent");
+        try {
+            int eventAction = event.getActionMasked();
+
+            eventAction = SPenSupport.convertSPenEventAction(eventAction);
+
+            if (!isValidTouchEventActionForNative(eventAction)) return false;
+
+            // A zero offset is quite common, in which case the unnecessary copy should be avoided.
+            MotionEvent offset = null;
+            if (mCurrentTouchOffsetX != 0 || mCurrentTouchOffsetY != 0) {
+                offset = createOffsetMotionEvent(event);
+                event = offset;
+            }
+
+            final int pointerCount = event.getPointerCount();
+
+            float[] touchMajor = {
+                    event.getTouchMajor(), pointerCount > 1 ? event.getTouchMajor(1) : 0};
+            float[] touchMinor = {
+                    event.getTouchMinor(), pointerCount > 1 ? event.getTouchMinor(1) : 0};
+
+            for (int i = 0; i < 2; i++) {
+                if (touchMajor[i] < touchMinor[i]) {
+                    float tmp = touchMajor[i];
+                    touchMajor[i] = touchMinor[i];
+                    touchMinor[i] = tmp;
+                }
+            }
+
+            final boolean consumed = nativeOnTouchEvent(mNativeEventForwarder, event,
+                    event.getEventTime(), eventAction, pointerCount, event.getHistorySize(),
+                    event.getActionIndex(), event.getX(), event.getY(),
+                    pointerCount > 1 ? event.getX(1) : 0, pointerCount > 1 ? event.getY(1) : 0,
+                    event.getPointerId(0), pointerCount > 1 ? event.getPointerId(1) : -1,
+                    touchMajor[0], touchMajor[1], touchMinor[0], touchMinor[1],
+                    event.getOrientation(), pointerCount > 1 ? event.getOrientation(1) : 0,
+                    event.getAxisValue(MotionEvent.AXIS_TILT),
+                    pointerCount > 1 ? event.getAxisValue(MotionEvent.AXIS_TILT, 1) : 0,
+                    event.getRawX(), event.getRawY(), event.getToolType(0),
+                    pointerCount > 1 ? event.getToolType(1) : MotionEvent.TOOL_TYPE_UNKNOWN,
+                    event.getButtonState(), event.getMetaState(), isTouchHandleEvent);
+
+            if (offset != null) offset.recycle();
+            return consumed;
+        } finally {
+            TraceEvent.end("sendTouchEvent");
+        }
+    }
+
+    /**
+     * Sets the current amount to offset incoming touch events by (including MotionEvent and
+     * DragEvent). This is used to handle content moving and not lining up properly with the
+     * android input system.
+     * @param dx The X offset in pixels to shift touch events.
+     * @param dy The Y offset in pixels to shift touch events.
+     */
+    public void setCurrentTouchEventOffsets(float dx, float dy) {
+        mCurrentTouchOffsetX = dx;
+        mCurrentTouchOffsetY = dy;
+    }
+
+    private MotionEvent createOffsetMotionEvent(MotionEvent src) {
+        MotionEvent dst = MotionEvent.obtain(src);
+        dst.offsetLocation(mCurrentTouchOffsetX, mCurrentTouchOffsetY);
+        return dst;
+    }
+
+    private static boolean isValidTouchEventActionForNative(int eventAction) {
+        // Only these actions have any effect on gesture detection.  Other
+        // actions have no corresponding WebTouchEvent type and may confuse the
+        // touch pipline, so we ignore them entirely.
+        return eventAction == MotionEvent.ACTION_DOWN || eventAction == MotionEvent.ACTION_UP
+                || eventAction == MotionEvent.ACTION_CANCEL
+                || eventAction == MotionEvent.ACTION_MOVE
+                || eventAction == MotionEvent.ACTION_POINTER_DOWN
+                || eventAction == MotionEvent.ACTION_POINTER_UP;
+    }
+
+    @TargetApi(Build.VERSION_CODES.M)
+    public boolean onMouseEvent(MotionEvent event) {
+        TraceEvent.begin("sendMouseEvent");
+
+        MotionEvent offsetEvent = createOffsetMotionEvent(event);
+        try {
+            int eventAction = event.getActionMasked();
+
+            // For mousedown and mouseup events, we use ACTION_BUTTON_PRESS
+            // and ACTION_BUTTON_RELEASE respectively because they provide
+            // info about the changed-button.
+            if (eventAction == MotionEvent.ACTION_DOWN || eventAction == MotionEvent.ACTION_UP) {
+                return false;
+            }
+            sendMouseEvent(event.getEventTime(), eventAction, offsetEvent.getX(),
+                    offsetEvent.getY(), event.getPointerId(0), event.getPressure(0),
+                    event.getOrientation(0), event.getAxisValue(MotionEvent.AXIS_TILT, 0),
+                    event.getActionButton(), event.getButtonState(), event.getMetaState(),
+                    event.getToolType(0));
+            return true;
+        } finally {
+            offsetEvent.recycle();
+            TraceEvent.end("sendMouseEvent");
+        }
+    }
+
+    @TargetApi(Build.VERSION_CODES.M)
+    public void sendMouseEvent(long timeMs, int action, float x, float y, int pointerId,
+            float pressure, float orientation, float tilt, int actionButton, int buttonState,
+            int metaState, int toolType) {
+        assert mNativeEventForwarder != 0;
+        nativeOnMouseEvent(mNativeEventForwarder, timeMs, action, x, y, pointerId, pressure,
+                orientation, tilt, actionButton, buttonState, metaState, toolType);
+    }
+
+    // All touch events (including flings, scrolls etc) accept coordinates in physical pixels.
+    private native boolean nativeOnTouchEvent(long nativeEventForwarder, MotionEvent event,
+            long timeMs, int action, int pointerCount, int historySize, int actionIndex, float x0,
+            float y0, float x1, float y1, int pointerId0, int pointerId1, float touchMajor0,
+            float touchMajor1, float touchMinor0, float touchMinor1, float orientation0,
+            float orientation1, float tilt0, float tilt1, float rawX, float rawY,
+            int androidToolType0, int androidToolType1, int androidButtonState,
+            int androidMetaState, boolean isTouchHandleEvent);
+    private native void nativeOnMouseEvent(long nativeEventForwarder, long timeMs, int action,
+            float x, float y, int pointerId, float pressure, float orientation, float tilt,
+            int changedButton, int buttonState, int metaState, int toolType);
+}
diff --git a/content/public/android/java/src/org/chromium/content/browser/SPenSupport.java b/ui/android/java/src/org/chromium/ui/base/SPenSupport.java
similarity index 72%
rename from content/public/android/java/src/org/chromium/content/browser/SPenSupport.java
rename to ui/android/java/src/org/chromium/ui/base/SPenSupport.java
index 62fb90c6..a8930c8 100644
--- a/content/public/android/java/src/org/chromium/content/browser/SPenSupport.java
+++ b/ui/android/java/src/org/chromium/ui/base/SPenSupport.java
@@ -1,19 +1,20 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
+// Copyright 2017 The Chromium Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-package org.chromium.content.browser;
+package org.chromium.ui.base;
 
 import android.content.Context;
 import android.content.pm.FeatureInfo;
 import android.os.Build;
 import android.view.MotionEvent;
 
+import org.chromium.base.ContextUtils;
+
 /**
  * Support S-Pen event detection and conversion.
  */
 public final class SPenSupport {
-
     // These values are obtained from Samsung.
     private static final int SPEN_ACTION_DOWN = 211;
     private static final int SPEN_ACTION_UP = 212;
@@ -22,27 +23,26 @@
     private static Boolean sIsSPenSupported;
 
     /**
-     * @return Whether SPen is supported on the device.
+     * Initialize SPen support. This is done lazily at the first invocation of
+     * {@link #convertSPenEventAction(int)}.
      */
-    public static boolean isSPenSupported(Context context) {
-        if (sIsSPenSupported == null) {
-            sIsSPenSupported = detectSPenSupport(context);
-        }
-        return sIsSPenSupported.booleanValue();
-    }
+    private static void initialize() {
+        if (sIsSPenSupported != null) return;
 
-    private static boolean detectSPenSupport(Context context) {
         if (!"SAMSUNG".equalsIgnoreCase(Build.MANUFACTURER)) {
-            return false;
+            sIsSPenSupported = false;
+            return;
         }
 
+        Context context = ContextUtils.getApplicationContext();
         final FeatureInfo[] infos = context.getPackageManager().getSystemAvailableFeatures();
         for (FeatureInfo info : infos) {
             if ("com.sec.feature.spen_usp".equalsIgnoreCase(info.name)) {
-                return true;
+                sIsSPenSupported = true;
+                return;
             }
         }
-        return false;
+        sIsSPenSupported = false;
     }
 
     /**
@@ -53,6 +53,9 @@
      * @return Event action after the conversion.
      */
     public static int convertSPenEventAction(int eventActionMasked) {
+        if (sIsSPenSupported == null) initialize();
+        if (!sIsSPenSupported.booleanValue()) return eventActionMasked;
+
         // S-Pen support: convert to normal stylus event handling
         switch (eventActionMasked) {
             case SPEN_ACTION_DOWN:
diff --git a/ui/android/ui_android_jni_registrar.cc b/ui/android/ui_android_jni_registrar.cc
index 3c02c50..b1709f97 100644
--- a/ui/android/ui_android_jni_registrar.cc
+++ b/ui/android/ui_android_jni_registrar.cc
@@ -7,6 +7,7 @@
 #include "base/android/jni_android.h"
 #include "base/android/jni_registrar.h"
 #include "base/macros.h"
+#include "ui/android/event_forwarder.h"
 #include "ui/android/resources/resource_manager_impl.h"
 #include "ui/android/screen_android.h"
 #include "ui/android/view_android.h"
@@ -16,6 +17,7 @@
 
 static base::android::RegistrationMethod kAndroidRegisteredMethods[] = {
     {"DisplayAndroidManager", ui::RegisterScreenAndroid},
+    {"EventForwarder", ui::RegisterEventForwarder},
     {"ResourceManager", ui::ResourceManagerImpl::RegisterResourceManager},
     {"WindowAndroid", WindowAndroid::RegisterWindowAndroid},
 };
diff --git a/ui/android/view_android.cc b/ui/android/view_android.cc
index 1d5b1466..88ba8c0 100644
--- a/ui/android/view_android.cc
+++ b/ui/android/view_android.cc
@@ -8,11 +8,14 @@
 
 #include "base/android/jni_android.h"
 #include "base/android/jni_string.h"
+#include "base/containers/adapters.h"
 #include "cc/layers/layer.h"
 #include "jni/ViewAndroidDelegate_jni.h"
+#include "ui/android/event_forwarder.h"
+#include "ui/android/view_client.h"
 #include "ui/android/window_android.h"
-#include "ui/display/display.h"
-#include "ui/display/screen.h"
+#include "ui/base/layout.h"
+#include "ui/events/android/motion_event_android.h"
 #include "url/gurl.h"
 
 namespace ui {
@@ -71,11 +74,12 @@
   return view_.get(env);
 }
 
-ViewAndroid::ViewAndroid(const JavaRef<jobject>& delegate)
+ViewAndroid::ViewAndroid(ViewClient* view_client)
     : parent_(nullptr),
-      delegate_(base::android::AttachCurrentThread(), delegate.obj()) {}
+      client_(view_client),
+      layout_params_(LayoutParams::MatchParent()) {}
 
-ViewAndroid::ViewAndroid() : parent_(nullptr) {}
+ViewAndroid::ViewAndroid() : ViewAndroid(nullptr) {}
 
 ViewAndroid::~ViewAndroid() {
   RemoveFromParent();
@@ -88,21 +92,71 @@
 }
 
 void ViewAndroid::SetDelegate(const JavaRef<jobject>& delegate) {
+  // A ViewAndroid may have its own delegate or otherwise will use the next
+  // available parent's delegate.
   JNIEnv* env = base::android::AttachCurrentThread();
   delegate_ = JavaObjectWeakGlobalRef(env, delegate);
 }
 
+float ViewAndroid::GetDipScale() {
+  return ui::GetScaleFactorForNativeView(this);
+}
+
+ScopedJavaLocalRef<jobject> ViewAndroid::GetEventForwarder() {
+  if (!event_forwarder_) {
+    DCHECK(!ViewTreeHasEventForwarder(this))
+        << "Root of the ViewAndroid can have at most one handler.";
+    event_forwarder_.reset(new EventForwarder(this));
+  }
+  return event_forwarder_->GetJavaObject();
+}
+
 void ViewAndroid::AddChild(ViewAndroid* child) {
   DCHECK(child);
   DCHECK(std::find(children_.begin(), children_.end(), child) ==
          children_.end());
+  DCHECK(!SubtreeHasEventForwarder(child) || !ViewTreeHasEventForwarder(this))
+      << "Only one event handler is allowed.";
 
+  // The new child goes to the top, which is the end of the list.
   children_.push_back(child);
   if (child->parent_)
     child->RemoveFromParent();
   child->parent_ = this;
 }
 
+// static
+bool ViewAndroid::ViewTreeHasEventForwarder(ViewAndroid* view) {
+  ViewAndroid* v = view;
+  do {
+    if (v->has_event_forwarder())
+      return true;
+    v = v->parent_;
+  } while (v);
+  return SubtreeHasEventForwarder(view);
+}
+
+// static
+bool ViewAndroid::SubtreeHasEventForwarder(ViewAndroid* view) {
+  if (view->has_event_forwarder())
+    return true;
+  for (auto* child : view->children_) {
+    if (SubtreeHasEventForwarder(child))
+      return true;
+  }
+  return false;
+}
+
+void ViewAndroid::MoveToFront(ViewAndroid* child) {
+  DCHECK(child);
+  auto it = std::find(children_.begin(), children_.end(), child);
+  DCHECK(it != children_.end());
+
+  // Top element is placed at the end of the list.
+  if (*it != children_.back())
+    children_.splice(children_.end(), children_, it);
+}
+
 void ViewAndroid::RemoveFromParent() {
   if (parent_)
     parent_->RemoveChild(this);
@@ -124,15 +178,13 @@
   if (delegate.is_null())
     return;
 
-  float scale = display::Screen::GetScreen()
-                    ->GetDisplayNearestWindow(this)
-                    .device_scale_factor();
-  int left_margin = std::round(bounds.x() * scale);
-  int top_margin = std::round((content_offset().y() + bounds.y()) * scale);
+  float dip_scale = GetDipScale();
+  int left_margin = std::round(bounds.x() * dip_scale);
+  int top_margin = std::round((content_offset().y() + bounds.y()) * dip_scale);
   JNIEnv* env = base::android::AttachCurrentThread();
   Java_ViewAndroidDelegate_setViewPosition(
       env, delegate, anchor, bounds.x(), bounds.y(), bounds.width(),
-      bounds.height(), scale, left_margin, top_margin);
+      bounds.height(), dip_scale, left_margin, top_margin);
 }
 
 ScopedJavaLocalRef<jobject> ViewAndroid::GetContainerView() {
@@ -177,6 +229,10 @@
   layer_ = layer;
 }
 
+void ViewAndroid::SetLayout(ViewAndroid::LayoutParams params) {
+  layout_params_ = params;
+}
+
 bool ViewAndroid::StartDragAndDrop(const JavaRef<jstring>& jtext,
                                    const JavaRef<jobject>& jimage) {
   ScopedJavaLocalRef<jobject> delegate(GetViewAndroidDelegate());
@@ -227,4 +283,51 @@
                                                 is_main_frame);
 }
 
+bool ViewAndroid::OnTouchEvent(const MotionEventAndroid& event,
+                               bool for_touch_handle) {
+  return HitTest(
+      base::Bind(&ViewAndroid::SendTouchEventToClient, for_touch_handle),
+      event);
+}
+
+bool ViewAndroid::SendTouchEventToClient(bool for_touch_handle,
+                                         ViewClient* client,
+                                         const MotionEventAndroid& event) {
+  return client->OnTouchEvent(event, for_touch_handle);
+}
+
+bool ViewAndroid::OnMouseEvent(const MotionEventAndroid& event) {
+  return HitTest(base::Bind(&ViewAndroid::SendMouseEventToClient), event);
+}
+
+bool ViewAndroid::SendMouseEventToClient(ViewClient* client,
+                                         const MotionEventAndroid& event) {
+  return client->OnMouseEvent(event);
+}
+
+bool ViewAndroid::HitTest(ViewClientCallback send_to_client,
+                          const MotionEventAndroid& event) {
+  if (client_ && send_to_client.Run(client_, event))
+    return true;
+
+  if (!children_.empty()) {
+    std::unique_ptr<MotionEventAndroid> e(
+        event.Offset(-layout_params_.x, -layout_params_.y));
+
+    // Match from back to front for hit testing.
+    for (auto* child : base::Reversed(children_)) {
+      bool matched = child->layout_params_.match_parent;
+      if (!matched) {
+        gfx::Rect bound(child->layout_params_.x, child->layout_params_.y,
+                        child->layout_params_.width,
+                        child->layout_params_.height);
+        matched = bound.Contains(e->GetX(0), e->GetY(0));
+      }
+      if (matched && child->HitTest(send_to_client, *e))
+        return true;
+    }
+  }
+  return false;
+}
+
 }  // namespace ui
diff --git a/ui/android/view_android.h b/ui/android/view_android.h
index 0abd77c..18bd140 100644
--- a/ui/android/view_android.h
+++ b/ui/android/view_android.h
@@ -8,6 +8,7 @@
 #include <list>
 
 #include "base/android/jni_weak_ref.h"
+#include "base/bind.h"
 #include "base/memory/ref_counted.h"
 #include "ui/android/ui_android_export.h"
 #include "ui/gfx/geometry/rect_f.h"
@@ -19,11 +20,18 @@
 }
 
 namespace ui {
-
+class EventForwarder;
+class MotionEventAndroid;
+class ViewClient;
 class WindowAndroid;
 
 // A simple container for a UI layer.
 // At the root of the hierarchy is a WindowAndroid, when attached.
+// Dispatches input/view events coming from Java layer. Hit testing against
+// those events is implemented so that the |ViewClient| will be invoked
+// when the event got hit on the area defined by |layout_params_|.
+// Hit testing is done in the order of parent -> child, and from top
+// of the stack to back among siblings.
 class UI_ANDROID_EXPORT ViewAndroid {
  public:
   // Stores an anchored view to delete itself at the end of its lifetime
@@ -56,9 +64,32 @@
     // Default copy/assign disabled by move constructor.
   };
 
-  // A ViewAndroid may have its own delegate or otherwise will
-  // use the next available parent's delegate.
-  ViewAndroid(const base::android::JavaRef<jobject>& delegate);
+  // Layout parameters used to set the view's position and size.
+  // Position is in parent's coordinate space.
+  struct LayoutParams {
+    static LayoutParams MatchParent() { return {true, 0, 0, 0, 0}; }
+    static LayoutParams Normal(int x, int y, int width, int height) {
+      return {false, x, y, width, height};
+    };
+
+    bool match_parent;  // Bounds matches that of the parent if true.
+    int x;
+    int y;
+    int width;
+    int height;
+
+    LayoutParams(const LayoutParams& p) = default;
+
+   private:
+    LayoutParams(bool match_parent, int x, int y, int width, int height)
+        : match_parent(match_parent),
+          x(x),
+          y(y),
+          width(width),
+          height(height) {}
+  };
+
+  explicit ViewAndroid(ViewClient* view_client);
 
   ViewAndroid();
   virtual ~ViewAndroid();
@@ -83,12 +114,24 @@
 
   void SetDelegate(const base::android::JavaRef<jobject>& delegate);
 
-  // Adds this view as a child of another view.
+  // Gets (creates one if not present) Java object of the EventForwarder
+  // for a view tree in the view hierarchy including this node.
+  // Only one instance per the view tree is allowed.
+  base::android::ScopedJavaLocalRef<jobject> GetEventForwarder();
+
+  // Adds a child to this view.
   void AddChild(ViewAndroid* child);
 
+  // Moves the give child ViewAndroid to the front of the list so that it can be
+  // the first responder of events.
+  void MoveToFront(ViewAndroid* child);
+
   // Detaches this view from its parent.
   void RemoveFromParent();
 
+  // Sets the layout relative to parent. Used to do hit testing against events.
+  void SetLayout(LayoutParams params);
+
   bool StartDragAndDrop(const base::android::JavaRef<jstring>& jtext,
                         const base::android::JavaRef<jobject>& jimage);
 
@@ -106,12 +149,41 @@
   // This may return null.
   base::android::ScopedJavaLocalRef<jobject> GetContainerView();
 
+  float GetDipScale();
+
  protected:
   ViewAndroid* parent_;
 
  private:
+  friend class EventForwarder;
+  friend class ViewAndroidBoundsTest;
+
+  using ViewClientCallback =
+      const base::Callback<bool(ViewClient*, const MotionEventAndroid&)>;
+
+  bool OnTouchEvent(const MotionEventAndroid& event, bool for_touch_handle);
+  bool OnMouseEvent(const MotionEventAndroid& event);
+
   void RemoveChild(ViewAndroid* child);
 
+  bool HitTest(ViewClientCallback send_to_client,
+               const MotionEventAndroid& event);
+
+  static bool SendTouchEventToClient(bool for_touch_handle,
+                                     ViewClient* client,
+                                     const MotionEventAndroid& event);
+  static bool SendMouseEventToClient(ViewClient* client,
+                                     const MotionEventAndroid& event);
+
+  bool has_event_forwarder() const { return !!event_forwarder_; }
+
+  // Returns true if any node of the tree along the hierarchy (view's children
+  // and parents) already has |EventForwarder| attached to it.
+  static bool ViewTreeHasEventForwarder(ViewAndroid* view);
+
+  // Returns true if any children node (or self) has |EventForwarder|.
+  static bool SubtreeHasEventForwarder(ViewAndroid* view);
+
   // Returns the Java delegate for this view. This is used to delegate work
   // up to the embedding view (or the embedder that can deal with the
   // implementation details).
@@ -121,7 +193,15 @@
   std::list<ViewAndroid*> children_;
   scoped_refptr<cc::Layer> layer_;
   JavaObjectWeakGlobalRef delegate_;
-  gfx::Vector2dF content_offset_;  // in CSS pixel
+
+  ViewClient* const client_;
+
+  // Basic view layout information. Used to do hit testing deciding whether
+  // the passed events should be processed by the view.
+  LayoutParams layout_params_;
+
+  gfx::Vector2dF content_offset_;  // in CSS pixel.
+  std::unique_ptr<EventForwarder> event_forwarder_;
 
   DISALLOW_COPY_AND_ASSIGN(ViewAndroid);
 };
diff --git a/ui/android/view_android_unittests.cc b/ui/android/view_android_unittests.cc
new file mode 100644
index 0000000..56d39e8
--- /dev/null
+++ b/ui/android/view_android_unittests.cc
@@ -0,0 +1,169 @@
+// 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 "testing/gtest/include/gtest/gtest.h"
+#include "ui/android/event_forwarder.h"
+#include "ui/android/view_android.h"
+#include "ui/android/view_client.h"
+#include "ui/events/android/motion_event_android.h"
+
+namespace ui {
+
+using base::android::JavaParamRef;
+
+class TestViewClient : public ViewClient {
+ public:
+  TestViewClient() : handle_event_(true), called_(false) {}
+
+  void SetHandleEvent(bool handle_event) { handle_event_ = handle_event; }
+  bool OnTouchEvent(const MotionEventAndroid& event,
+                    bool for_touch_handle) override {
+    called_ = true;
+    return handle_event_;
+  }
+
+  bool EventHandled() { return called_ && handle_event_; }
+  bool EventCalled() { return called_; }
+  void Reset() { called_ = false; }
+
+ private:
+  bool handle_event_;  // Marks as event was consumed. True by default.
+  bool called_;
+};
+
+class ViewAndroidBoundsTest : public testing::Test {
+ public:
+  ViewAndroidBoundsTest()
+      : root_(nullptr),
+        view1_(&client1_),
+        view2_(&client2_),
+        view3_(&client3_) {
+    root_.GetEventForwarder();
+    root_.SetLayout(ViewAndroid::LayoutParams::MatchParent());
+  }
+
+  void Reset() {
+    client1_.Reset();
+    client2_.Reset();
+    client3_.Reset();
+  }
+
+  void GenerateTouchEventAt(float x, float y) {
+    ui::MotionEventAndroid::Pointer pointer0(0, x, y, 0, 0, 0, 0, 0);
+    ui::MotionEventAndroid::Pointer pointer1(0, 0, 0, 0, 0, 0, 0, 0);
+    ui::MotionEventAndroid event(1.f, nullptr, JavaParamRef<jobject>(nullptr),
+                                 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, &pointer0,
+                                 &pointer1);
+    root_.OnTouchEvent(event, false);
+  }
+
+  void ExpectHit(const TestViewClient& hitClient) {
+    TestViewClient* clients[3] = {&client1_, &client2_, &client3_};
+    for (auto* client : clients) {
+      if (&hitClient == client)
+        EXPECT_TRUE(client->EventHandled());
+      else
+        EXPECT_FALSE(client->EventHandled());
+    }
+    Reset();
+  }
+
+  ViewAndroid root_;
+  ViewAndroid view1_;
+  ViewAndroid view2_;
+  ViewAndroid view3_;
+  TestViewClient client1_;
+  TestViewClient client2_;
+  TestViewClient client3_;
+};
+
+TEST_F(ViewAndroidBoundsTest, MatchesViewInFront) {
+  view1_.SetLayout(ViewAndroid::LayoutParams::Normal(50, 50, 400, 600));
+  view2_.SetLayout(ViewAndroid::LayoutParams::Normal(50, 50, 400, 600));
+  root_.AddChild(&view2_);
+  root_.AddChild(&view1_);
+
+  GenerateTouchEventAt(100.f, 100.f);
+  ExpectHit(client1_);
+
+  // View 2 moves up to the top, and events should hit it from now.
+  root_.MoveToFront(&view2_);
+  GenerateTouchEventAt(100.f, 100.f);
+  ExpectHit(client2_);
+}
+
+TEST_F(ViewAndroidBoundsTest, MatchesViewArea) {
+  view1_.SetLayout(ViewAndroid::LayoutParams::Normal(50, 50, 200, 200));
+  view2_.SetLayout(ViewAndroid::LayoutParams::Normal(20, 20, 400, 600));
+
+  root_.AddChild(&view2_);
+  root_.AddChild(&view1_);
+
+  // Falls within |view1_|'s bounds
+  GenerateTouchEventAt(100.f, 100.f);
+  ExpectHit(client1_);
+
+  // Falls within |view2_|'s bounds
+  GenerateTouchEventAt(300.f, 400.f);
+  ExpectHit(client2_);
+}
+
+TEST_F(ViewAndroidBoundsTest, MatchesViewAfterMove) {
+  view1_.SetLayout(ViewAndroid::LayoutParams::Normal(50, 50, 200, 200));
+  view2_.SetLayout(ViewAndroid::LayoutParams::Normal(20, 20, 400, 600));
+  root_.AddChild(&view2_);
+  root_.AddChild(&view1_);
+
+  GenerateTouchEventAt(100.f, 100.f);
+  ExpectHit(client1_);
+
+  view1_.SetLayout(ViewAndroid::LayoutParams::Normal(150, 150, 200, 200));
+  GenerateTouchEventAt(100.f, 100.f);
+  ExpectHit(client2_);
+}
+
+TEST_F(ViewAndroidBoundsTest, MatchesViewSizeOfkMatchParent) {
+  view1_.SetLayout(ViewAndroid::LayoutParams::Normal(20, 20, 400, 600));
+  view3_.SetLayout(ViewAndroid::LayoutParams::MatchParent());
+  view2_.SetLayout(ViewAndroid::LayoutParams::Normal(50, 50, 200, 200));
+
+  root_.AddChild(&view1_);
+  root_.AddChild(&view2_);
+  view1_.AddChild(&view3_);
+
+  GenerateTouchEventAt(100.f, 100.f);
+  ExpectHit(client2_);
+
+  GenerateTouchEventAt(300.f, 400.f);
+  ExpectHit(client1_);
+
+  client1_.SetHandleEvent(false);
+  GenerateTouchEventAt(300.f, 400.f);
+  EXPECT_TRUE(client1_.EventCalled());
+  ExpectHit(client3_);
+}
+
+TEST_F(ViewAndroidBoundsTest, MatchesViewsWithOffset) {
+  view1_.SetLayout(ViewAndroid::LayoutParams::Normal(10, 20, 150, 100));
+  view2_.SetLayout(ViewAndroid::LayoutParams::Normal(20, 30, 40, 30));
+  view3_.SetLayout(ViewAndroid::LayoutParams::Normal(70, 30, 40, 30));
+
+  root_.AddChild(&view1_);
+  view1_.AddChild(&view2_);
+  view1_.AddChild(&view3_);
+
+  GenerateTouchEventAt(70, 30);
+  ExpectHit(client1_);
+
+  client1_.SetHandleEvent(false);
+  GenerateTouchEventAt(40, 60);
+  EXPECT_TRUE(client1_.EventCalled());
+  ExpectHit(client2_);
+
+  GenerateTouchEventAt(100, 70);
+  EXPECT_TRUE(client1_.EventCalled());
+  ExpectHit(client3_);
+}
+
+}  // namespace ui
diff --git a/ui/android/view_client.cc b/ui/android/view_client.cc
new file mode 100644
index 0000000..43c8339
--- /dev/null
+++ b/ui/android/view_client.cc
@@ -0,0 +1,18 @@
+// 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/android/view_client.h"
+
+namespace ui {
+
+bool ViewClient::OnTouchEvent(const MotionEventAndroid& event,
+                              bool for_touch_handle) {
+  return false;
+}
+
+bool ViewClient::OnMouseEvent(const MotionEventAndroid& event) {
+  return false;
+}
+
+}  // namespace ui
diff --git a/ui/android/view_client.h b/ui/android/view_client.h
new file mode 100644
index 0000000..cf7be4d
--- /dev/null
+++ b/ui/android/view_client.h
@@ -0,0 +1,28 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_ANDROID_VIEW_CLIENT_H_
+#define UI_ANDROID_VIEW_CLIENT_H_
+
+#include "ui/android/ui_android_export.h"
+
+namespace ui {
+
+class MotionEventAndroid;
+
+// Client interface used to forward events from Java to native views.
+// Calls are dispatched to its children along the hierarchy of ViewAndroid.
+// Use bool return type to stop propagating the call i.e. overriden method
+// should return true to indicate that the event was handled and stop
+// the processing.
+class UI_ANDROID_EXPORT ViewClient {
+ public:
+  virtual bool OnTouchEvent(const MotionEventAndroid& event,
+                            bool for_touch_handle);
+  virtual bool OnMouseEvent(const MotionEventAndroid& event);
+};
+
+}  // namespace ui
+
+#endif  // UI_ANDROID_VIEW_CLIENT_H_
diff --git a/ui/arc/notification/arc_notification_manager.cc b/ui/arc/notification/arc_notification_manager.cc
index b4619203..556f18a 100644
--- a/ui/arc/notification/arc_notification_manager.cc
+++ b/ui/arc/notification/arc_notification_manager.cc
@@ -8,7 +8,7 @@
 #include <utility>
 
 #include "ash/common/system/toast/toast_manager.h"
-#include "ash/common/wm_shell.h"
+#include "ash/shell.h"
 #include "base/memory/ptr_util.h"
 #include "base/stl_util.h"
 #include "base/strings/utf_string_conversions.h"
@@ -245,12 +245,12 @@
       base::UTF8ToUTF16(data->text.has_value() ? *data->text : std::string()));
   const base::string16 dismiss_text16(base::UTF8ToUTF16(
       data->dismiss_text.has_value() ? *data->dismiss_text : std::string()));
-  ash::WmShell::Get()->toast_manager()->Show(
+  ash::Shell::GetInstance()->toast_manager()->Show(
       ash::ToastData(data->id, text16, data->duration, dismiss_text16));
 }
 
 void ArcNotificationManager::OnToastCancelled(mojom::ArcToastDataPtr data) {
-  ash::WmShell::Get()->toast_manager()->Cancel(data->id);
+  ash::Shell::GetInstance()->toast_manager()->Cancel(data->id);
 }
 
 }  // namespace arc
diff --git a/ui/base/class_property.h b/ui/base/class_property.h
index 6621e8e..4df49894 100644
--- a/ui/base/class_property.h
+++ b/ui/base/class_property.h
@@ -30,7 +30,7 @@
 //
 //    // Use this to define an exported property whose value is a heap
 //    // allocated object, and has to be owned and freed by the class.
-//    DEFINE_OWNED_UI_CLASS_PROPERTY_KEY(gfx::Rect, kRestoreBoundsKey, NULL);
+//    DEFINE_OWNED_UI_CLASS_PROPERTY_KEY(gfx::Rect, kRestoreBoundsKey, nullptr);
 //
 //    // Use this to define a non exported property that is primitive,
 //    // or a pointer you don't want to automatically deleted, and is used
diff --git a/ui/base/dragdrop/drag_utils.cc b/ui/base/dragdrop/drag_utils.cc
index 45c6f07..4b63fbbacc 100644
--- a/ui/base/dragdrop/drag_utils.cc
+++ b/ui/base/dragdrop/drag_utils.cc
@@ -4,12 +4,98 @@
 
 #include "ui/base/dragdrop/drag_utils.h"
 
+#include "base/files/file_util.h"
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/strings/utf_string_conversions.h"
+#include "build/build_config.h"
 #include "ui/base/dragdrop/os_exchange_data.h"
 #include "ui/gfx/canvas.h"
-#include "ui/gfx/image/image_skia.h"
+#include "ui/gfx/font_list.h"
+#include "ui/gfx/geometry/point.h"
+#include "ui/gfx/geometry/rect.h"
+#include "ui/gfx/geometry/size.h"
+#include "ui/gfx/image/canvas_image_source.h"
+#include "url/gurl.h"
 
 namespace drag_utils {
 
+namespace {
+
+// Maximum width of the link drag image in pixels.
+static const int kLinkDragImageVPadding = 3;
+
+// File dragging pixel measurements
+static const int kFileDragImageMaxWidth = 200;
+static const SkColor kFileDragImageTextColor = SK_ColorBLACK;
+
+class FileDragImageSource : public gfx::CanvasImageSource {
+ public:
+  FileDragImageSource(const base::FilePath& file_name,
+                      const gfx::ImageSkia& icon)
+      : CanvasImageSource(CalculateSize(icon), false),
+        file_name_(file_name),
+        icon_(icon) {
+  }
+
+  ~FileDragImageSource() override {}
+
+  // Overridden from gfx::CanvasImageSource.
+  void Draw(gfx::Canvas* canvas) override {
+    if (!icon_.isNull()) {
+      // Paint the icon.
+      canvas->DrawImageInt(icon_, (size().width() - icon_.width()) / 2, 0);
+    }
+
+    base::string16 name = file_name_.BaseName().LossyDisplayName();
+    const int flags = gfx::Canvas::TEXT_ALIGN_CENTER;
+    const gfx::FontList font_list;
+#if defined(OS_WIN)
+    // Paint the file name. We inset it one pixel to allow room for the halo.
+    const gfx::Rect rect(1, icon_.height() + kLinkDragImageVPadding + 1,
+                         size().width() - 2, font_list.GetHeight());
+    canvas->DrawStringRectWithHalo(name, font_list, kFileDragImageTextColor,
+                                   SK_ColorWHITE, rect, flags);
+#else
+    // NO_SUBPIXEL_RENDERING is required when drawing to a non-opaque canvas.
+    const gfx::Rect rect(0, icon_.height() + kLinkDragImageVPadding,
+                         size().width(), font_list.GetHeight());
+    canvas->DrawStringRectWithFlags(name, font_list, kFileDragImageTextColor,
+                                    rect,
+                                    flags | gfx::Canvas::NO_SUBPIXEL_RENDERING);
+#endif
+  }
+
+ private:
+  gfx::Size CalculateSize(const gfx::ImageSkia& icon) const {
+    const int width = kFileDragImageMaxWidth;
+    // Add +2 here to allow room for the halo.
+    const int height = gfx::FontList().GetHeight() + icon.height() +
+                       kLinkDragImageVPadding + 2;
+    return gfx::Size(width, height);
+  }
+
+  const base::FilePath file_name_;
+  const gfx::ImageSkia icon_;
+
+  DISALLOW_COPY_AND_ASSIGN(FileDragImageSource);
+};
+
+}  // namespace
+
+void CreateDragImageForFile(const base::FilePath& file_name,
+                            const gfx::ImageSkia& icon,
+                            ui::OSExchangeData* data_object) {
+  DCHECK(data_object);
+  gfx::CanvasImageSource* source = new FileDragImageSource(file_name, icon);
+  gfx::Size size = source->size();
+  // ImageSkia takes ownership of |source|.
+  gfx::ImageSkia image = gfx::ImageSkia(source, size);
+
+  gfx::Vector2d cursor_offset(size.width() / 2, kLinkDragImageVPadding);
+  SetDragImageOnDataObject(image, cursor_offset, data_object);
+}
+
 void SetDragImageOnDataObject(const gfx::Canvas& canvas,
                               const gfx::Vector2d& cursor_offset,
                               ui::OSExchangeData* data_object) {
diff --git a/ui/base/dragdrop/drag_utils.h b/ui/base/dragdrop/drag_utils.h
index 845ebbc..d9382161 100644
--- a/ui/base/dragdrop/drag_utils.h
+++ b/ui/base/dragdrop/drag_utils.h
@@ -19,9 +19,18 @@
 namespace ui {
 class OSExchangeData;
 }
+using ui::OSExchangeData;
 
 namespace drag_utils {
 
+// Creates a dragging image to be displayed when the user drags a file from
+// Chrome (via the download manager, for example). The drag image is set into
+// the supplied data_object. |file_name| can be a full path, but the directory
+// portion will be truncated in the drag image. |icon| can be empty.
+UI_BASE_EXPORT void CreateDragImageForFile(const base::FilePath& file_name,
+                                           const gfx::ImageSkia& icon,
+                                           ui::OSExchangeData* data_object);
+
 // Sets the drag image on data_object from the supplied canvas.
 // |cursor_offset| gives the location of the hotspot for the drag image.
 UI_BASE_EXPORT void SetDragImageOnDataObject(const gfx::Canvas& canvas,
diff --git a/ui/base/text/OWNERS b/ui/base/text/OWNERS
index b9e8da9..b999cb1 100644
--- a/ui/base/text/OWNERS
+++ b/ui/base/text/OWNERS
@@ -1 +1,3 @@
 asvitkine@chromium.org
+
+# COMPONENT: UI>Localization
diff --git a/ui/events/android/motion_event_android.cc b/ui/events/android/motion_event_android.cc
index 8e99aa3..4af424d 100644
--- a/ui/events/android/motion_event_android.cc
+++ b/ui/events/android/motion_event_android.cc
@@ -15,6 +15,7 @@
 #include "ui/events/event_utils.h"
 
 using base::android::AttachCurrentThread;
+using base::android::ScopedJavaLocalRef;
 using namespace JNI_MotionEvent;
 
 namespace ui {
@@ -188,6 +189,7 @@
                                        jint pointer_count,
                                        jint history_size,
                                        jint action_index,
+                                       jint android_action_button,
                                        jint android_button_state,
                                        jint android_meta_state,
                                        jfloat raw_offset_x_pixels,
@@ -200,6 +202,7 @@
       cached_pointer_count_(pointer_count),
       cached_history_size_(ToValidHistorySize(history_size, cached_action_)),
       cached_action_index_(action_index),
+      cached_action_button_(android_action_button),
       cached_button_state_(FromAndroidButtonState(android_button_state)),
       cached_flags_(ToEventFlags(android_meta_state, android_button_state)),
       cached_raw_position_offset_(ToDips(raw_offset_x_pixels),
@@ -217,6 +220,32 @@
     cached_pointers_[1] = FromAndroidPointer(*pointer1);
 }
 
+MotionEventAndroid::MotionEventAndroid(const MotionEventAndroid& e)
+    : event_(e.event_),
+      pix_to_dip_(e.pix_to_dip_),
+      cached_time_(e.cached_time_),
+      cached_action_(e.cached_action_),
+      cached_pointer_count_(e.cached_pointer_count_),
+      cached_history_size_(e.cached_history_size_),
+      cached_action_index_(e.cached_action_index_),
+      cached_action_button_(e.cached_action_button_),
+      cached_button_state_(e.cached_button_state_),
+      cached_flags_(e.cached_flags_),
+      cached_raw_position_offset_(e.cached_raw_position_offset_),
+      unique_event_id_(ui::GetNextTouchEventId()) {
+  for (size_t i = 0; i < cached_pointer_count_; i++)
+    cached_pointers_[i] = e.cached_pointers_[i];
+}
+
+std::unique_ptr<MotionEventAndroid> MotionEventAndroid::Offset(float x,
+                                                               float y) const {
+  std::unique_ptr<MotionEventAndroid> event(new MotionEventAndroid(*this));
+  for (size_t i = 0; i < cached_pointer_count_; i++) {
+    event->cached_pointers_[i] = OffsetCachedPointer(cached_pointers_[i], x, y);
+  }
+  return event;
+}
+
 MotionEventAndroid::~MotionEventAndroid() {
 }
 
@@ -228,6 +257,14 @@
   return cached_action_;
 }
 
+int MotionEventAndroid::GetActionButton() const {
+  return cached_action_button_;
+}
+
+ScopedJavaLocalRef<jobject> MotionEventAndroid::GetJavaObject() const {
+  return ScopedJavaLocalRef<jobject>(event_);
+}
+
 int MotionEventAndroid::GetActionIndex() const {
   DCHECK(cached_action_ == MotionEvent::ACTION_POINTER_UP ||
          cached_action_ == MotionEvent::ACTION_POINTER_DOWN)
@@ -388,4 +425,20 @@
   return result;
 }
 
-}  // namespace content
+MotionEventAndroid::CachedPointer MotionEventAndroid::OffsetCachedPointer(
+    const CachedPointer& pointer,
+    float x,
+    float y) const {
+  CachedPointer result;
+  result.id = pointer.id;
+  result.position = gfx::PointF(pointer.position.x() + ToDips(x),
+                                pointer.position.y() + ToDips(y));
+  result.touch_major = pointer.touch_major;
+  result.touch_minor = pointer.touch_minor;
+  result.orientation = pointer.orientation;
+  result.tilt = pointer.tilt;
+  result.tool_type = pointer.tool_type;
+  return result;
+}
+
+}  // namespace ui
diff --git a/ui/events/android/motion_event_android.h b/ui/events/android/motion_event_android.h
index 00fc4216..305fe6a 100644
--- a/ui/events/android/motion_event_android.h
+++ b/ui/events/android/motion_event_android.h
@@ -54,6 +54,7 @@
                      jint pointer_count,
                      jint history_size,
                      jint action_index,
+                     jint android_action_button,
                      jint android_button_state,
                      jint meta_state,
                      jfloat raw_offset_x_pixels,
@@ -62,6 +63,8 @@
                      const Pointer* const pointer1);
   ~MotionEventAndroid() override;
 
+  std::unique_ptr<MotionEventAndroid> Offset(float x, float y) const;
+
   // ui::MotionEvent methods.
   uint32_t GetUniqueEventId() const override;
   Action GetAction() const override;
@@ -91,11 +94,17 @@
   int GetButtonState() const override;
   int GetFlags() const override;
 
+  int GetActionButton() const;
+  base::android::ScopedJavaLocalRef<jobject> GetJavaObject() const;
+
  private:
   struct CachedPointer;
 
   float ToDips(float pixels) const;
   CachedPointer FromAndroidPointer(const Pointer& pointer) const;
+  CachedPointer OffsetCachedPointer(const CachedPointer& pointer,
+                                    float x,
+                                    float y) const;
 
   // Cache pointer coords, id's and major lengths for the most common
   // touch-related scenarios, i.e., scrolling and pinching.  This prevents
@@ -114,6 +123,7 @@
   const size_t cached_pointer_count_;
   const size_t cached_history_size_;
   const int cached_action_index_;
+  const int cached_action_button_;
   const int cached_button_state_;
   const int cached_flags_;
   const gfx::Vector2dF cached_raw_position_offset_;
@@ -131,7 +141,9 @@
   // A unique identifier for the Android motion event.
   const uint32_t unique_event_id_;
 
-  DISALLOW_COPY_AND_ASSIGN(MotionEventAndroid);
+  // Disallow copy/assign.
+  MotionEventAndroid(const MotionEventAndroid& e);  // private ctor
+  void operator=(const MotionEventAndroid&) = delete;
 };
 
 }  // namespace content
diff --git a/ui/events/android/motion_event_android_unittest.cc b/ui/events/android/motion_event_android_unittest.cc
index 7e619b5d..5ba65ca 100644
--- a/ui/events/android/motion_event_android_unittest.cc
+++ b/ui/events/android/motion_event_android_unittest.cc
@@ -17,6 +17,7 @@
 namespace {
 const float kPixToDip = 0.5f;
 
+int kAndroidActionButton = 0;
 int kAndroidActionDown = AMOTION_EVENT_ACTION_DOWN;
 int kAndroidActionPointerDown = AMOTION_EVENT_ACTION_POINTER_DOWN;
 int kAndroidAltKeyDown = AMETA_ALT_ON;
@@ -47,20 +48,11 @@
   int pointer_count = 2;
   int history_size = 0;
   int action_index = -1;
-  MotionEventAndroid event(kPixToDip,
-                           base::android::AttachCurrentThread(),
-                           nullptr,
-                           event_time_ms,
-                           kAndroidActionDown,
-                           pointer_count,
-                           history_size,
-                           action_index,
-                           kAndroidButtonPrimary,
-                           kAndroidAltKeyDown,
-                           raw_offset,
-                           -raw_offset,
-                           &p0,
-                           &p1);
+  MotionEventAndroid event(
+      kPixToDip, base::android::AttachCurrentThread(), nullptr, event_time_ms,
+      kAndroidActionDown, pointer_count, history_size, action_index,
+      kAndroidActionButton, kAndroidButtonPrimary, kAndroidAltKeyDown,
+      raw_offset, -raw_offset, &p0, &p1);
 
   EXPECT_EQ(MotionEvent::ACTION_DOWN, event.GetAction());
   EXPECT_EQ(event_time, event.GetEventTime());
@@ -94,20 +86,9 @@
   const int pointer_count = 1;
   MotionEventAndroid::Pointer p0(
       1, 13.7f, -7.13f, 5.3f, 1.2f, 0.1f, 0.2f, kAndroidToolTypeFinger);
-  MotionEventAndroid event(kPixToDip,
-                           base::android::AttachCurrentThread(),
-                           nullptr,
-                           0,
-                           kAndroidActionDown,
-                           pointer_count,
-                           0,
-                           0,
-                           0,
-                           0,
-                           0,
-                           0,
-                           &p0,
-                           nullptr);
+  MotionEventAndroid event(kPixToDip, base::android::AttachCurrentThread(),
+                           nullptr, 0, kAndroidActionDown, pointer_count, 0, 0,
+                           0, 0, 0, 0, 0, &p0, nullptr);
 
   std::unique_ptr<MotionEvent> clone = event.Clone();
   EXPECT_EQ(ui::test::ToString(event), ui::test::ToString(*clone));
@@ -118,20 +99,9 @@
   const int pointer_count = 1;
   MotionEventAndroid::Pointer p0(
       1, 13.7f, -7.13f, 5.3f, 1.2f, 0.1f, 0.2f, kAndroidToolTypeFinger);
-  MotionEventAndroid event(kPixToDip,
-                           base::android::AttachCurrentThread(),
-                           nullptr,
-                           event_time_ms,
-                           kAndroidActionDown,
-                           pointer_count,
-                           0,
-                           0,
-                           0,
-                           0,
-                           0,
-                           0,
-                           &p0,
-                           nullptr);
+  MotionEventAndroid event(kPixToDip, base::android::AttachCurrentThread(),
+                           nullptr, event_time_ms, kAndroidActionDown,
+                           pointer_count, 0, 0, 0, 0, 0, 0, 0, &p0, nullptr);
 
   std::unique_ptr<MotionEvent> cancel_event = event.Cancel();
   EXPECT_EQ(MotionEvent::ACTION_CANCEL, cancel_event->GetAction());
@@ -151,20 +121,9 @@
   float orientation1 = std::numeric_limits<float>::quiet_NaN();
   MotionEventAndroid::Pointer p0(0, 0, 0, 0, 0, orientation0, 0, 0);
   MotionEventAndroid::Pointer p1(1, 0, 0, 0, 0, orientation1, 0, 0);
-  MotionEventAndroid event(kPixToDip,
-                           base::android::AttachCurrentThread(),
-                           nullptr,
-                           0,
-                           kAndroidActionDown,
-                           pointer_count,
-                           0,
-                           0,
-                           0,
-                           0,
-                           0,
-                           0,
-                           &p0,
-                           &p1);
+  MotionEventAndroid event(kPixToDip, base::android::AttachCurrentThread(),
+                           nullptr, 0, kAndroidActionDown, pointer_count, 0, 0,
+                           0, 0, 0, 0, 0, &p0, &p1);
 
   EXPECT_EQ(0.f, event.GetOrientation(0));
   EXPECT_EQ(0.f, event.GetOrientation(1));
@@ -174,20 +133,9 @@
   int pointer_count = 1;
   size_t history_size = 5;
   MotionEventAndroid::Pointer p0(0, 0, 0, 0, 0, 0, 0, 0);
-  MotionEventAndroid event(kPixToDip,
-                           base::android::AttachCurrentThread(),
-                           nullptr,
-                           0,
-                           kAndroidActionDown,
-                           pointer_count,
-                           history_size,
-                           0,
-                           0,
-                           0,
-                           0,
-                           0,
-                           &p0,
-                           nullptr);
+  MotionEventAndroid event(kPixToDip, base::android::AttachCurrentThread(),
+                           nullptr, 0, kAndroidActionDown, pointer_count,
+                           history_size, 0, 0, 0, 0, 0, 0, &p0, nullptr);
 
   EXPECT_EQ(0U, event.GetHistorySize());
 }
@@ -200,20 +148,9 @@
   int pointer_count = 2;
   int history_size = 0;
   int action_index = 1;
-  MotionEventAndroid event(kPixToDip,
-                           base::android::AttachCurrentThread(),
-                           nullptr,
-                           0,
-                           kAndroidActionPointerDown,
-                           pointer_count,
-                           history_size,
-                           action_index,
-                           0,
-                           0,
-                           0,
-                           0,
-                           &p0,
-                           &p1);
+  MotionEventAndroid event(kPixToDip, base::android::AttachCurrentThread(),
+                           nullptr, 0, kAndroidActionPointerDown, pointer_count,
+                           history_size, action_index, 0, 0, 0, 0, 0, &p0, &p1);
 
   EXPECT_EQ(MotionEvent::ACTION_POINTER_DOWN, event.GetAction());
   EXPECT_EQ(action_index, event.GetActionIndex());
diff --git a/ui/gfx/OWNERS b/ui/gfx/OWNERS
index 0905260..ec4cedb 100644
--- a/ui/gfx/OWNERS
+++ b/ui/gfx/OWNERS
@@ -29,3 +29,5 @@
 # If you're doing structural changes get a review from one of the OWNERS.
 per-file *.gyp*=*
 per-file BUILD.gn=*
+
+# COMPONENT: UI>GFX
diff --git a/ui/gl/gpu_timing.cc b/ui/gl/gpu_timing.cc
index d83914c..5a9b49c3 100644
--- a/ui/gl/gpu_timing.cc
+++ b/ui/gl/gpu_timing.cc
@@ -22,7 +22,7 @@
 }
 
 int32_t QueryTimestampBits() {
-  GLint timestamp_bits;
+  GLint timestamp_bits = 0;
   glGetQueryiv(GL_TIMESTAMP, GL_QUERY_COUNTER_BITS, &timestamp_bits);
   return static_cast<int32_t>(timestamp_bits);
 }
diff --git a/ui/ozone/platform/x11/DEPS b/ui/ozone/platform/x11/DEPS
new file mode 100644
index 0000000..8d59099
--- /dev/null
+++ b/ui/ozone/platform/x11/DEPS
@@ -0,0 +1,3 @@
+include_rules = [
+  "+ui/base/x",
+]
diff --git a/ui/ozone/platform/x11/ozone_platform_x11.cc b/ui/ozone/platform/x11/ozone_platform_x11.cc
index ae74f0e..03ac7cc9 100644
--- a/ui/ozone/platform/x11/ozone_platform_x11.cc
+++ b/ui/ozone/platform/x11/ozone_platform_x11.cc
@@ -12,6 +12,7 @@
 #include "base/command_line.h"
 #include "base/memory/ptr_util.h"
 #include "base/strings/utf_string_conversions.h"
+#include "ui/base/x/x11_util.h"
 #include "ui/display/fake_display_delegate.h"
 #include "ui/events/platform/x11/x11_event_source_libevent.h"
 #include "ui/ozone/common/stub_overlay_manager.h"
@@ -29,22 +30,17 @@
 
 namespace {
 
-// Returns true if we should operate in Mus mode.
+// Returns true if Ozone is running inside the mus process.
 bool RunningInsideMus() {
   bool has_channel_handle = base::CommandLine::ForCurrentProcess()->HasSwitch(
       "mojo-platform-channel-handle");
   return has_channel_handle;
 }
 
-// Singleton OzonePlatform implementation for Linux X11 platform.
+// Singleton OzonePlatform implementation for X11 platform.
 class OzonePlatformX11 : public OzonePlatform {
  public:
-  OzonePlatformX11() {
-    // If we're running in Mus mode both the UI and GPU components of Ozone will
-    // be running in the same process. Enable X11 concurrent thread support.
-    if (RunningInsideMus())
-      XInitThreads();
-  }
+  OzonePlatformX11() {}
 
   ~OzonePlatformX11() override {}
 
@@ -88,22 +84,24 @@
     return base::MakeUnique<display::FakeDisplayDelegate>();
   }
 
-  void InitializeUI() override {
+  void InitializeUI(const InitParams& params) override {
+    InitializeCommon(params);
     window_manager_.reset(new X11WindowManagerOzone);
-    if (!event_source_)
-      event_source_.reset(new X11EventSourceLibevent(gfx::GetXDisplay()));
     overlay_manager_.reset(new StubOverlayManager());
     input_controller_ = CreateStubInputController();
     cursor_factory_ozone_.reset(new X11CursorFactoryOzone());
     gpu_platform_support_host_.reset(CreateStubGpuPlatformSupportHost());
   }
 
-  void InitializeGPU() override {
-    if (!event_source_)
-      event_source_.reset(new X11EventSourceLibevent(gfx::GetXDisplay()));
+  void InitializeUI() override { NOTREACHED(); }
+
+  void InitializeGPU(const InitParams& params) override {
+    InitializeCommon(params);
     surface_factory_ozone_.reset(new X11SurfaceFactory());
   }
 
+  void InitializeGPU() override { NOTREACHED(); }
+
   base::MessageLoop::Type GetMessageLoopTypeForGpu() override {
     // When Ozone X11 backend is running use an UI loop to grab Expose events.
     // See GLSurfaceGLX and https://crbug.com/326995.
@@ -111,7 +109,27 @@
   }
 
  private:
-  // Objects in the browser process.
+  // Performs initialization steps need by both UI and GPU.
+  void InitializeCommon(const InitParams& params) {
+    // TODO(kylechar): Add DCHECK we only enter InitializeCommon() twice for
+    // single process mode.
+    if (common_initialized_)
+      return;
+
+    // If both UI and GPU are running in the same process then XInitThreads()
+    // must be the first Xlib call.
+    if (params.single_process || RunningInsideMus())
+      XInitThreads();
+
+    ui::SetDefaultX11ErrorHandlers();
+    event_source_.reset(new X11EventSourceLibevent(gfx::GetXDisplay()));
+
+    common_initialized_ = true;
+  }
+
+  bool common_initialized_ = false;
+
+  // Objects in the UI process.
   std::unique_ptr<X11WindowManagerOzone> window_manager_;
   std::unique_ptr<OverlayManagerOzone> overlay_manager_;
   std::unique_ptr<InputController> input_controller_;
@@ -121,7 +139,7 @@
   // Objects in the GPU process.
   std::unique_ptr<X11SurfaceFactory> surface_factory_ozone_;
 
-  // Objects in both browser and GPU process.
+  // Objects in both UI and GPU process.
   std::unique_ptr<X11EventSourceLibevent> event_source_;
 
   DISALLOW_COPY_AND_ASSIGN(OzonePlatformX11);