diff --git a/BUILD.gn b/BUILD.gn
index 9378182..a4625d7 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -456,7 +456,10 @@
     }
 
     if (enable_extensions) {
-      deps += [ "//extensions/shell:app_shell" ]
+      deps += [
+        "//extensions/shell:app_shell",
+        "//extensions/shell/installer:app_shell_installer",
+      ]
     }
 
     if (enable_nacl) {
diff --git a/DEPS b/DEPS
index 43d68cd..3c75e871e 100644
--- a/DEPS
+++ b/DEPS
@@ -64,7 +64,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling PDFium
   # and whatever else without interference from each other.
-  'pdfium_revision': '748793bd30ef25919c069f1856cc7e6ee0aee181',
+  'pdfium_revision': '897c4b922f1fb50b942cda6cab0f4605f8afac3c',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling openmax_dl
   # and whatever else without interference from each other.
@@ -96,7 +96,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling catapult
   # and whatever else without interference from each other.
-  'catapult_revision': '1ffc3ed0526747eded946b2970a50046603b9272',
+  'catapult_revision': '4ef4b9509fe15a757ad506be6bac48070eb945d3',
   # 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/AwSafeBrowsingConfigHelper.java b/android_webview/java/src/org/chromium/android_webview/AwSafeBrowsingConfigHelper.java
index c6b6a14..1177fea 100644
--- a/android_webview/java/src/org/chromium/android_webview/AwSafeBrowsingConfigHelper.java
+++ b/android_webview/java/src/org/chromium/android_webview/AwSafeBrowsingConfigHelper.java
@@ -4,13 +4,9 @@
 
 package org.chromium.android_webview;
 
-import android.annotation.SuppressLint;
-import android.content.ContentResolver;
 import android.content.Context;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
-import android.os.AsyncTask;
-import android.provider.Settings;
 
 import org.chromium.base.CommandLine;
 import org.chromium.base.Log;
@@ -26,25 +22,15 @@
     private static final String OPT_IN_META_DATA_STR = "android.webkit.WebView.EnableSafeBrowsing";
 
     public static void maybeInitSafeBrowsingFromSettings(final Context appContext) {
-        if (AwSafeBrowsingConfigHelper.shouldEnableSafeBrowsingSupport(appContext)) {
-            // Assume safebrowsing on by default initially.
+        if (CommandLine.getInstance().hasSwitch(AwSwitches.WEBVIEW_ENABLE_SAFEBROWSING_SUPPORT)
+                || appHasOptedIn(appContext)) {
+            // Assume safebrowsing on by default initially. If GMS is available, we later use
+            // isVerifyAppsEnabled() to check if "Scan device for security threats" has been checked
+            // by the user.
             AwContentsStatics.setSafeBrowsingEnabled(true);
-            // Fetch Android settings related to safe-browsing in the background.
-            AsyncTask.THREAD_POOL_EXECUTOR.execute(new Runnable() {
-                @Override
-                public void run() {
-                    AwContentsStatics.setSafeBrowsingEnabled(
-                            isScanDeviceForSecurityThreatsEnabled(appContext));
-                }
-            });
         }
     }
 
-    private static boolean shouldEnableSafeBrowsingSupport(Context appContext) {
-        return CommandLine.getInstance().hasSwitch(AwSwitches.WEBVIEW_ENABLE_SAFEBROWSING_SUPPORT)
-                || appHasOptedIn(appContext);
-    }
-
     private static boolean appHasOptedIn(Context appContext) {
         try {
             ApplicationInfo info = appContext.getPackageManager().getApplicationInfo(
@@ -63,14 +49,6 @@
         }
     }
 
-    @SuppressLint("NewApi") // android.provider.Settings.Global#getInt requires API level 17
-    private static boolean isScanDeviceForSecurityThreatsEnabled(Context applicationContext) {
-        // Determine if the "Scan device for security threats" functionality is enabled in
-        // Android->System->Google->Security settings.
-        ContentResolver contentResolver = applicationContext.getContentResolver();
-        return Settings.Secure.getInt(contentResolver, "package_verifier_user_consent", 1) > 0;
-    }
-
     // Not meant to be instantiated.
     private AwSafeBrowsingConfigHelper() {}
 }
diff --git a/ash/BUILD.gn b/ash/BUILD.gn
index 04c1ca6..26914aa 100644
--- a/ash/BUILD.gn
+++ b/ash/BUILD.gn
@@ -188,6 +188,8 @@
     "host/root_window_transformer.h",
     "host/transformer_helper.cc",
     "host/transformer_helper.h",
+    "ime/ime_controller.cc",
+    "ime/ime_controller.h",
     "ime/ime_switch_type.h",
     "key_event_watcher.cc",
     "key_event_watcher.h",
diff --git a/ash/app_list/app_list_delegate_impl.cc b/ash/app_list/app_list_delegate_impl.cc
index 1ff3bfc3..ef2f8c9 100644
--- a/ash/app_list/app_list_delegate_impl.cc
+++ b/ash/app_list/app_list_delegate_impl.cc
@@ -5,13 +5,15 @@
 #include "ash/app_list/app_list_delegate_impl.h"
 
 #include "ash/root_window_controller.h"
+#include "ash/shelf/app_list_button.h"
+#include "ash/shelf/shelf.h"
+#include "ash/shelf/shelf_widget.h"
 #include "ash/shell.h"
 #include "ash/shell_port.h"
-#include "ui/app_list/app_list_features.h"
 #include "ui/app_list/presenter/app_list.h"
 
 namespace ash {
-// TODO(newcomer): Remove this class as a part of crbug.com/726838
+
 AppListDelegateImpl::AppListDelegateImpl() {
   Shell::Get()->app_list()->set_delegate(this);
 }
@@ -22,11 +24,17 @@
 
 void AppListDelegateImpl::OnAppListVisibilityChanged(bool visible,
                                                      int64_t display_id) {
-  if (app_list::features::IsFullscreenAppListEnabled()) {
-    aura::Window* root_window =
-        ShellPort::Get()->GetRootWindowForDisplayId(display_id);
-    Shell::Get()->OnAppListVisibilityChanged(visible, root_window);
-  }
+  aura::Window* root_window =
+      ShellPort::Get()->GetRootWindowForDisplayId(display_id);
+  AppListButton* app_list_button =
+      Shelf::ForWindow(root_window)->shelf_widget()->GetAppListButton();
+  if (!app_list_button)
+    return;
+
+  if (visible)
+    app_list_button->OnAppListShown();
+  else
+    app_list_button->OnAppListDismissed();
 }
 
 }  // namespace ash
diff --git a/ash/app_list/app_list_presenter_delegate.cc b/ash/app_list/app_list_presenter_delegate.cc
index fc81a0ab..01b2bcc5 100644
--- a/ash/app_list/app_list_presenter_delegate.cc
+++ b/ash/app_list/app_list_presenter_delegate.cc
@@ -119,9 +119,13 @@
 
 void AppListPresenterDelegate::OnShown(int64_t display_id) {
   is_visible_ = true;
+  // Update applist button status when app list visibility is changed.
   aura::Window* root_window =
       ShellPort::Get()->GetRootWindowForDisplayId(display_id);
-  Shell::Get()->OnAppListVisibilityChanged(is_visible_, root_window);
+  AppListButton* app_list_button =
+      Shelf::ForWindow(root_window)->shelf_widget()->GetAppListButton();
+  if (app_list_button)
+    app_list_button->OnAppListShown();
 }
 
 void AppListPresenterDelegate::OnDismissed() {
@@ -129,9 +133,12 @@
   DCHECK(view_);
 
   is_visible_ = false;
-  aura::Window* root_window =
-      RootWindowController::ForTargetRootWindow()->GetRootWindow();
-  Shell::Get()->OnAppListVisibilityChanged(is_visible_, root_window);
+
+  // Update applist button status when app list visibility is changed.
+  Shelf* shelf = Shelf::ForWindow(view_->GetWidget()->GetNativeWindow());
+  AppListButton* app_list_button = shelf->shelf_widget()->GetAppListButton();
+  if (app_list_button)
+    app_list_button->OnAppListDismissed();
 }
 
 void AppListPresenterDelegate::UpdateBounds() {
diff --git a/ash/app_list/app_list_presenter_delegate_unittest.cc b/ash/app_list/app_list_presenter_delegate_unittest.cc
index 3d27aa20..ab85c94 100644
--- a/ash/app_list/app_list_presenter_delegate_unittest.cc
+++ b/ash/app_list/app_list_presenter_delegate_unittest.cc
@@ -5,7 +5,6 @@
 #include <memory>
 
 #include "ash/ash_switches.h"
-#include "ash/public/cpp/config.h"
 #include "ash/public/cpp/shell_window_ids.h"
 #include "ash/shell.h"
 #include "ash/test/ash_test_base.h"
@@ -15,8 +14,6 @@
 #include "base/macros.h"
 #include "base/test/scoped_feature_list.h"
 #include "ui/app_list/app_list_features.h"
-#include "ui/app_list/app_list_switches.h"
-#include "ui/app_list/views/app_list_main_view.h"
 #include "ui/app_list/views/app_list_view.h"
 #include "ui/aura/test/test_windows.h"
 #include "ui/aura/window.h"
@@ -57,12 +54,12 @@
     UpdateDisplay("1024x768");
   }
 
+ private:
   void EnableFullscreenAppList() {
     scoped_feature_list_.InitAndEnableFeature(
         app_list::features::kEnableFullscreenAppList);
   }
 
- private:
   test::TestAppListViewPresenterImpl app_list_presenter_impl_;
   bool test_with_fullscreen_;
   base::test::ScopedFeatureList scoped_feature_list_;
@@ -74,7 +71,7 @@
 // the parameterized tests.
 INSTANTIATE_TEST_CASE_P(, AppListPresenterDelegateTest, testing::Bool());
 
-// Tests that app list hides when focus moves to a normal window.
+// Tests that app launcher hides when focus moves to a normal window.
 TEST_P(AppListPresenterDelegateTest, HideOnFocusOut) {
   app_list_presenter_impl()->Show(GetPrimaryDisplayId());
   EXPECT_TRUE(app_list_presenter_impl()->GetTargetVisibility());
@@ -85,7 +82,7 @@
   EXPECT_FALSE(app_list_presenter_impl()->GetTargetVisibility());
 }
 
-// Tests that app list remains visible when focus is moved to a different
+// Tests that app launcher remains visible when focus is moved to a different
 // window in kShellWindowId_AppListContainer.
 TEST_P(AppListPresenterDelegateTest,
        RemainVisibleWhenFocusingToApplistContainer) {
@@ -143,7 +140,7 @@
   EXPECT_FALSE(app_list_presenter_impl()->GetTargetVisibility());
 }
 
-// Tests opening the app list on a non-primary display, then deleting the
+// Tests opening the app launcher on a non-primary display, then deleting the
 // display.
 TEST_P(AppListPresenterDelegateTest, NonPrimaryDisplay) {
   // Set up a screen with two displays (horizontally adjacent).
@@ -166,7 +163,7 @@
   EXPECT_FALSE(app_list_presenter_impl()->GetTargetVisibility());
 }
 
-// Tests opening the app list on a tiny display that is too small to contain
+// Tests opening the app launcher on a tiny display that is too small to contain
 // it.
 TEST_F(AppListPresenterDelegateTest, TinyDisplay) {
   // Set up a screen with a tiny display (height smaller than the app list).
@@ -189,67 +186,4 @@
   EXPECT_GE(app_list_view_top, kMinimalAppListMargin);
 }
 
-// Tests that the peeking app list is enlarged to fullscreen after the user
-// types in the search box.
-TEST_F(AppListPresenterDelegateTest, SnapToFullscreenAfterSearchboxInput) {
-  // TODO(newcomer): investigate failure in mash. http://crbug.com/726838.
-  if (Shell::GetAshConfig() == Config::MASH)
-    return;
-
-  EnableFullscreenAppList();
-  UpdateDisplay("1024x768");
-  EXPECT_TRUE(app_list::features::IsFullscreenAppListEnabled());
-  app_list_presenter_impl()->Show(GetPrimaryDisplayId());
-  app_list::AppListView* app_list = app_list_presenter_impl()->GetView();
-  // Check that it is in peeking mode.
-  EXPECT_FALSE(app_list->is_fullscreen());
-
-  // Dummy key event to search box.
-  ui::test::EventGenerator& generator = GetEventGenerator();
-  generator.PressKey(ui::KeyboardCode::VKEY_0, 0);
-  // Check that it is in fullscreen mode.
-  EXPECT_TRUE(app_list->is_fullscreen());
-}
-
-// Tests that the peeking app list closes if the user taps outside its
-// bounds.
-TEST_F(AppListPresenterDelegateTest, TapAndClickOutsideClosesPeekingAppList) {
-  EnableFullscreenAppList();
-
-  app_list_presenter_impl()->Show(GetPrimaryDisplayId());
-  EXPECT_TRUE(app_list_presenter_impl()->GetTargetVisibility());
-  ui::test::EventGenerator& generator = GetEventGenerator();
-
-  // Grab the bounds of the search box,
-  // which is guaranteed to be inside the app list.
-  gfx::Point tap_point = app_list_presenter_impl()
-                             ->GetView()
-                             ->search_box_widget()
-                             ->GetContentsView()
-                             ->GetBoundsInScreen()
-                             .CenterPoint();
-
-  // Tapping inside the bounds doesn't close the app list.
-  generator.GestureTapAt(tap_point);
-  EXPECT_TRUE(app_list_presenter_impl()->GetTargetVisibility());
-
-  // Clicking inside the bounds doesn't close the app list.
-  generator.MoveMouseTo(tap_point);
-  generator.ClickLeftButton();
-  EXPECT_TRUE(app_list_presenter_impl()->GetTargetVisibility());
-
-  // Tapping outside the bounds closes the app list.
-  tap_point.set_x(tap_point.x() + 750);
-  generator.GestureTapAt(tap_point);
-  EXPECT_FALSE(app_list_presenter_impl()->GetTargetVisibility());
-
-  app_list_presenter_impl()->Show(GetPrimaryDisplayId());
-  EXPECT_TRUE(app_list_presenter_impl()->GetTargetVisibility());
-
-  // Clicking outside the bounds closes the app list.
-  generator.MoveMouseTo(tap_point);
-  generator.ClickLeftButton();
-  EXPECT_FALSE(app_list_presenter_impl()->GetTargetVisibility());
-}
-
 }  // namespace ash
diff --git a/ash/ash_strings.grd b/ash/ash_strings.grd
index f4b54e9b..68fe43c 100644
--- a/ash/ash_strings.grd
+++ b/ash/ash_strings.grd
@@ -300,6 +300,9 @@
       <message name="IDS_ASH_STATUS_TRAY_IME_SETTINGS" desc="The label used for IME settings entry.">
         Customize languages and input...
       </message>
+      <message name="IDS_ASH_STATUS_TRAY_IME_MANAGED" desc="Tooltip for the icon displayed when input methods are managed by enterprise policy">
+        Input methods are configured by your administrator.
+      </message>
       <message name="IDS_ASH_STATUS_TRAY_IME_EMOJI" desc="The label used for IME emoji entry.">
         Emoji palette
       </message>
diff --git a/ash/content/display/screen_orientation_controller_chromeos_unittest.cc b/ash/content/display/screen_orientation_controller_chromeos_unittest.cc
index 60fca91..8a708ec 100644
--- a/ash/content/display/screen_orientation_controller_chromeos_unittest.cc
+++ b/ash/content/display/screen_orientation_controller_chromeos_unittest.cc
@@ -19,7 +19,6 @@
 #include "ash/test/content/test_shell_content_state.h"
 #include "ash/test/screen_orientation_controller_test_api.h"
 #include "ash/test/test_shell_delegate.h"
-#include "ash/test/test_system_tray_delegate.h"
 #include "ash/wm/maximize_mode/maximize_mode_controller.h"
 #include "ash/wm/window_state.h"
 #include "base/command_line.h"
diff --git a/ash/ime/ime_controller.cc b/ash/ime/ime_controller.cc
new file mode 100644
index 0000000..4fc7be5
--- /dev/null
+++ b/ash/ime/ime_controller.cc
@@ -0,0 +1,29 @@
+// 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 "ash/ime/ime_controller.h"
+
+namespace ash {
+
+ImeController::ImeController() = default;
+
+ImeController::~ImeController() = default;
+
+IMEInfo ImeController::GetCurrentIme() const {
+  return IMEInfo();
+}
+
+std::vector<IMEPropertyInfo> ImeController::GetCurrentImeProperties() const {
+  return std::vector<IMEPropertyInfo>();
+}
+
+std::vector<IMEInfo> ImeController::GetAvailableImes() const {
+  return std::vector<IMEInfo>();
+}
+
+bool ImeController::IsImeManaged() const {
+  return false;
+}
+
+}  // namespace ash
diff --git a/ash/ime/ime_controller.h b/ash/ime/ime_controller.h
new file mode 100644
index 0000000..dce021b
--- /dev/null
+++ b/ash/ime/ime_controller.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 ASH_IME_IME_CONTROLLER_H_
+#define ASH_IME_IME_CONTROLLER_H_
+
+#include <vector>
+
+#include "ash/ash_export.h"
+#include "ash/system/tray/ime_info.h"
+#include "base/macros.h"
+
+namespace ash {
+struct IMEInfo;
+
+// Connects ash IME users (e.g. the system tray) to the IME implementation,
+// which might live in Chrome browser or in a separate mojo service.
+// TODO(jamescook): Convert to use mojo IME interface to Chrome browser.
+class ASH_EXPORT ImeController {
+ public:
+  ImeController();
+  virtual ~ImeController();
+
+  // Returns the currently selected IME.
+  virtual IMEInfo GetCurrentIme() const;
+
+  // Returns a list of properties for the currently selected IME.
+  virtual std::vector<IMEPropertyInfo> GetCurrentImeProperties() const;
+
+  // Returns a list of available IMEs. "Available" IMEs are both installed and
+  // enabled by the user in settings.
+  virtual std::vector<IMEInfo> GetAvailableImes() const;
+
+  // Returns true if the available IMEs are managed by enterprise policy.
+  virtual bool IsImeManaged() const;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(ImeController);
+};
+
+}  // namespace ash
+
+#endif  // ASH_IME_IME_CONTROLLER_H_
diff --git a/ash/mus/shell_delegate_mus.cc b/ash/mus/shell_delegate_mus.cc
index 744ec66..e6a79c8 100644
--- a/ash/mus/shell_delegate_mus.cc
+++ b/ash/mus/shell_delegate_mus.cc
@@ -87,6 +87,10 @@
   return new SystemTrayDelegateMus();
 }
 
+ImeController* ShellDelegateMus::GetImeController() {
+  return &stub_ime_controller_;
+}
+
 std::unique_ptr<WallpaperDelegate> ShellDelegateMus::CreateWallpaperDelegate() {
   return base::MakeUnique<WallpaperDelegateMus>();
 }
diff --git a/ash/mus/shell_delegate_mus.h b/ash/mus/shell_delegate_mus.h
index 7736d37..4c9f266 100644
--- a/ash/mus/shell_delegate_mus.h
+++ b/ash/mus/shell_delegate_mus.h
@@ -7,6 +7,7 @@
 
 #include <memory>
 
+#include "ash/ime/ime_controller.h"
 #include "ash/shell_delegate.h"
 #include "base/macros.h"
 
@@ -36,6 +37,7 @@
   void ShelfInit() override;
   void ShelfShutdown() override;
   SystemTrayDelegate* CreateSystemTrayDelegate() override;
+  ImeController* GetImeController() override;
   std::unique_ptr<WallpaperDelegate> CreateWallpaperDelegate() override;
   AccessibilityDelegate* CreateAccessibilityDelegate() override;
   std::unique_ptr<PaletteDelegate> CreatePaletteDelegate() override;
@@ -53,6 +55,7 @@
  private:
   // |connector_| may be null in tests.
   service_manager::Connector* connector_;
+  ImeController stub_ime_controller_;
 
   DISALLOW_COPY_AND_ASSIGN(ShellDelegateMus);
 };
diff --git a/ash/public/cpp/shell_window_ids.h b/ash/public/cpp/shell_window_ids.h
index 7d0b3f1..d5ae3c0 100644
--- a/ash/public/cpp/shell_window_ids.h
+++ b/ash/public/cpp/shell_window_ids.h
@@ -56,9 +56,6 @@
   // The container for top-level windows with the 'always-on-top' flag set.
   kShellWindowId_AlwaysOnTopContainer,
 
-  // The container for the app list.
-  kShellWindowId_AppListContainer,
-
   // The container for the shelf.
   kShellWindowId_ShelfContainer,
 
@@ -68,6 +65,9 @@
   // The container for panel windows.
   kShellWindowId_PanelContainer,
 
+  // The container for the app list.
+  kShellWindowId_AppListContainer,
+
   // The container for user-specific modal windows.
   kShellWindowId_SystemModalContainer,
 
@@ -137,10 +137,10 @@
     kShellWindowId_VirtualKeyboardContainer,
     kShellWindowId_DefaultContainer,
     kShellWindowId_AlwaysOnTopContainer,
-    kShellWindowId_AppListContainer,
     kShellWindowId_ShelfContainer,
     kShellWindowId_ShelfBubbleContainer,
     kShellWindowId_PanelContainer,
+    kShellWindowId_AppListContainer,
     kShellWindowId_SystemModalContainer,
     kShellWindowId_LockScreenWallpaperContainer,
     kShellWindowId_LockScreenContainer,
diff --git a/ash/root_window_controller.cc b/ash/root_window_controller.cc
index 14c0548..7910744 100644
--- a/ash/root_window_controller.cc
+++ b/ash/root_window_controller.cc
@@ -897,12 +897,6 @@
   wm::SetSnapsChildrenToPhysicalPixelBoundary(always_on_top_container);
   always_on_top_container->SetProperty(kUsesScreenCoordinatesKey, true);
 
-  aura::Window* app_list_container =
-      CreateContainer(kShellWindowId_AppListContainer, "AppListContainer",
-                      non_lock_screen_containers);
-  wm::SetSnapsChildrenToPhysicalPixelBoundary(app_list_container);
-  app_list_container->SetProperty(kUsesScreenCoordinatesKey, true);
-
   aura::Window* shelf_container =
       CreateContainer(kShellWindowId_ShelfContainer, "ShelfContainer",
                       non_lock_screen_containers);
@@ -923,6 +917,12 @@
   shelf_bubble_container->SetProperty(kUsesScreenCoordinatesKey, true);
   shelf_bubble_container->SetProperty(kLockedToRootKey, true);
 
+  aura::Window* app_list_container =
+      CreateContainer(kShellWindowId_AppListContainer, "AppListContainer",
+                      non_lock_screen_containers);
+  wm::SetSnapsChildrenToPhysicalPixelBoundary(app_list_container);
+  app_list_container->SetProperty(kUsesScreenCoordinatesKey, true);
+
   aura::Window* modal_container =
       CreateContainer(kShellWindowId_SystemModalContainer,
                       "SystemModalContainer", non_lock_screen_containers);
diff --git a/ash/shelf/app_list_button.cc b/ash/shelf/app_list_button.cc
index efecc29..584d9d8 100644
--- a/ash/shelf/app_list_button.cc
+++ b/ash/shelf/app_list_button.cc
@@ -44,7 +44,7 @@
   DCHECK(listener_);
   DCHECK(shelf_view_);
   DCHECK(shelf_);
-  Shell::Get()->AddShellObserver(this);
+
   SetInkDropMode(InkDropMode::ON_NO_GESTURE_HANDLER);
   set_ink_drop_base_color(kShelfInkDropBaseColor);
   set_ink_drop_visible_opacity(kShelfInkDropVisibleOpacity);
@@ -55,9 +55,7 @@
   set_notify_action(CustomButton::NOTIFY_ON_PRESS);
 }
 
-AppListButton::~AppListButton() {
-  Shell::Get()->RemoveShellObserver(this);
-}
+AppListButton::~AppListButton() {}
 
 void AppListButton::OnAppListShown() {
   AnimateInkDrop(views::InkDropState::ACTIVATED, nullptr);
@@ -241,15 +239,4 @@
   }
 }
 
-void AppListButton::OnAppListVisibilityChanged(bool shown,
-                                               aura::Window* root_window) {
-  if (shelf_ != Shelf::ForWindow(root_window))
-    return;
-
-  if (shown)
-    OnAppListShown();
-  else
-    OnAppListDismissed();
-}
-
 }  // namespace ash
diff --git a/ash/shelf/app_list_button.h b/ash/shelf/app_list_button.h
index fa9a35b..02c69c2 100644
--- a/ash/shelf/app_list_button.h
+++ b/ash/shelf/app_list_button.h
@@ -8,7 +8,6 @@
 #include <memory>
 
 #include "ash/ash_export.h"
-#include "ash/shell_observer.h"
 #include "base/macros.h"
 #include "third_party/skia/include/core/SkColor.h"
 #include "ui/views/controls/button/image_button.h"
@@ -19,8 +18,7 @@
 class ShelfView;
 
 // Button used for the AppList icon on the shelf.
-class ASH_EXPORT AppListButton : public views::ImageButton,
-                                 public ShellObserver {
+class ASH_EXPORT AppListButton : public views::ImageButton {
  public:
   AppListButton(InkDropButtonListener* listener,
                 ShelfView* shelf_view,
@@ -57,10 +55,6 @@
   // ink drops.
   gfx::Point GetCenterPoint() const;
 
-  // ShellObserver overrides:
-  void OnAppListVisibilityChanged(bool shown,
-                                  aura::Window* root_window) override;
-
   // True if the app list is currently showing for this display.
   // This is useful because other IsApplistVisible functions aren't per-display.
   bool is_showing_app_list_;
diff --git a/ash/shelf/shelf_layout_manager.cc b/ash/shelf/shelf_layout_manager.cc
index d5474587..3b2c170 100644
--- a/ash/shelf/shelf_layout_manager.cc
+++ b/ash/shelf/shelf_layout_manager.cc
@@ -28,7 +28,6 @@
 #include "base/auto_reset.h"
 #include "base/command_line.h"
 #include "base/i18n/rtl.h"
-#include "ui/app_list/app_list_features.h"
 #include "ui/base/ui_base_switches.h"
 #include "ui/compositor/layer.h"
 #include "ui/compositor/layer_animation_observer.h"
@@ -426,16 +425,6 @@
       &keyboard_observer_);
 }
 
-void ShelfLayoutManager::OnAppListVisibilityChanged(bool shown,
-                                                    aura::Window* root_window) {
-  if (shelf_ != Shelf::ForWindow(root_window))
-    return;
-
-  is_app_list_visible_ = shown;
-  if (app_list::features::IsFullscreenAppListEnabled())
-    MaybeUpdateShelfBackground(AnimationChangeType::IMMEDIATE);
-}
-
 void ShelfLayoutManager::OnWindowActivated(ActivationReason reason,
                                            aura::Window* gained_active,
                                            aura::Window* lost_active) {
@@ -479,10 +468,6 @@
   if (state_.session_state != session_manager::SessionState::ACTIVE)
     return SHELF_BACKGROUND_OVERLAP;
 
-  // If the app list is active, hide the shelf background to prevent overlap.
-  if (is_app_list_visible_ && app_list::features::IsFullscreenAppListEnabled())
-    return SHELF_BACKGROUND_DEFAULT;
-
   if (state_.visibility_state != SHELF_AUTO_HIDE &&
       state_.window_state == wm::WORKSPACE_WINDOW_STATE_MAXIMIZED) {
     return SHELF_BACKGROUND_MAXIMIZED;
diff --git a/ash/shelf/shelf_layout_manager.h b/ash/shelf/shelf_layout_manager.h
index 7c3731a..7e66fb2 100644
--- a/ash/shelf/shelf_layout_manager.h
+++ b/ash/shelf/shelf_layout_manager.h
@@ -64,6 +64,7 @@
 
   // Clears internal data for shutdown process.
   void PrepareForShutdown();
+
   // Returns whether the shelf and its contents (shelf, status) are visible
   // on the screen.
   bool IsVisible() const;
@@ -137,8 +138,6 @@
   void OnPinnedStateChanged(aura::Window* pinned_window) override;
   void OnVirtualKeyboardStateChanged(bool activated,
                                      aura::Window* root_window) override;
-  void OnAppListVisibilityChanged(bool shown,
-                                  aura::Window* root_window) override;
 
   // Overridden from wm::ActivationChangeObserver:
   void OnWindowActivated(ActivationReason reason,
@@ -318,10 +317,6 @@
   // Do any windows overlap the shelf? This is maintained by WorkspaceManager.
   bool window_overlaps_shelf_;
 
-  // Is the AppList visible? This is maintained by
-  // OnAppListVisibilityChanged.
-  bool is_app_list_visible_ = false;
-
   base::OneShotTimer auto_hide_timer_;
 
   // Whether the mouse was over the shelf when the auto hide timer started.
diff --git a/ash/shelf/shelf_view_unittest.cc b/ash/shelf/shelf_view_unittest.cc
index 361a947..4ee61920 100644
--- a/ash/shelf/shelf_view_unittest.cc
+++ b/ash/shelf/shelf_view_unittest.cc
@@ -34,7 +34,6 @@
 #include "ash/test/shelf_view_test_api.h"
 #include "ash/test/shell_test_api.h"
 #include "ash/test/test_shell_delegate.h"
-#include "ash/test/test_system_tray_delegate.h"
 #include "ash/test/wallpaper_controller_test_api.h"
 #include "ash/wallpaper/wallpaper_controller.h"
 #include "base/i18n/rtl.h"
diff --git a/ash/shell.cc b/ash/shell.cc
index 1186c21..e7e58d4 100644
--- a/ash/shell.cc
+++ b/ash/shell.cc
@@ -42,6 +42,7 @@
 #include "ash/frame/custom_frame_view_ash.h"
 #include "ash/gpu_support.h"
 #include "ash/high_contrast/high_contrast_controller.h"
+#include "ash/ime/ime_controller.h"
 #include "ash/keyboard/keyboard_ui.h"
 #include "ash/laser/laser_pointer_controller.h"
 #include "ash/login/lock_screen_controller.h"
@@ -553,12 +554,6 @@
   g_is_browser_process_with_mash = true;
 }
 
-void Shell::OnAppListVisibilityChanged(bool visible,
-                                       aura::Window* root_window) {
-  for (auto& observer : shell_observers_)
-    observer.OnAppListVisibilityChanged(visible, root_window);
-}
-
 ////////////////////////////////////////////////////////////////////////////////
 // Shell, private:
 
@@ -570,6 +565,7 @@
           base::MakeUnique<system::BrightnessControllerChromeos>()),
       cast_config_(base::MakeUnique<CastConfigController>()),
       focus_cycler_(base::MakeUnique<FocusCycler>()),
+      ime_controller_(shell_delegate->GetImeController()),
       immersive_context_(base::MakeUnique<ImmersiveContextAsh>()),
       keyboard_brightness_control_delegate_(
           base::MakeUnique<KeyboardBrightnessController>()),
@@ -595,6 +591,7 @@
       native_cursor_manager_(nullptr),
       simulate_modal_window_open_for_testing_(false),
       is_touch_hud_projection_enabled_(false) {
+  DCHECK(ime_controller_);
   // TODO(sky): better refactor cash/mash dependencies. Perhaps put all cash
   // state on ShellPortClassic. http://crbug.com/671246.
 
@@ -922,9 +919,11 @@
   if (!display_initialized)
     display_manager_->InitDefaultDisplay();
 
-  if (config == Config::CLASSIC) {
+  // TODO(sky): move this to chrome for mash. http://crbug.com/729824.
+  if (ShouldEnableSimplifiedDisplayManagement())
     display_manager_->RefreshFontParams();
 
+  if (config == Config::CLASSIC) {
     aura::Env::GetInstance()->set_context_factory(init_params.context_factory);
     aura::Env::GetInstance()->set_context_factory_private(
         init_params.context_factory_private);
diff --git a/ash/shell.h b/ash/shell.h
index 6be67bc5..910315c 100644
--- a/ash/shell.h
+++ b/ash/shell.h
@@ -18,7 +18,6 @@
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
 #include "base/observer_list.h"
-#include "ui/app_list/presenter/app_list_delegate.h"
 #include "ui/aura/window.h"
 #include "ui/display/screen.h"
 #include "ui/events/event_target.h"
@@ -110,6 +109,7 @@
 class FocusCycler;
 class GPUSupport;
 class HighContrastController;
+class ImeController;
 class ImmersiveContextAsh;
 class ImmersiveHandlerFactoryAsh;
 class KeyboardBrightnessControlDelegate;
@@ -324,6 +324,7 @@
   }
   ::wm::CompoundEventFilter* env_filter() { return env_filter_.get(); }
   FocusCycler* focus_cycler() { return focus_cycler_.get(); }
+  ImeController* ime_controller() { return ime_controller_; }
   KeyboardBrightnessControlDelegate* keyboard_brightness_control_delegate() {
     return keyboard_brightness_control_delegate_.get();
   }
@@ -610,8 +611,6 @@
   // Used to provide better error messages for Shell::Get() under mash.
   static void SetIsBrowserProcessWithMash();
 
-  void OnAppListVisibilityChanged(bool visible, aura::Window* root_window);
-
  private:
   FRIEND_TEST_ALL_PREFIXES(ExtendedDesktopTest, TestCursor);
   FRIEND_TEST_ALL_PREFIXES(WindowManagerTest, MouseEventCursors);
@@ -692,6 +691,7 @@
   std::unique_ptr<CastConfigController> cast_config_;
   std::unique_ptr<DragDropController> drag_drop_controller_;
   std::unique_ptr<FocusCycler> focus_cycler_;
+  ImeController* const ime_controller_;
   std::unique_ptr<ImmersiveContextAsh> immersive_context_;
   std::unique_ptr<KeyboardBrightnessControlDelegate>
       keyboard_brightness_control_delegate_;
diff --git a/ash/shell/shell_delegate_impl.cc b/ash/shell/shell_delegate_impl.cc
index 6958fe8..5dc0147 100644
--- a/ash/shell/shell_delegate_impl.cc
+++ b/ash/shell/shell_delegate_impl.cc
@@ -14,8 +14,8 @@
 #include "ash/shell/context_menu.h"
 #include "ash/shell/example_factory.h"
 #include "ash/shell/toplevel_window.h"
+#include "ash/system/tray/system_tray_delegate.h"
 #include "ash/test/test_keyboard_ui.h"
-#include "ash/test/test_system_tray_delegate.h"
 #include "ash/wm/window_state.h"
 #include "base/memory/ptr_util.h"
 #include "base/message_loop/message_loop.h"
@@ -107,7 +107,11 @@
 void ShellDelegateImpl::ShelfShutdown() {}
 
 SystemTrayDelegate* ShellDelegateImpl::CreateSystemTrayDelegate() {
-  return new test::TestSystemTrayDelegate;
+  return new SystemTrayDelegate;
+}
+
+ImeController* ShellDelegateImpl::GetImeController() {
+  return &stub_ime_controller_;
 }
 
 std::unique_ptr<WallpaperDelegate>
diff --git a/ash/shell/shell_delegate_impl.h b/ash/shell/shell_delegate_impl.h
index 0074e50c..ba873a7 100644
--- a/ash/shell/shell_delegate_impl.h
+++ b/ash/shell/shell_delegate_impl.h
@@ -8,6 +8,7 @@
 #include <memory>
 #include <string>
 
+#include "ash/ime/ime_controller.h"
 #include "ash/shell_delegate.h"
 #include "base/macros.h"
 
@@ -38,6 +39,7 @@
   void ShelfInit() override;
   void ShelfShutdown() override;
   SystemTrayDelegate* CreateSystemTrayDelegate() override;
+  ImeController* GetImeController() override;
   std::unique_ptr<WallpaperDelegate> CreateWallpaperDelegate() override;
   AccessibilityDelegate* CreateAccessibilityDelegate() override;
   std::unique_ptr<PaletteDelegate> CreatePaletteDelegate() override;
@@ -53,6 +55,8 @@
   void UpdateTouchscreenStatusFromPrefs() override;
 
  private:
+  ImeController stub_ime_controller_;
+
   DISALLOW_COPY_AND_ASSIGN(ShellDelegateImpl);
 };
 
diff --git a/ash/shell_delegate.h b/ash/shell_delegate.h
index f5c62c3b..583a852 100644
--- a/ash/shell_delegate.h
+++ b/ash/shell_delegate.h
@@ -39,6 +39,7 @@
 
 class AccessibilityDelegate;
 class GPUSupport;
+class ImeController;
 class PaletteDelegate;
 class Shelf;
 struct ShelfItem;
@@ -98,6 +99,9 @@
   // Creates a system-tray delegate. Shell takes ownership of the delegate.
   virtual SystemTrayDelegate* CreateSystemTrayDelegate() = 0;
 
+  // TODO(jamescook): Remove in favor of chrome using a mojo interface on ash.
+  virtual ImeController* GetImeController() = 0;
+
   // Creates a wallpaper delegate. Shell takes ownership of the delegate.
   virtual std::unique_ptr<WallpaperDelegate> CreateWallpaperDelegate() = 0;
 
diff --git a/ash/shell_observer.h b/ash/shell_observer.h
index fc0bbaf8..563f2a59 100644
--- a/ash/shell_observer.h
+++ b/ash/shell_observer.h
@@ -16,10 +16,6 @@
 
 class ASH_EXPORT ShellObserver {
  public:
-  // Called when the AppList is shown or dismissed.
-  virtual void OnAppListVisibilityChanged(bool shown,
-                                          aura::Window* root_window) {}
-
   // Called when a casting session is started or stopped.
   virtual void OnCastingSessionStartedOrStopped(bool started) {}
 
diff --git a/ash/system/ime/tray_ime_chromeos.cc b/ash/system/ime/tray_ime_chromeos.cc
index 9ecec4f7..abd34c2 100644
--- a/ash/system/ime/tray_ime_chromeos.cc
+++ b/ash/system/ime/tray_ime_chromeos.cc
@@ -7,13 +7,13 @@
 #include <memory>
 #include <vector>
 
+#include "ash/ime/ime_controller.h"
 #include "ash/resources/vector_icons/vector_icons.h"
 #include "ash/shell.h"
 #include "ash/shell_port.h"
 #include "ash/strings/grit/ash_strings.h"
 #include "ash/system/tray/system_tray.h"
 #include "ash/system/tray/system_tray_controller.h"
-#include "ash/system/tray/system_tray_delegate.h"
 #include "ash/system/tray/system_tray_notifier.h"
 #include "ash/system/tray/tray_constants.h"
 #include "ash/system/tray/tray_details_view.h"
@@ -75,13 +75,15 @@
 // enterprise-controlled icon).
 class IMEDetailedView : public ImeListView {
  public:
-  explicit IMEDetailedView(SystemTrayItem* owner)
-      : ImeListView(owner), settings_button_(nullptr) {}
+  IMEDetailedView(SystemTrayItem* owner, ImeController* ime_controller)
+      : ImeListView(owner), ime_controller_(ime_controller) {
+    DCHECK(ime_controller_);
+  }
 
   ~IMEDetailedView() override {}
 
-  void SetImeManagedMessage(base::string16 ime_managed_message) {
-    ime_managed_message_ = ime_managed_message;
+  views::ImageView* controlled_setting_icon() {
+    return controlled_setting_icon_;
   }
 
   void Update(const IMEInfoList& list,
@@ -110,11 +112,12 @@
   }
 
   void CreateExtraTitleRowButtons() override {
-    if (!ime_managed_message_.empty()) {
+    if (ime_controller_->IsImeManaged()) {
       controlled_setting_icon_ = TrayPopupUtils::CreateMainImageView();
       controlled_setting_icon_->SetImage(
           gfx::CreateVectorIcon(kSystemMenuBusinessIcon, kMenuIconColor));
-      controlled_setting_icon_->SetTooltipText(ime_managed_message_);
+      controlled_setting_icon_->SetTooltipText(
+          l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_IME_MANAGED));
       tri_view()->AddView(TriView::Container::END, controlled_setting_icon_);
     }
 
@@ -131,13 +134,13 @@
       owner()->system_tray()->CloseSystemBubble();
   }
 
-  views::Button* settings_button_;
+  ImeController* const ime_controller_;
+
+  // Gear icon that takes the user to IME settings.
+  views::Button* settings_button_ = nullptr;
 
   // This icon says that the IMEs are managed by policy.
-  views::ImageView* controlled_setting_icon_;
-  // If non-empty, a controlled setting icon should be displayed with this
-  // string as tooltip.
-  base::string16 ime_managed_message_;
+  views::ImageView* controlled_setting_icon_ = nullptr;
 
   DISALLOW_COPY_AND_ASSIGN(IMEDetailedView);
 };
@@ -146,11 +149,13 @@
 
 TrayIME::TrayIME(SystemTray* system_tray)
     : SystemTrayItem(system_tray, UMA_IME),
+      ime_controller_(Shell::Get()->ime_controller()),
       tray_label_(nullptr),
       default_(nullptr),
       detailed_(nullptr),
       keyboard_suppressed_(false),
       is_visible_(true) {
+  DCHECK(ime_controller_);
   SystemTrayNotifier* tray_notifier = Shell::Get()->system_tray_notifier();
   tray_notifier->AddVirtualKeyboardObserver(this);
   tray_notifier->AddAccessibilityObserver(this);
@@ -181,7 +186,6 @@
     default_->UpdateLabel(GetDefaultViewLabel(ime_list_.size() > 1));
   }
   if (detailed_) {
-    detailed_->SetImeManagedMessage(ime_managed_message_);
     detailed_->Update(ime_list_, property_list_, ShouldShowKeyboardToggle(),
                       GetSingleImeBehavior());
   }
@@ -211,9 +215,7 @@
 
 base::string16 TrayIME::GetDefaultViewLabel(bool show_ime_label) {
   if (show_ime_label) {
-    IMEInfo current;
-    Shell::Get()->system_tray_delegate()->GetCurrentIME(&current);
-    return current.name;
+    return ime_controller_->GetCurrentIme().name;
   } else {
     // Display virtual keyboard status instead.
     int id = keyboard::IsKeyboardEnabled()
@@ -244,8 +246,7 @@
 
 views::View* TrayIME::CreateDetailedView(LoginStatus status) {
   CHECK(detailed_ == nullptr);
-  detailed_ = new tray::IMEDetailedView(this);
-  detailed_->SetImeManagedMessage(ime_managed_message_);
+  detailed_ = new tray::IMEDetailedView(this, ime_controller_);
   detailed_->Init(ShouldShowKeyboardToggle(), GetSingleImeBehavior());
   return detailed_;
 }
@@ -264,13 +265,9 @@
 
 void TrayIME::OnIMERefresh() {
   // Caches the current ime state.
-  SystemTrayDelegate* delegate = Shell::Get()->system_tray_delegate();
-  ime_list_.clear();
-  property_list_.clear();
-  delegate->GetCurrentIME(&current_ime_);
-  delegate->GetAvailableIMEList(&ime_list_);
-  delegate->GetCurrentIMEProperties(&property_list_);
-  ime_managed_message_ = delegate->GetIMEManagedMessage();
+  current_ime_ = ime_controller_->GetCurrentIme();
+  property_list_ = ime_controller_->GetCurrentImeProperties();
+  ime_list_ = ime_controller_->GetAvailableImes();
 
   Update();
 }
@@ -284,7 +281,7 @@
 }
 
 bool TrayIME::IsIMEManaged() {
-  return !ime_managed_message_.empty();
+  return ime_controller_->IsImeManaged();
 }
 
 bool TrayIME::ShouldDefaultViewBeVisible() {
@@ -306,4 +303,8 @@
                         : ImeListView::HIDE_SINGLE_IME;
 }
 
+views::View* TrayIME::GetControlledSettingIconForTesting() {
+  return detailed_ ? detailed_->controlled_setting_icon() : nullptr;
+}
+
 }  // namespace ash
diff --git a/ash/system/ime/tray_ime_chromeos.h b/ash/system/ime/tray_ime_chromeos.h
index ac60097..699f9696 100644
--- a/ash/system/ime/tray_ime_chromeos.h
+++ b/ash/system/ime/tray_ime_chromeos.h
@@ -22,6 +22,7 @@
 class IMEDetailedView;
 }
 
+class ImeController;
 class TrayItemView;
 
 // Controller for IME options in the system menu. Note this is separate from
@@ -67,7 +68,7 @@
   void OnIMERefresh() override;
   void OnIMEMenuActivationChanged(bool is_active) override;
 
-  // Returns true input methods are managed by policy.
+  // Returns true if input methods are managed by policy.
   bool IsIMEManaged();
 
   // Whether the default view should be shown.
@@ -79,6 +80,10 @@
   // sub-view.
   ImeListView::SingleImeBehavior GetSingleImeBehavior();
 
+  // Returns the icon used when the IME is managed.
+  views::View* GetControlledSettingIconForTesting();
+
+  ImeController* ime_controller_;
   TrayItemView* tray_label_;
   tray::IMEDefaultView* default_;
   tray::IMEDetailedView* detailed_;
@@ -88,9 +93,6 @@
   IMEInfoList ime_list_;
   IMEInfo current_ime_;
   IMEPropertyInfoList property_list_;
-  // If non-empty, a controlled-setting icon should be displayed with a tooltip
-  // text defined by this string.
-  base::string16 ime_managed_message_;
 
   // Whether the IME label and tray items should be visible.
   bool is_visible_;
diff --git a/ash/system/ime/tray_ime_chromeos_unittest.cc b/ash/system/ime/tray_ime_chromeos_unittest.cc
index a7b2fe47..9bbad2f 100644
--- a/ash/system/ime/tray_ime_chromeos_unittest.cc
+++ b/ash/system/ime/tray_ime_chromeos_unittest.cc
@@ -6,6 +6,7 @@
 
 #include "ash/accessibility_delegate.h"
 #include "ash/accessibility_types.h"
+#include "ash/ime/ime_controller.h"
 #include "ash/public/cpp/config.h"
 #include "ash/shell.h"
 #include "ash/system/ime_menu/ime_list_view.h"
@@ -16,6 +17,31 @@
 #include "ui/keyboard/keyboard_util.h"
 
 namespace ash {
+namespace {
+
+class TestImeController : public ImeController {
+ public:
+  TestImeController() = default;
+  ~TestImeController() override = default;
+
+  // ImeController:
+  std::vector<IMEPropertyInfo> GetCurrentImeProperties() const override {
+    return current_ime_properties_;
+  }
+  std::vector<IMEInfo> GetAvailableImes() const override {
+    return available_imes_;
+  }
+  bool IsImeManaged() const override { return is_ime_managed_; }
+
+  std::vector<IMEPropertyInfo> current_ime_properties_;
+  std::vector<IMEInfo> available_imes_;
+  bool is_ime_managed_ = false;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(TestImeController);
+};
+
+}  // namespace
 
 class TrayIMETest : public test::AshTestBase {
  public:
@@ -30,15 +56,18 @@
   // is not created in ash tests.
   void SetAccessibilityKeyboardEnabled(bool enabled);
 
-  // Sets the current number of active IMEs.
-  void SetIMELength(int length);
+  // Creates |count| simulated active IMEs.
+  void SetActiveImeCount(int count);
 
   // Returns the view responsible for toggling virtual keyboard.
   views::View* GetToggleView() const;
 
-  // Sets the managed IMEs tooltip message (and thus also if IMEs are managed =
-  // non-empty or not = empty)
-  void SetManagedMessage(base::string16 managed_message);
+  // Simulates IME being managed by policy.
+  void SetImeManaged(bool managed);
+
+  views::View* GetImeManagedIcon();
+
+  void AddPropertyToCurrentIme(IMEPropertyInfo property);
 
   void SuppressKeyboard();
   void RestoreKeyboard();
@@ -48,6 +77,7 @@
   void TearDown() override;
 
  private:
+  TestImeController test_ime_controller_;
   std::unique_ptr<TrayIME> tray_;
   std::unique_ptr<views::View> default_view_;
   std::unique_ptr<views::View> detailed_view_;
@@ -68,13 +98,13 @@
       notification);
 }
 
-void TrayIMETest::SetIMELength(int length) {
-  tray_->ime_list_.clear();
+void TrayIMETest::SetActiveImeCount(int count) {
+  test_ime_controller_.available_imes_.clear();
   IMEInfo ime;
-  for (int i = 0; i < length; i++) {
-    tray_->ime_list_.push_back(ime);
+  for (int i = 0; i < count; i++) {
+    test_ime_controller_.available_imes_.push_back(ime);
   }
-  tray_->Update();
+  tray_->OnIMERefresh();
 }
 
 views::View* TrayIMETest::GetToggleView() const {
@@ -82,9 +112,18 @@
   return test_api.GetToggleView();
 }
 
-void TrayIMETest::SetManagedMessage(base::string16 managed_message) {
-  tray_->ime_managed_message_ = managed_message;
-  tray_->Update();
+void TrayIMETest::SetImeManaged(bool managed) {
+  test_ime_controller_.is_ime_managed_ = true;
+  tray_->OnIMERefresh();
+}
+
+views::View* TrayIMETest::GetImeManagedIcon() {
+  return tray_->GetControlledSettingIconForTesting();
+}
+
+void TrayIMETest::AddPropertyToCurrentIme(IMEPropertyInfo property) {
+  test_ime_controller_.current_ime_properties_.push_back(property);
+  tray_->OnIMERefresh();
 }
 
 void TrayIMETest::SuppressKeyboard() {
@@ -120,6 +159,7 @@
 void TrayIMETest::SetUp() {
   test::AshTestBase::SetUp();
   tray_.reset(new TrayIME(GetPrimarySystemTray()));
+  tray_->ime_controller_ = &test_ime_controller_;
   default_view_.reset(tray_->CreateDefaultView(LoginStatus::USER));
   detailed_view_.reset(tray_->CreateDetailedView(LoginStatus::USER));
 }
@@ -137,26 +177,57 @@
 // Tests that if the keyboard is not suppressed the default view is hidden
 // if less than 2 IMEs are present.
 TEST_F(TrayIMETest, HiddenWithNoIMEs) {
-  SetIMELength(0);
+  SetActiveImeCount(0);
   EXPECT_FALSE(default_view()->visible());
-  SetIMELength(1);
+  SetActiveImeCount(1);
   EXPECT_FALSE(default_view()->visible());
-  SetIMELength(2);
+  SetActiveImeCount(2);
   EXPECT_TRUE(default_view()->visible());
 }
 
 // Tests that if IMEs are managed, the default view is displayed even for a
 // single IME.
 TEST_F(TrayIMETest, ShownWithSingleIMEWhenManaged) {
-  SetManagedMessage(base::ASCIIToUTF16("managed"));
-  SetIMELength(0);
+  SetImeManaged(true);
+  SetActiveImeCount(0);
   EXPECT_FALSE(default_view()->visible());
-  SetIMELength(1);
+  SetActiveImeCount(1);
   EXPECT_TRUE(default_view()->visible());
-  SetIMELength(2);
+  SetActiveImeCount(2);
   EXPECT_TRUE(default_view()->visible());
 }
 
+// Tests that if an IME has multiple properties the default view is
+// displayed even for a single IME.
+TEST_F(TrayIMETest, ShownWithSingleImeWithProperties) {
+  SetActiveImeCount(1);
+  EXPECT_FALSE(default_view()->visible());
+
+  IMEPropertyInfo property1;
+  AddPropertyToCurrentIme(property1);
+  EXPECT_FALSE(default_view()->visible());
+
+  IMEPropertyInfo property2;
+  AddPropertyToCurrentIme(property2);
+  EXPECT_TRUE(default_view()->visible());
+}
+
+// Tests that when IMEs are managed an icon appears in the tray with an
+// appropriate tooltip.
+TEST_F(TrayIMETest, ImeManagedIcon) {
+  SetActiveImeCount(1);
+  EXPECT_FALSE(GetImeManagedIcon());
+
+  SetImeManaged(true);
+  views::View* icon = GetImeManagedIcon();
+  ASSERT_TRUE(icon);
+  base::string16 tooltip;
+  icon->GetTooltipText(gfx::Point(), &tooltip);
+  EXPECT_EQ(
+      base::ASCIIToUTF16("Input methods are configured by your administrator."),
+      tooltip);
+}
+
 // Tests that if no IMEs are present the default view is hidden when a11y is
 // enabled.
 TEST_F(TrayIMETest, HidesOnA11yEnabled) {
@@ -164,7 +235,7 @@
   if (Shell::GetAshConfig() == Config::MASH)
     return;
 
-  SetIMELength(0);
+  SetActiveImeCount(0);
   SuppressKeyboard();
   EXPECT_TRUE(default_view()->visible());
   // Enable a11y keyboard.
@@ -182,7 +253,7 @@
   if (Shell::GetAshConfig() == Config::MASH)
     return;
 
-  SetIMELength(0);
+  SetActiveImeCount(0);
   SuppressKeyboard();
   EXPECT_FALSE(keyboard::IsKeyboardEnabled());
   views::View* toggle = GetToggleView();
diff --git a/ash/system/ime_menu/ime_list_view.cc b/ash/system/ime_menu/ime_list_view.cc
index 8f2e655f..5600479 100644
--- a/ash/system/ime_menu/ime_list_view.cc
+++ b/ash/system/ime_menu/ime_list_view.cc
@@ -4,6 +4,7 @@
 
 #include "ash/system/ime_menu/ime_list_view.h"
 
+#include "ash/ime/ime_controller.h"
 #include "ash/ime/ime_switch_type.h"
 #include "ash/resources/vector_icons/vector_icons.h"
 #include "ash/shell.h"
@@ -12,7 +13,6 @@
 #include "ash/system/tray/actionable_view.h"
 #include "ash/system/tray/ime_info.h"
 #include "ash/system/tray/system_menu_button.h"
-#include "ash/system/tray/system_tray_delegate.h"
 #include "ash/system/tray/tray_constants.h"
 #include "ash/system/tray/tray_details_view.h"
 #include "ash/system/tray/tray_popup_header_button.h"
@@ -195,11 +195,9 @@
 
 void ImeListView::Init(bool show_keyboard_toggle,
                        SingleImeBehavior single_ime_behavior) {
-  SystemTrayDelegate* delegate = Shell::Get()->system_tray_delegate();
-  IMEInfoList list;
-  delegate->GetAvailableIMEList(&list);
-  IMEPropertyInfoList property_list;
-  delegate->GetCurrentIMEProperties(&property_list);
+  ImeController* ime_controller = Shell::Get()->ime_controller();
+  IMEInfoList list = ime_controller->GetAvailableImes();
+  IMEPropertyInfoList property_list = ime_controller->GetCurrentImeProperties();
   Update(list, property_list, show_keyboard_toggle, single_ime_behavior);
 }
 
diff --git a/ash/system/ime_menu/ime_menu_tray.cc b/ash/system/ime_menu/ime_menu_tray.cc
index 17720b43..ce3f458 100644
--- a/ash/system/ime_menu/ime_menu_tray.cc
+++ b/ash/system/ime_menu/ime_menu_tray.cc
@@ -6,6 +6,7 @@
 
 #include "ash/accessibility_delegate.h"
 #include "ash/ash_constants.h"
+#include "ash/ime/ime_controller.h"
 #include "ash/root_window_controller.h"
 #include "ash/session/session_controller.h"
 #include "ash/shelf/shelf.h"
@@ -15,7 +16,6 @@
 #include "ash/system/ime_menu/ime_list_view.h"
 #include "ash/system/tray/system_menu_button.h"
 #include "ash/system/tray/system_tray_controller.h"
-#include "ash/system/tray/system_tray_delegate.h"
 #include "ash/system/tray/system_tray_notifier.h"
 #include "ash/system/tray/tray_constants.h"
 #include "ash/system/tray/tray_container.h"
@@ -279,12 +279,14 @@
 
 ImeMenuTray::ImeMenuTray(Shelf* shelf)
     : TrayBackgroundView(shelf),
+      ime_controller_(Shell::Get()->ime_controller()),
       label_(new ImeMenuLabel()),
       show_keyboard_(false),
       force_show_keyboard_(false),
       keyboard_suppressed_(false),
       show_bubble_after_keyboard_hidden_(false),
       weak_ptr_factory_(this) {
+  DCHECK(ime_controller_);
   SetInkDropMode(InkDropMode::ON);
   SetupLabelForTray(label_);
   label_->SetElideBehavior(gfx::TRUNCATE);
@@ -443,11 +445,9 @@
 void ImeMenuTray::OnIMERefresh() {
   UpdateTrayLabel();
   if (bubble_ && ime_list_view_) {
-    SystemTrayDelegate* delegate = Shell::Get()->system_tray_delegate();
-    IMEInfoList list;
-    delegate->GetAvailableIMEList(&list);
-    IMEPropertyInfoList property_list;
-    delegate->GetCurrentIMEProperties(&property_list);
+    std::vector<IMEInfo> list = ime_controller_->GetAvailableImes();
+    IMEPropertyInfoList property_list =
+        ime_controller_->GetCurrentImeProperties();
     ime_list_view_->Update(list, property_list, false,
                            ImeListView::SHOW_SINGLE_IME);
   }
@@ -534,7 +534,7 @@
 }
 
 void ImeMenuTray::UpdateTrayLabel() {
-  Shell::Get()->system_tray_delegate()->GetCurrentIME(&current_ime_);
+  current_ime_ = ime_controller_->GetCurrentIme();
 
   // Updates the tray label based on the current input method.
   if (current_ime_.third_party)
diff --git a/ash/system/ime_menu/ime_menu_tray.h b/ash/system/ime_menu/ime_menu_tray.h
index 3019158ca..d2c9eda 100644
--- a/ash/system/ime_menu/ime_menu_tray.h
+++ b/ash/system/ime_menu/ime_menu_tray.h
@@ -20,6 +20,7 @@
 }  // namespace views
 
 namespace ash {
+class ImeController;
 class ImeListView;
 
 // A button in the tray which displays the short name of the currently-activated
@@ -95,6 +96,8 @@
   // Disables the virtual keyboard.
   void DisableVirtualKeyboard();
 
+  ImeController* ime_controller_;
+
   // Bubble for default and detailed views.
   std::unique_ptr<TrayBubbleWrapper> bubble_;
   ImeListView* ime_list_view_;
diff --git a/ash/system/ime_menu/ime_menu_tray_unittest.cc b/ash/system/ime_menu/ime_menu_tray_unittest.cc
index 10ea62d8..7bc17e95 100644
--- a/ash/system/ime_menu/ime_menu_tray_unittest.cc
+++ b/ash/system/ime_menu/ime_menu_tray_unittest.cc
@@ -6,6 +6,7 @@
 
 #include "ash/accelerators/accelerator_controller.h"
 #include "ash/accessibility_delegate.h"
+#include "ash/ime/ime_controller.h"
 #include "ash/shell.h"
 #include "ash/system/ime_menu/ime_list_view.h"
 #include "ash/system/status_area_widget.h"
@@ -13,7 +14,6 @@
 #include "ash/system/tray/system_tray_notifier.h"
 #include "ash/test/ash_test_base.h"
 #include "ash/test/status_area_widget_test_helper.h"
-#include "ash/test/test_system_tray_delegate.h"
 #include "base/run_loop.h"
 #include "base/strings/utf_string_conversions.h"
 #include "ui/accessibility/ax_node_data.h"
@@ -27,16 +27,43 @@
 using base::UTF8ToUTF16;
 
 namespace ash {
+namespace {
+
+class TestImeController : public ImeController {
+ public:
+  TestImeController() = default;
+  ~TestImeController() override = default;
+
+  // ImeController:
+  IMEInfo GetCurrentIme() const override { return current_ime_; }
+  std::vector<IMEInfo> GetAvailableImes() const override {
+    return available_imes_;
+  }
+
+  IMEInfo current_ime_;
+  std::vector<IMEInfo> available_imes_;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(TestImeController);
+};
 
 ImeMenuTray* GetTray() {
   return StatusAreaWidgetTestHelper::GetStatusAreaWidget()->ime_menu_tray();
 }
 
+}  // namespace
+
 class ImeMenuTrayTest : public test::AshTestBase {
  public:
   ImeMenuTrayTest() {}
   ~ImeMenuTrayTest() override {}
 
+  // test::AshTestBase:
+  void SetUp() override {
+    test::AshTestBase::SetUp();
+    GetTray()->ime_controller_ = &test_ime_controller_;
+  }
+
  protected:
   // Returns true if the IME menu tray is visible.
   bool IsVisible() { return GetTray()->visible(); }
@@ -50,13 +77,12 @@
   // Returns true if the IME menu bubble has been shown.
   bool IsBubbleShown() { return GetTray()->IsImeMenuBubbleShown(); }
 
-  // Returns true if the IME menu list has been updated with the right IME list.
-  bool IsTrayImeListValid(const std::vector<IMEInfo>& expected_imes,
+  // Verifies the IME menu list has been updated with the right IME list.
+  void ExpectValidImeList(const std::vector<IMEInfo>& expected_imes,
                           const IMEInfo& expected_current_ime) {
     const std::map<views::View*, std::string>& ime_map =
         ImeListViewTestApi(GetTray()->ime_list_view_).ime_map();
-    if (ime_map.size() != expected_imes.size())
-      return false;
+    EXPECT_EQ(expected_imes.size(), ime_map.size());
 
     std::vector<std::string> expected_ime_ids;
     for (const auto& ime : expected_imes) {
@@ -64,22 +90,16 @@
     }
     for (const auto& ime : ime_map) {
       // Tests that all the IMEs on the view is in the list of selected IMEs.
-      if (std::find(expected_ime_ids.begin(), expected_ime_ids.end(),
-                    ime.second) == expected_ime_ids.end()) {
-        return false;
-      }
+      EXPECT_TRUE(base::ContainsValue(expected_ime_ids, ime.second));
 
       // Tests that the checked IME is the current IME.
       ui::AXNodeData node_data;
       ime.first->GetAccessibleNodeData(&node_data);
       const auto checked_state = static_cast<ui::AXCheckedState>(
           node_data.GetIntAttribute(ui::AX_ATTR_CHECKED_STATE));
-      if (checked_state == ui::AX_CHECKED_STATE_TRUE) {
-        if (ime.second != expected_current_ime.id)
-          return false;
-      }
+      if (checked_state == ui::AX_CHECKED_STATE_TRUE)
+        EXPECT_EQ(expected_current_ime.id, ime.second);
     }
-    return true;
   }
 
   // Focuses in the given type of input context.
@@ -89,7 +109,15 @@
     ui::IMEBridge::Get()->SetCurrentInputContext(input_context);
   }
 
+  void SetCurrentIme(IMEInfo ime) { test_ime_controller_.current_ime_ = ime; }
+
+  void SetAvailableImes(const std::vector<IMEInfo>& imes) {
+    test_ime_controller_.available_imes_ = imes;
+  }
+
  private:
+  TestImeController test_ime_controller_;
+
   DISALLOW_COPY_AND_ASSIGN(ImeMenuTrayTest);
 };
 
@@ -118,7 +146,7 @@
   info1.short_name = UTF8ToUTF16("US");
   info1.third_party = false;
   info1.selected = true;
-  GetSystemTrayDelegate()->SetCurrentIME(info1);
+  SetCurrentIme(info1);
   Shell::Get()->system_tray_notifier()->NotifyRefreshIME();
   EXPECT_EQ(UTF8ToUTF16("US"), GetTrayText());
 
@@ -130,7 +158,7 @@
   info2.short_name = UTF8ToUTF16("UK");
   info2.third_party = true;
   info2.selected = true;
-  GetSystemTrayDelegate()->SetCurrentIME(info2);
+  SetCurrentIme(info2);
   Shell::Get()->system_tray_notifier()->NotifyRefreshIME();
   EXPECT_EQ(UTF8ToUTF16("UK*"), GetTrayText());
 }
@@ -206,19 +234,19 @@
 
   std::vector<IMEInfo> ime_info_list{info1, info2, info3};
 
-  GetSystemTrayDelegate()->SetAvailableIMEList(ime_info_list);
-  GetSystemTrayDelegate()->SetCurrentIME(info1);
+  SetAvailableImes(ime_info_list);
+  SetCurrentIme(info1);
   Shell::Get()->system_tray_notifier()->NotifyRefreshIME();
   EXPECT_EQ(UTF8ToUTF16("US"), GetTrayText());
-  EXPECT_TRUE(IsTrayImeListValid(ime_info_list, info1));
+  ExpectValidImeList(ime_info_list, info1);
 
   ime_info_list[0].selected = false;
   ime_info_list[2].selected = true;
-  GetSystemTrayDelegate()->SetAvailableIMEList(ime_info_list);
-  GetSystemTrayDelegate()->SetCurrentIME(info3);
+  SetAvailableImes(ime_info_list);
+  SetCurrentIme(info3);
   Shell::Get()->system_tray_notifier()->NotifyRefreshIME();
   EXPECT_EQ(UTF8ToUTF16("拼"), GetTrayText());
-  EXPECT_TRUE(IsTrayImeListValid(ime_info_list, info3));
+  ExpectValidImeList(ime_info_list, info3);
 
   // Closes the menu before quitting.
   GetTray()->PerformAction(tap);
diff --git a/ash/system/screen_layout_observer_unittest.cc b/ash/system/screen_layout_observer_unittest.cc
index 08e71ff..502fb39b 100644
--- a/ash/system/screen_layout_observer_unittest.cc
+++ b/ash/system/screen_layout_observer_unittest.cc
@@ -9,7 +9,6 @@
 #include "ash/system/devicetype_utils.h"
 #include "ash/system/tray/system_tray.h"
 #include "ash/test/ash_test_base.h"
-#include "ash/test/test_system_tray_delegate.h"
 #include "ash/wm/maximize_mode/maximize_mode_controller.h"
 #include "base/strings/string16.h"
 #include "base/strings/string_util.h"
diff --git a/ash/system/supervised/tray_supervised_user_unittest.cc b/ash/system/supervised/tray_supervised_user_unittest.cc
index 683df04..8faf755 100644
--- a/ash/system/supervised/tray_supervised_user_unittest.cc
+++ b/ash/system/supervised/tray_supervised_user_unittest.cc
@@ -12,7 +12,6 @@
 #include "ash/system/tray/system_tray_test_api.h"
 #include "ash/test/ash_test_base.h"
 #include "ash/test/test_session_controller_client.h"
-#include "ash/test/test_system_tray_delegate.h"
 #include "base/memory/ptr_util.h"
 #include "base/strings/utf_string_conversions.h"
 #include "ui/message_center/message_center.h"
diff --git a/ash/system/tray/system_tray_delegate.cc b/ash/system/tray/system_tray_delegate.cc
index 82c4ced6..c2d3b6e 100644
--- a/ash/system/tray/system_tray_delegate.cc
+++ b/ash/system/tray/system_tray_delegate.cc
@@ -4,8 +4,6 @@
 
 #include "ash/system/tray/system_tray_delegate.h"
 
-#include "ash/system/tray/ime_info.h"
-
 namespace ash {
 
 SystemTrayDelegate::SystemTrayDelegate() = default;
@@ -16,16 +14,6 @@
 
 void SystemTrayDelegate::ShowUserLogin() {}
 
-void SystemTrayDelegate::GetCurrentIME(IMEInfo* info) {}
-
-void SystemTrayDelegate::GetAvailableIMEList(IMEInfoList* list) {}
-
-void SystemTrayDelegate::GetCurrentIMEProperties(IMEPropertyInfoList* list) {}
-
-base::string16 SystemTrayDelegate::GetIMEManagedMessage() {
-  return base::string16();
-}
-
 NetworkingConfigDelegate* SystemTrayDelegate::GetNetworkingConfigDelegate()
     const {
   return nullptr;
diff --git a/ash/system/tray/system_tray_delegate.h b/ash/system/tray/system_tray_delegate.h
index 81ba1b1..a363254 100644
--- a/ash/system/tray/system_tray_delegate.h
+++ b/ash/system/tray/system_tray_delegate.h
@@ -5,18 +5,9 @@
 #ifndef ASH_SYSTEM_TRAY_SYSTEM_TRAY_DELEGATE_H_
 #define ASH_SYSTEM_TRAY_SYSTEM_TRAY_DELEGATE_H_
 
-#include <string>
-#include <vector>
-
 #include "ash/ash_export.h"
-#include "base/strings/string16.h"
 
 namespace ash {
-struct IMEInfo;
-struct IMEPropertyInfo;
-
-using IMEInfoList = std::vector<IMEInfo>;
-using IMEPropertyInfoList = std::vector<IMEPropertyInfo>;
 
 class NetworkingConfigDelegate;
 
@@ -38,18 +29,6 @@
   // Shows login UI to add other users to this session.
   virtual void ShowUserLogin();
 
-  // Returns the currently selected IME.
-  virtual void GetCurrentIME(IMEInfo* info);
-
-  // Returns a list of availble IMEs.
-  virtual void GetAvailableIMEList(IMEInfoList* list);
-
-  // Returns a list of properties for the currently selected IME.
-  virtual void GetCurrentIMEProperties(IMEPropertyInfoList* list);
-
-  // Returns a non-empty string if IMEs are managed by policy.
-  virtual base::string16 GetIMEManagedMessage();
-
   // Returns NetworkingConfigDelegate. May return nullptr.
   virtual NetworkingConfigDelegate* GetNetworkingConfigDelegate() const;
 
diff --git a/ash/system/web_notification/web_notification_tray_unittest.cc b/ash/system/web_notification/web_notification_tray_unittest.cc
index 5f487d8c..5a5d7ca 100644
--- a/ash/system/web_notification/web_notification_tray_unittest.cc
+++ b/ash/system/web_notification/web_notification_tray_unittest.cc
@@ -19,7 +19,6 @@
 #include "ash/system/web_notification/ash_popup_alignment_delegate.h"
 #include "ash/test/ash_test_base.h"
 #include "ash/test/status_area_widget_test_helper.h"
-#include "ash/test/test_system_tray_delegate.h"
 #include "ash/wm/window_state.h"
 #include "base/memory/ptr_util.h"
 #include "base/strings/stringprintf.h"
diff --git a/ash/test/BUILD.gn b/ash/test/BUILD.gn
index 41785fea3..d7e3f43 100644
--- a/ash/test/BUILD.gn
+++ b/ash/test/BUILD.gn
@@ -113,8 +113,6 @@
     "test_session_state_animator.h",
     "test_shell_delegate.cc",
     "test_shell_delegate.h",
-    "test_system_tray_delegate.cc",
-    "test_system_tray_delegate.h",
     "test_system_tray_item.cc",
     "test_system_tray_item.h",
     "test_wallpaper_delegate.cc",
diff --git a/ash/test/ash_test_base.cc b/ash/test/ash_test_base.cc
index ef6c1f1..5c556b9 100644
--- a/ash/test/ash_test_base.cc
+++ b/ash/test/ash_test_base.cc
@@ -25,7 +25,6 @@
 #include "ash/test/ash_test_helper.h"
 #include "ash/test/test_session_controller_client.h"
 #include "ash/test/test_shell_delegate.h"
-#include "ash/test/test_system_tray_delegate.h"
 #include "ash/wm/window_positioner.h"
 #include "services/ui/public/cpp/property_type_converters.h"
 #include "services/ui/public/interfaces/window_manager.mojom.h"
@@ -413,11 +412,6 @@
   return ash_test_helper_->test_session_controller_client();
 }
 
-TestSystemTrayDelegate* AshTestBase::GetSystemTrayDelegate() {
-  return static_cast<TestSystemTrayDelegate*>(
-      Shell::Get()->system_tray_delegate());
-}
-
 void AshTestBase::SetSessionStarted(bool session_started) {
   if (session_started)
     GetSessionControllerClient()->CreatePredefinedUserSessions(1);
diff --git a/ash/test/ash_test_base.h b/ash/test/ash_test_base.h
index e078e46..c6f0f42 100644
--- a/ash/test/ash_test_base.h
+++ b/ash/test/ash_test_base.h
@@ -58,7 +58,6 @@
 class AshTestHelper;
 class TestScreenshotDelegate;
 class TestSessionControllerClient;
-class TestSystemTrayDelegate;
 
 class AshTestBase : public testing::Test {
  public:
@@ -179,8 +178,6 @@
 
   TestSessionControllerClient* GetSessionControllerClient();
 
-  TestSystemTrayDelegate* GetSystemTrayDelegate();
-
   // Utility methods to emulate user logged in or not, session started or not
   // and user able to lock screen or not cases.
   void SetSessionStarted(bool session_started);
diff --git a/ash/test/ash_test_helper.cc b/ash/test/ash_test_helper.cc
index f506bfcc..b5b6f5d 100644
--- a/ash/test/ash_test_helper.cc
+++ b/ash/test/ash_test_helper.cc
@@ -24,7 +24,6 @@
 #include "ash/test/display_configuration_controller_test_api.h"
 #include "ash/test/test_screenshot_delegate.h"
 #include "ash/test/test_shell_delegate.h"
-#include "ash/test/test_system_tray_delegate.h"
 #include "base/memory/ptr_util.h"
 #include "base/run_loop.h"
 #include "base/strings/string_split.h"
diff --git a/ash/test/test_shell_delegate.cc b/ash/test/test_shell_delegate.cc
index 846c6e5..e010d9f 100644
--- a/ash/test/test_shell_delegate.cc
+++ b/ash/test/test_shell_delegate.cc
@@ -13,10 +13,10 @@
 #include "ash/shelf/shelf.h"
 #include "ash/shell.h"
 #include "ash/shell_observer.h"
+#include "ash/system/tray/system_tray_delegate.h"
 #include "ash/system/tray/system_tray_notifier.h"
 #include "ash/test/test_accessibility_delegate.h"
 #include "ash/test/test_keyboard_ui.h"
-#include "ash/test/test_system_tray_delegate.h"
 #include "ash/test/test_wallpaper_delegate.h"
 #include "ash/wm/window_state.h"
 #include "ash/wm/window_util.h"
@@ -102,7 +102,11 @@
 void TestShellDelegate::ShelfShutdown() {}
 
 SystemTrayDelegate* TestShellDelegate::CreateSystemTrayDelegate() {
-  return new TestSystemTrayDelegate;
+  return new SystemTrayDelegate;
+}
+
+ImeController* TestShellDelegate::GetImeController() {
+  return &stub_ime_controller_;
 }
 
 std::unique_ptr<WallpaperDelegate>
diff --git a/ash/test/test_shell_delegate.h b/ash/test/test_shell_delegate.h
index a5a0467..84e1131 100644
--- a/ash/test/test_shell_delegate.h
+++ b/ash/test/test_shell_delegate.h
@@ -8,6 +8,7 @@
 #include <memory>
 #include <string>
 
+#include "ash/ime/ime_controller.h"
 #include "ash/shell_delegate.h"
 #include "base/macros.h"
 
@@ -50,6 +51,7 @@
   void ShelfShutdown() override;
   void OpenUrlFromArc(const GURL& url) override;
   SystemTrayDelegate* CreateSystemTrayDelegate() override;
+  ImeController* GetImeController() override;
   std::unique_ptr<WallpaperDelegate> CreateWallpaperDelegate() override;
   AccessibilityDelegate* CreateAccessibilityDelegate() override;
   std::unique_ptr<PaletteDelegate> CreatePaletteDelegate() override;
@@ -80,6 +82,7 @@
   bool touchscreen_enabled_in_local_pref_ = true;
   bool media_sessions_suspended_ = false;
   std::unique_ptr<ShelfInitializer> shelf_initializer_;
+  ImeController stub_ime_controller_;
   PrefService* active_user_pref_service_ = nullptr;  // Not owned.
 
   DISALLOW_COPY_AND_ASSIGN(TestShellDelegate);
diff --git a/ash/test/test_system_tray_delegate.cc b/ash/test/test_system_tray_delegate.cc
deleted file mode 100644
index f14d374e9f..0000000
--- a/ash/test/test_system_tray_delegate.cc
+++ /dev/null
@@ -1,31 +0,0 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "ash/test/test_system_tray_delegate.h"
-
-namespace ash {
-namespace test {
-
-TestSystemTrayDelegate::TestSystemTrayDelegate() = default;
-
-TestSystemTrayDelegate::~TestSystemTrayDelegate() = default;
-
-void TestSystemTrayDelegate::SetCurrentIME(const IMEInfo& info) {
-  current_ime_ = info;
-}
-
-void TestSystemTrayDelegate::SetAvailableIMEList(const IMEInfoList& list) {
-  ime_list_ = list;
-}
-
-void TestSystemTrayDelegate::GetCurrentIME(IMEInfo* info) {
-  *info = current_ime_;
-}
-
-void TestSystemTrayDelegate::GetAvailableIMEList(IMEInfoList* list) {
-  *list = ime_list_;
-}
-
-}  // namespace test
-}  // namespace ash
diff --git a/ash/test/test_system_tray_delegate.h b/ash/test/test_system_tray_delegate.h
deleted file mode 100644
index 38548c0..0000000
--- a/ash/test/test_system_tray_delegate.h
+++ /dev/null
@@ -1,40 +0,0 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef ASH_TEST_TEST_SYSTEM_TRAY_DELEGATE_H_
-#define ASH_TEST_TEST_SYSTEM_TRAY_DELEGATE_H_
-
-#include "ash/system/tray/ime_info.h"
-#include "ash/system/tray/system_tray_delegate.h"
-#include "base/macros.h"
-
-namespace ash {
-namespace test {
-
-class TestSystemTrayDelegate : public SystemTrayDelegate {
- public:
-  TestSystemTrayDelegate();
-  ~TestSystemTrayDelegate() override;
-
-  // Sets the IME info.
-  void SetCurrentIME(const IMEInfo& info);
-
-  // Sets the list of available IMEs.
-  void SetAvailableIMEList(const IMEInfoList& list);
-
-  // SystemTrayDelegate:
-  void GetCurrentIME(IMEInfo* info) override;
-  void GetAvailableIMEList(IMEInfoList* list) override;
-
- private:
-  IMEInfo current_ime_;
-  IMEInfoList ime_list_;
-
-  DISALLOW_COPY_AND_ASSIGN(TestSystemTrayDelegate);
-};
-
-}  // namespace test
-}  // namespace ash
-
-#endif  // ASH_TEST_TEST_SYSTEM_TRAY_DELEGATE_H_
diff --git a/ash/wm/maximize_mode/maximize_mode_controller_unittest.cc b/ash/wm/maximize_mode/maximize_mode_controller_unittest.cc
index cf6bb66..4b813e4f 100644
--- a/ash/wm/maximize_mode/maximize_mode_controller_unittest.cc
+++ b/ash/wm/maximize_mode/maximize_mode_controller_unittest.cc
@@ -13,7 +13,6 @@
 #include "ash/shell.h"
 #include "ash/system/tray/system_tray_delegate.h"
 #include "ash/test/ash_test_base.h"
-#include "ash/test/test_system_tray_delegate.h"
 #include "ash/wm/overview/window_selector_controller.h"
 #include "base/command_line.h"
 #include "base/run_loop.h"
diff --git a/base/android/jni_generator/jni_generator.py b/base/android/jni_generator/jni_generator.py
index 99d8b424..d4c3523 100755
--- a/base/android/jni_generator/jni_generator.py
+++ b/base/android/jni_generator/jni_generator.py
@@ -420,7 +420,7 @@
   needed by non-browser processes must explicitly be annotated with @MainDex
   to force JNI registration.
   """
-  re_maindex = re.compile(r'@MainDex[\s\S]*class\s+\w+\s*{')
+  re_maindex = re.compile(r'@MainDex[\s\S]*class({|[\s\S]*{)')
   found = re.search(re_maindex, contents)
   return 'true' if found else 'false'
 
diff --git a/base/android/jni_generator/jni_generator_tests.py b/base/android/jni_generator/jni_generator_tests.py
index c0c82388..feac90a 100755
--- a/base/android/jni_generator/jni_generator_tests.py
+++ b/base/android/jni_generator/jni_generator_tests.py
@@ -18,7 +18,11 @@
 import sys
 import unittest
 import jni_generator
-from jni_generator import CalledByNative, JniParams, NativeMethod, Param
+from jni_generator import CalledByNative
+from jni_generator import IsMainDexJavaClass
+from jni_generator import JniParams
+from jni_generator import NativeMethod
+from jni_generator import Param
 
 
 SCRIPT_NAME = 'base/android/jni_generator/jni_generator.py'
@@ -977,6 +981,45 @@
       test_data, 'org/chromium/foo/Bar', options)
     self.assertGoldenTextEquals(jni_from_java.GetContent())
 
+  def testMainDexAnnotation(self):
+    mainDexEntries = [
+      '@MainDex public class Test {',
+      '@MainDex public class Test{',
+      """@MainDex
+         public class Test {
+      """,
+      """@MainDex public class Test
+         {
+      """,
+      '@MainDex /* This class is a test */ public class Test {',
+      '@MainDex public class Test implements java.io.Serializable {',
+      '@MainDex public class Test implements java.io.Serializable, Bidule {',
+      '@MainDex public class Test extends BaseTest {',
+      """@MainDex
+         public class Test extends BaseTest implements Bidule {
+      """,
+      """@MainDex
+         public class Test extends BaseTest implements Bidule, Machin, Chose {
+      """,
+      """@MainDex
+         public class Test implements Testable<java.io.Serializable> {
+      """,
+      '@MainDex public class Test implements Testable<java.io.Serializable> {',
+      '@MainDex public class Test extends Testable<java.io.Serializable> {',
+    ]
+    for entry in mainDexEntries:
+      self.assertEquals("true", IsMainDexJavaClass(entry))
+
+  def testNoMainDexAnnotation(self):
+    noMainDexEntries = [
+      'public class Test {',
+      '@NotMainDex public class Test {',
+      'public class Test implements java.io.Serializable {',
+      'public class Test extends BaseTest {'
+    ]
+    for entry in noMainDexEntries:
+      self.assertEquals("false", IsMainDexJavaClass(entry))
+
   def testNativeExportsOnlyOption(self):
     test_data = """
     package org.chromium.example.jni_generator;
diff --git a/chrome/BUILD.gn b/chrome/BUILD.gn
index 6cba336..cb1b62ee 100644
--- a/chrome/BUILD.gn
+++ b/chrome/BUILD.gn
@@ -1526,15 +1526,17 @@
   if (!is_android) {
     public_deps += [
       "//chrome/browser/resources:component_extension_resources",
-      "//chrome/browser/resources:options_resources",
       "//chrome/browser/resources:settings_resources",
     ]
   }
 
   if (is_chromeos) {
-    public_deps += [ "//chrome/browser/resources/chromeos/chromevox" ]
-    public_deps += [ "//chrome/browser/resources/chromeos/select_to_speak" ]
-    public_deps += [ "//chrome/browser/resources/chromeos/switch_access" ]
+    public_deps += [
+      "//chrome/browser/resources:options_resources",
+      "//chrome/browser/resources/chromeos/chromevox",
+      "//chrome/browser/resources/chromeos/select_to_speak",
+      "//chrome/browser/resources/chromeos/switch_access",
+    ]
   }
 
   if (enable_extensions) {
diff --git a/chrome/android/BUILD.gn b/chrome/android/BUILD.gn
index 22dc801e6..429b2d5 100644
--- a/chrome/android/BUILD.gn
+++ b/chrome/android/BUILD.gn
@@ -556,8 +556,11 @@
       "javatests/src/org/chromium/chrome/browser/vr_shell/mock/MockVrCoreVersionCheckerImpl.java",
       "javatests/src/org/chromium/chrome/browser/vr_shell/mock/MockVrDaydreamApi.java",
       "javatests/src/org/chromium/chrome/browser/vr_shell/nfc_apk/SimNfcActivity.java",
+      "javatests/src/org/chromium/chrome/browser/vr_shell/util/CardboardUtils.java",
       "javatests/src/org/chromium/chrome/browser/vr_shell/util/NfcSimUtils.java",
-      "javatests/src/org/chromium/chrome/browser/vr_shell/util/VrUtils.java",
+      "javatests/src/org/chromium/chrome/browser/vr_shell/util/VrInfoBarUtils.java",
+      "javatests/src/org/chromium/chrome/browser/vr_shell/util/VrShellDelegateUtils.java",
+      "javatests/src/org/chromium/chrome/browser/vr_shell/util/VrTransitionUtils.java",
       "javatests/src/org/chromium/chrome/browser/vr_shell/VrFeedbackInfoBarTest.java",
       "javatests/src/org/chromium/chrome/browser/vr_shell/VrShellTest.java",
       "javatests/src/org/chromium/chrome/browser/vr_shell/VrShellNavigationTest.java",
diff --git a/chrome/android/java/res/layout/bottom_toolbar_phone.xml b/chrome/android/java/res/layout/bottom_toolbar_phone.xml
index 9dc4891..fded2a5 100644
--- a/chrome/android/java/res/layout/bottom_toolbar_phone.xml
+++ b/chrome/android/java/res/layout/bottom_toolbar_phone.xml
@@ -10,7 +10,9 @@
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:id="@+id/toolbar"
     android:layout_width="match_parent"
-    android:layout_height="match_parent" >
+    android:layout_height="match_parent"
+    android:focusable="true"
+    android:focusableInTouchMode="true" >
 
     <include layout="@layout/toolbar_phone_common"/>
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManager.java b/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManager.java
index 1b5e0a5..53e907b3 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManager.java
@@ -431,9 +431,9 @@
         if (isTap && mPolicy.shouldPreviousTapResolve()) {
             // Cache the native translate data, so JNI calls won't be made when time-critical.
             mTranslateController.cacheNativeTranslateData();
-        } else {
+        } else if (!TextUtils.isEmpty(selection)) {
             boolean shouldPrefetch = mPolicy.shouldPrefetchSearchResult();
-            mSearchRequest = createContextualSearchRequest(selection, null, null, shouldPrefetch);
+            mSearchRequest = new ContextualSearchRequest(selection, null, null, shouldPrefetch);
             mTranslateController.forceAutoDetectTranslateUnlessDisabled(mSearchRequest);
             mDidStartLoadingResolvedSearchRequest = false;
             mSearchPanel.setSearchTerm(selection);
@@ -447,6 +447,10 @@
                 RecordUserAction.record(isSingleWord ? "ContextualSearch.ManualRefineSingleWord"
                                                      : "ContextualSearch.ManualRefineMultiWord");
             }
+        } else {
+            // The selection is no longer valid, so we can't build a request.  Don't show the UX.
+            hideContextualSearch(StateChangeReason.UNKNOWN);
+            return;
         }
         mWereSearchResultsSeen = false;
 
@@ -503,18 +507,6 @@
         }
     }
 
-    /**
-     * A method that can override the creation of a standard search request. This should only be
-     * used for testing.
-     * @param term The search term to create the request with.
-     * @param altTerm An alternate search term.
-     * @param isLowPriorityEnabled Whether the request can be made at low priority.
-     */
-    protected ContextualSearchRequest createContextualSearchRequest(
-            String term, String altTerm, String mid, boolean isLowPriorityEnabled) {
-        return new ContextualSearchRequest(term, altTerm, mid, isLowPriorityEnabled);
-    }
-
     /** Accessor for the {@code InfoBarContainer} currently attached to the {@code Tab}. */
     private InfoBarContainer getInfoBarContainer() {
         Tab tab = mActivity.getActivityTab();
@@ -710,7 +702,7 @@
             // appear in the user's history until the user views it).  See crbug.com/406446.
             boolean shouldPreload = !doPreventPreload && mPolicy.shouldPrefetchSearchResult();
             mSearchRequest =
-                    createContextualSearchRequest(searchTerm, alternateTerm, mid, shouldPreload);
+                    new ContextualSearchRequest(searchTerm, alternateTerm, mid, shouldPreload);
             // Trigger translation, if enabled.
             mTranslateController.forceTranslateIfNeeded(mSearchRequest, contextLanguage);
             mDidStartLoadingResolvedSearchRequest = false;
@@ -948,8 +940,9 @@
                 mWereSearchResultsSeen = true;
                 // If there's no current request, then either a search term resolution
                 // is in progress or we should do a verbatim search now.
-                if (mSearchRequest == null && mPolicy.shouldCreateVerbatimRequest()) {
-                    mSearchRequest = createContextualSearchRequest(
+                if (mSearchRequest == null && mPolicy.shouldCreateVerbatimRequest()
+                        && !TextUtils.isEmpty(mSelectionController.getSelectedText())) {
+                    mSearchRequest = new ContextualSearchRequest(
                             mSelectionController.getSelectedText(), null, null, false);
                     mDidStartLoadingResolvedSearchRequest = false;
                 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/infobar/InfoBarContainerLayout.java b/chrome/android/java/src/org/chromium/chrome/browser/infobar/InfoBarContainerLayout.java
index babe13a61c..fd259f0 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/infobar/InfoBarContainerLayout.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/infobar/InfoBarContainerLayout.java
@@ -218,8 +218,8 @@
             Animator.AnimatorListener listener = new AnimatorListenerAdapter() {
                 @Override
                 public void onAnimationEnd(Animator animation) {
-                    mAnimation = null;
                     InfoBarAnimation.this.onAnimationEnd();
+                    mAnimation = null;
                     for (InfoBarAnimationListener listener : mAnimationListeners) {
                         listener.notifyAnimationFinished(getAnimationType());
                     }
@@ -448,13 +448,6 @@
                 public void onAnimationStart(Animator animation) {
                     setHierarchyClipsChildren(false);
                 }
-
-                @Override
-                public void onAnimationEnd(Animator animation) {
-                    mAppearingWrapper.setRestrictHeightForAnimation(false);
-                    mAppearingWrapper.removeView(mAppearingWrapper.getItem().getView());
-                    setHierarchyClipsChildren(true);
-                }
             });
 
             set.playSequentially(animators);
@@ -464,6 +457,13 @@
         }
 
         @Override
+        public void onAnimationEnd() {
+            mAppearingWrapper.setRestrictHeightForAnimation(false);
+            mAppearingWrapper.removeView(mAppearingWrapper.getItem().getView());
+            setHierarchyClipsChildren(true);
+        }
+
+        @Override
         int getAnimationType() {
             return InfoBarAnimationListener.ANIMATION_TYPE_SHOW;
         }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/BottomToolbarPhone.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/BottomToolbarPhone.java
index 8570bc39..b81afc13 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/BottomToolbarPhone.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/BottomToolbarPhone.java
@@ -34,6 +34,7 @@
 import org.chromium.chrome.browser.widget.bottomsheet.BottomSheetMetrics;
 import org.chromium.chrome.browser.widget.bottomsheet.BottomSheetObserver;
 import org.chromium.chrome.browser.widget.bottomsheet.EmptyBottomSheetObserver;
+import org.chromium.ui.UiUtils;
 import org.chromium.ui.base.LocalizationUtils;
 import org.chromium.ui.interpolators.BakedBezierInterpolator;
 
@@ -306,7 +307,8 @@
         if (mProgressBar == null) return;
 
         ViewGroup coordinator = (ViewGroup) getRootView().findViewById(R.id.coordinator);
-        coordinator.addView(mProgressBar);
+        UiUtils.insertBefore(coordinator, mProgressBar, mBottomSheet);
+
         mProgressBar.setProgressBarContainer(coordinator);
     }
 
@@ -439,8 +441,6 @@
         if (!mUseToolbarHandle) {
             initExpandButton();
         } else {
-            setFocusable(true);
-            setFocusableInTouchMode(true);
             setContentDescription(
                     getResources().getString(R.string.bottom_sheet_accessibility_toolbar));
         }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/widget/selection/SelectableListLayout.java b/chrome/android/java/src/org/chromium/chrome/browser/widget/selection/SelectableListLayout.java
index 14c4621..ab76880 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/widget/selection/SelectableListLayout.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/widget/selection/SelectableListLayout.java
@@ -279,6 +279,7 @@
         mAdapter.unregisterAdapterDataObserver(mAdapterObserver);
         mToolbar.getSelectionDelegate().removeObserver(this);
         mToolbar.destroy();
+        mRecyclerView.setAdapter(null);
     }
 
     /**
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchTapEventTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchTapEventTest.java
index 6f910e6..0b23f5bd 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchTapEventTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchTapEventTest.java
@@ -5,7 +5,6 @@
 package org.chromium.chrome.browser.contextualsearch;
 
 import android.content.Context;
-import android.net.Uri;
 import android.support.test.filters.SmallTest;
 import android.widget.LinearLayout;
 
@@ -35,8 +34,6 @@
 import org.chromium.ui.resources.dynamics.DynamicResourceLoader;
 import org.chromium.ui.touch_selection.SelectionEventType;
 
-import javax.annotation.Nullable;
-
 /**
  * Mock touch events with Contextual Search to test behavior of its panel and manager.
  */
@@ -55,21 +52,6 @@
     private OverlayPanelManagerWrapper mPanelManager;
     private SelectionClient mContextualSearchClient;
 
-    /**
-     * A ContextualSearchRequest that foregoes URI template lookup.
-     */
-    private static class MockContextualSearchRequest extends ContextualSearchRequest {
-        public MockContextualSearchRequest(String term, String altTerm, boolean prefetch) {
-            super(term, altTerm, "", prefetch);
-        }
-
-        @Override
-        protected Uri getUriTemplate(
-                String query, @Nullable String alternateTerm, String mid, boolean shouldPrefetch) {
-            return Uri.parse("");
-        }
-    }
-
     // --------------------------------------------------------------------------------------------
 
     /**
@@ -119,12 +101,6 @@
         }
 
         @Override
-        protected ContextualSearchRequest createContextualSearchRequest(
-                String query, String altTerm, String mid, boolean shouldPrefetch) {
-            return new MockContextualSearchRequest(query, altTerm, shouldPrefetch);
-        }
-
-        @Override
         protected void nativeGatherSurroundingText(long nativeContextualSearchManager,
                 ContextualSearchContext contextualSearchContext, WebContents baseWebContents) {}
 
@@ -349,4 +325,31 @@
         Assert.assertEquals(mPanelManager.getRequestPanelShowCount(), 1);
         Assert.assertEquals(mPanelManager.getPanelHideCount(), 0);
     }
+
+    /**
+     * Tests that a Tap gesture processing is robust even when the selection somehow gets cleared
+     * during that process.  This tests a failure-case found in crbug.com/728644.
+     */
+    @Test
+    @SmallTest
+    @Feature({"ContextualSearch"})
+    @Restriction(Restriction.RESTRICTION_TYPE_NON_LOW_END_DEVICE)
+    public void testTapProcessIsRobustWhenSelectionGetsCleared() throws InterruptedException {
+        Assert.assertEquals(mPanelManager.getRequestPanelShowCount(), 0);
+
+        // Fake a Tap event.
+        mockTapText("text");
+        // Generate the surrounding-text-available callback.
+        generateTextSurroundingSelectionAvailable();
+
+        // Now clear the selection!
+        mContextualSearchManager.getSelectionController().clearSelection();
+
+        // Continue processing the Tap by acknowledging the SelectWordAroundCaret has selected the
+        // word.  However we just simulated a condition that clears the selection above, so we're
+        // testing for robustness in completion of the processing even when there's no selection.
+        generateSelectWordAroundCaretAck();
+        Assert.assertEquals(mPanelManager.getRequestPanelShowCount(), 0);
+        Assert.assertEquals(mPanelManager.getPanelHideCount(), 0);
+    }
 }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/vr_shell/VrFeedbackInfoBarTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/vr_shell/VrFeedbackInfoBarTest.java
index e09ab77..33862a4 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/vr_shell/VrFeedbackInfoBarTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/vr_shell/VrFeedbackInfoBarTest.java
@@ -5,8 +5,8 @@
 package org.chromium.chrome.browser.vr_shell;
 
 import static org.chromium.chrome.browser.vr_shell.VrTestRule.PAGE_LOAD_TIMEOUT_S;
-import static org.chromium.chrome.browser.vr_shell.util.VrUtils.POLL_TIMEOUT_LONG_MS;
-import static org.chromium.chrome.browser.vr_shell.util.VrUtils.POLL_TIMEOUT_SHORT_MS;
+import static org.chromium.chrome.browser.vr_shell.VrTestRule.POLL_TIMEOUT_LONG_MS;
+import static org.chromium.chrome.browser.vr_shell.VrTestRule.POLL_TIMEOUT_SHORT_MS;
 import static org.chromium.chrome.test.util.ChromeRestriction.RESTRICTION_TYPE_VIEWER_DAYDREAM;
 
 import android.support.test.filters.MediumTest;
@@ -17,18 +17,16 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
-import org.chromium.base.ThreadUtils;
 import org.chromium.base.test.util.CommandLineFlags;
 import org.chromium.base.test.util.Restriction;
 import org.chromium.chrome.browser.ChromeSwitches;
-import org.chromium.chrome.browser.infobar.InfoBar;
-import org.chromium.chrome.browser.vr_shell.util.VrUtils;
+import org.chromium.chrome.browser.vr_shell.util.VrInfoBarUtils;
+import org.chromium.chrome.browser.vr_shell.util.VrShellDelegateUtils;
+import org.chromium.chrome.browser.vr_shell.util.VrTransitionUtils;
 import org.chromium.chrome.test.ChromeActivityTestRule;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
 import org.chromium.chrome.test.util.ChromeTabUtils;
-import org.chromium.chrome.test.util.InfoBarUtil;
 
-import java.util.List;
 import java.util.concurrent.TimeoutException;
 
 /**
@@ -54,43 +52,16 @@
         Assert.assertFalse(VrFeedbackStatus.getFeedbackOptOut());
     }
 
-    private void clickInfoBarButton(final boolean primary) {
-        final List<InfoBar> infoBars = mVrTestRule.getInfoBars();
-        ThreadUtils.runOnUiThreadBlocking(new Runnable() {
-            @Override
-            public void run() {
-                if (primary) {
-                    InfoBarUtil.clickPrimaryButton(infoBars.get(0));
-                } else {
-                    InfoBarUtil.clickSecondaryButton(infoBars.get(0));
-                }
-            }
-        });
-        InfoBarUtil.waitUntilNoInfoBarsExist(mVrTestRule.getInfoBars());
-    }
-
-    private void clickInfobarCloseButton() {
-        final List<InfoBar> infoBars = mVrTestRule.getInfoBars();
-        ThreadUtils.runOnUiThreadBlocking(new Runnable() {
-            @Override
-            public void run() {
-                InfoBarUtil.clickCloseButton(infoBars.get(0));
-            }
-        });
-        InfoBarUtil.waitUntilNoInfoBarsExist(mVrTestRule.getInfoBars());
-    }
-
     private void assertState(boolean isInVr, boolean isInfobarVisible) {
         Assert.assertEquals("Browser is in VR", isInVr, VrShellDelegate.isInVr());
-        VrUtils.expectInfoBarPresent(
-                mVrTestRule.getActivity().getWindow().getDecorView(), isInfobarVisible);
+        VrInfoBarUtils.expectInfoBarPresent(mVrTestRule, isInfobarVisible);
     }
 
     private void enterThenExitVr() {
-        VrUtils.forceEnterVr();
-        VrUtils.waitForVrSupported(POLL_TIMEOUT_LONG_MS);
+        VrTransitionUtils.forceEnterVr();
+        VrTransitionUtils.waitForVrEntry(POLL_TIMEOUT_LONG_MS);
         assertState(true /* isInVr */, false /* isInfobarVisible  */);
-        VrUtils.forceExitVr(VrUtils.getVrShellDelegateInstance());
+        VrTransitionUtils.forceExitVr();
     }
 
     /**
@@ -101,12 +72,12 @@
     public void testFeedbackFrequency() throws InterruptedException, TimeoutException {
         mVrTestRule.loadUrlAndAwaitInitialization(TEST_PAGE_2D_URL, PAGE_LOAD_TIMEOUT_S);
         // Set frequency of infobar to every 2nd time.
-        VrUtils.getVrShellDelegateInstance().setFeedbackFrequencyForTesting(2);
+        VrShellDelegateUtils.getDelegateInstance().setFeedbackFrequencyForTesting(2);
 
         // Verify that the Feedback infobar is visible when exiting VR.
         enterThenExitVr();
         assertState(false /* isInVr */, true /* isInfobarVisible  */);
-        clickInfobarCloseButton();
+        VrInfoBarUtils.clickInfobarCloseButton(mVrTestRule);
 
         // Feedback infobar shouldn't show up this time.
         enterThenExitVr();
@@ -115,7 +86,7 @@
         // Feedback infobar should show up again.
         enterThenExitVr();
         assertState(false /* isInVr */, true /* isInfobarVisible  */);
-        clickInfobarCloseButton();
+        VrInfoBarUtils.clickInfobarCloseButton(mVrTestRule);
     }
 
     /**
@@ -127,14 +98,14 @@
         mVrTestRule.loadUrlAndAwaitInitialization(TEST_PAGE_2D_URL, PAGE_LOAD_TIMEOUT_S);
 
         // Show infobar every time.
-        VrUtils.getVrShellDelegateInstance().setFeedbackFrequencyForTesting(1);
+        VrShellDelegateUtils.getDelegateInstance().setFeedbackFrequencyForTesting(1);
 
         // The infobar should show the first time.
         enterThenExitVr();
         assertState(false /* isInVr */, true /* isInfobarVisible  */);
 
         // Opt-out of seeing the infobar.
-        clickInfoBarButton(false /* primary */);
+        VrInfoBarUtils.clickInfoBarButton(VrInfoBarUtils.Button.SECONDARY, mVrTestRule);
         Assert.assertTrue(VrFeedbackStatus.getFeedbackOptOut());
 
         // The infobar should not show because the user opted out.
@@ -152,13 +123,13 @@
         mVrTestRule.loadUrlAndAwaitInitialization(TEST_PAGE_WEBVR_URL, PAGE_LOAD_TIMEOUT_S);
         Assert.assertTrue("VRDisplay found",
                 mVrTestRule.vrDisplayFound(mVrTestRule.getFirstTabWebContents()));
-        mVrTestRule.enterPresentationAndWait(
+        VrTransitionUtils.enterPresentationAndWait(
                 mVrTestRule.getFirstTabCvc(), mVrTestRule.getFirstTabWebContents());
         assertState(true /* isInVr */, false /* isInfobarVisible  */);
         Assert.assertTrue(VrShellDelegate.getVrShellForTesting().getWebVrModeEnabled());
 
         // Exiting VR should not prompt for feedback since the no VR browsing was performed.
-        VrUtils.forceExitVr(VrUtils.getVrShellDelegateInstance());
+        VrTransitionUtils.forceExitVr();
         assertState(false /* isInVr */, false /* isInfobarVisible  */);
     }
 
@@ -172,7 +143,7 @@
         mVrTestRule.loadUrlAndAwaitInitialization(TEST_PAGE_WEBVR_URL, PAGE_LOAD_TIMEOUT_S);
         Assert.assertTrue("VRDisplay found",
                 mVrTestRule.vrDisplayFound(mVrTestRule.getFirstTabWebContents()));
-        mVrTestRule.enterPresentationAndWait(
+        VrTransitionUtils.enterPresentationAndWait(
                 mVrTestRule.getFirstTabCvc(), mVrTestRule.getFirstTabWebContents());
         assertState(true /* isInVr */, false /* isInfobarVisible  */);
         Assert.assertTrue(VrShellDelegate.getVrShellForTesting().getWebVrModeEnabled());
@@ -189,7 +160,7 @@
                 }, POLL_TIMEOUT_LONG_MS);
 
         // Exiting VR should prompt for feedback since 2D browsing was performed after.
-        VrUtils.forceExitVr(VrUtils.getVrShellDelegateInstance());
+        VrTransitionUtils.forceExitVr();
         assertState(false /* isInVr */, true /* isInfobarVisible  */);
     }
 }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/vr_shell/VrShellNavigationTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/vr_shell/VrShellNavigationTest.java
index 2d2b76c..60b686f 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/vr_shell/VrShellNavigationTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/vr_shell/VrShellNavigationTest.java
@@ -5,8 +5,8 @@
 package org.chromium.chrome.browser.vr_shell;
 
 import static org.chromium.chrome.browser.vr_shell.VrTestRule.PAGE_LOAD_TIMEOUT_S;
-import static org.chromium.chrome.browser.vr_shell.util.VrUtils.POLL_TIMEOUT_LONG_MS;
-import static org.chromium.chrome.browser.vr_shell.util.VrUtils.POLL_TIMEOUT_SHORT_MS;
+import static org.chromium.chrome.browser.vr_shell.VrTestRule.POLL_TIMEOUT_LONG_MS;
+import static org.chromium.chrome.browser.vr_shell.VrTestRule.POLL_TIMEOUT_SHORT_MS;
 import static org.chromium.chrome.test.util.ChromeRestriction.RESTRICTION_TYPE_VIEWER_DAYDREAM;
 
 import android.support.test.filters.MediumTest;
@@ -20,7 +20,8 @@
 import org.chromium.base.test.util.CommandLineFlags;
 import org.chromium.base.test.util.Restriction;
 import org.chromium.chrome.browser.ChromeSwitches;
-import org.chromium.chrome.browser.vr_shell.util.VrUtils;
+import org.chromium.chrome.browser.vr_shell.util.VrInfoBarUtils;
+import org.chromium.chrome.browser.vr_shell.util.VrTransitionUtils;
 import org.chromium.chrome.test.ChromeActivityTestCaseBase;
 import org.chromium.chrome.test.ChromeActivityTestRule;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
@@ -55,8 +56,8 @@
 
     @Before
     public void setUp() throws Exception {
-        VrUtils.forceEnterVr();
-        VrUtils.waitForVrSupported(POLL_TIMEOUT_LONG_MS);
+        VrTransitionUtils.forceEnterVr();
+        VrTransitionUtils.waitForVrEntry(POLL_TIMEOUT_LONG_MS);
     }
 
     private String getUrl(Page page) {
@@ -96,7 +97,7 @@
 
     private void enterPresentationOrFail(ContentViewCore cvc)
             throws InterruptedException, TimeoutException {
-        mVrTestRule.enterPresentation(cvc);
+        VrTransitionUtils.enterPresentation(cvc);
         mVrTestRule.pollJavaScriptBoolean("vrDisplay.isPresenting", POLL_TIMEOUT_SHORT_MS,
                 mVrTestRule.getFirstTabWebContents());
         Assert.assertTrue(VrShellDelegate.getVrShellForTesting().getWebVrModeEnabled());
@@ -112,7 +113,7 @@
         Assert.assertEquals("Browser is in fullscreen",
                 fullscreenMode == FullscreenMode.FULLSCREENED, DOMUtils.isFullscreen(wc));
         // Feedback infobar should never show up during navigations.
-        VrUtils.expectInfoBarPresent(mVrTestRule.getActivity().getWindow().getDecorView(), false);
+        VrInfoBarUtils.expectInfoBarPresent(mVrTestRule, false);
     }
 
     /**
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/vr_shell/VrShellTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/vr_shell/VrShellTest.java
index 3033484..6e8f6ec 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/vr_shell/VrShellTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/vr_shell/VrShellTest.java
@@ -5,8 +5,8 @@
 package org.chromium.chrome.browser.vr_shell;
 
 import static org.chromium.chrome.browser.vr_shell.VrTestRule.PAGE_LOAD_TIMEOUT_S;
-import static org.chromium.chrome.browser.vr_shell.util.VrUtils.POLL_CHECK_INTERVAL_LONG_MS;
-import static org.chromium.chrome.browser.vr_shell.util.VrUtils.POLL_TIMEOUT_LONG_MS;
+import static org.chromium.chrome.browser.vr_shell.VrTestRule.POLL_CHECK_INTERVAL_LONG_MS;
+import static org.chromium.chrome.browser.vr_shell.VrTestRule.POLL_TIMEOUT_LONG_MS;
 import static org.chromium.chrome.test.util.ChromeRestriction.RESTRICTION_TYPE_DEVICE_DAYDREAM;
 import static org.chromium.chrome.test.util.ChromeRestriction.RESTRICTION_TYPE_DEVICE_NON_DAYDREAM;
 import static org.chromium.chrome.test.util.ChromeRestriction.RESTRICTION_TYPE_VIEWER_DAYDREAM;
@@ -31,7 +31,9 @@
 import org.chromium.chrome.browser.compositor.CompositorViewHolder;
 import org.chromium.chrome.browser.tabmodel.TabModelSelector;
 import org.chromium.chrome.browser.vr_shell.mock.MockVrDaydreamApi;
-import org.chromium.chrome.browser.vr_shell.util.VrUtils;
+import org.chromium.chrome.browser.vr_shell.util.NfcSimUtils;
+import org.chromium.chrome.browser.vr_shell.util.VrShellDelegateUtils;
+import org.chromium.chrome.browser.vr_shell.util.VrTransitionUtils;
 import org.chromium.chrome.test.ChromeActivityTestRule;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
 import org.chromium.chrome.test.ChromeTabbedActivityTestRule;
@@ -61,7 +63,7 @@
     @Before
     public void setUp() throws Exception {
         mActivityTestRule.startMainActivityOnBlankPage();
-        mDelegate = VrUtils.getVrShellDelegateInstance();
+        mDelegate = VrShellDelegateUtils.getDelegateInstance();
     }
 
     private void enterExitVrMode(boolean supported) {
@@ -69,28 +71,28 @@
         if (!supported) {
             mDelegate.overrideDaydreamApiForTesting(mockApi);
         }
-        VrUtils.forceEnterVr();
+        VrTransitionUtils.forceEnterVr();
         if (supported) {
-            VrUtils.waitForVrSupported(POLL_TIMEOUT_LONG_MS);
+            VrTransitionUtils.waitForVrEntry(POLL_TIMEOUT_LONG_MS);
             Assert.assertTrue(VrShellDelegate.isInVr());
         } else {
             Assert.assertFalse(mockApi.getLaunchInVrCalled());
             Assert.assertFalse(VrShellDelegate.isInVr());
         }
-        VrUtils.forceExitVr(mDelegate);
+        VrTransitionUtils.forceExitVr();
         Assert.assertFalse(VrShellDelegate.isInVr());
     }
 
     private void enterVrModeNfc(boolean supported) {
         InstrumentationRegistry.getInstrumentation().waitForIdleSync();
-        VrUtils.simNfcScan(mActivityTestRule.getActivity());
+        NfcSimUtils.simNfcScan(mActivityTestRule.getActivity());
         if (supported) {
-            VrUtils.waitForVrSupported(POLL_TIMEOUT_LONG_MS);
+            VrTransitionUtils.waitForVrEntry(POLL_TIMEOUT_LONG_MS);
             Assert.assertTrue(VrShellDelegate.isInVr());
         } else {
             Assert.assertFalse(VrShellDelegate.isInVr());
         }
-        VrUtils.forceExitVr(mDelegate);
+        VrTransitionUtils.forceExitVr();
         // TODO(bsheedy): Figure out why NFC tests cause the next test to fail
         // to enter VR unless we sleep for some amount of time after exiting VR
         // in the NFC test
@@ -150,8 +152,8 @@
     public void testControllerScrolling() throws InterruptedException {
         // Load page in VR and make sure the controller is pointed at the content quad
         mActivityTestRule.loadUrl("chrome://credits", PAGE_LOAD_TIMEOUT_S);
-        VrUtils.forceEnterVr();
-        VrUtils.waitForVrSupported(POLL_TIMEOUT_LONG_MS);
+        VrTransitionUtils.forceEnterVr();
+        VrTransitionUtils.waitForVrEntry(POLL_TIMEOUT_LONG_MS);
         EmulatedVrController controller = new EmulatedVrController(mActivityTestRule.getActivity());
         final ContentViewCore cvc =
                 mActivityTestRule.getActivity().getActivityTab().getActiveContentViewCore();
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/vr_shell/VrTestRule.java b/chrome/android/javatests/src/org/chromium/chrome/browser/vr_shell/VrTestRule.java
index c9f47d5..2e8948d 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/vr_shell/VrTestRule.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/vr_shell/VrTestRule.java
@@ -4,28 +4,16 @@
 
 package org.chromium.chrome.browser.vr_shell;
 
-import static org.chromium.chrome.browser.vr_shell.util.VrUtils.POLL_CHECK_INTERVAL_LONG_MS;
-import static org.chromium.chrome.browser.vr_shell.util.VrUtils.POLL_CHECK_INTERVAL_SHORT_MS;
-import static org.chromium.chrome.browser.vr_shell.util.VrUtils.POLL_TIMEOUT_LONG_MS;
-import static org.chromium.chrome.browser.vr_shell.util.VrUtils.POLL_TIMEOUT_SHORT_MS;
-
-import android.app.Activity;
-import android.support.test.InstrumentationRegistry;
-
 import org.junit.Assert;
 import org.junit.runner.Description;
 import org.junit.runners.model.Statement;
 
 import org.chromium.base.Log;
 import org.chromium.base.test.util.UrlUtils;
-import org.chromium.chrome.browser.ChromeTabbedActivity;
-import org.chromium.chrome.browser.vr_shell.util.VrUtils;
 import org.chromium.chrome.test.ChromeTabbedActivityTestRule;
 import org.chromium.content.browser.ContentViewCore;
-import org.chromium.content.browser.test.util.ClickUtils;
 import org.chromium.content.browser.test.util.Criteria;
 import org.chromium.content.browser.test.util.CriteriaHelper;
-import org.chromium.content.browser.test.util.DOMUtils;
 import org.chromium.content.browser.test.util.JavaScriptUtils;
 import org.chromium.content_public.browser.WebContents;
 
@@ -54,9 +42,14 @@
  * which can then grab the results and pass/fail the instrumentation test.
  */
 public class VrTestRule extends ChromeTabbedActivityTestRule {
+    public static final int PAGE_LOAD_TIMEOUT_S = 10;
+    public static final int POLL_CHECK_INTERVAL_SHORT_MS = 50;
+    public static final int POLL_CHECK_INTERVAL_LONG_MS = 100;
+    public static final int POLL_TIMEOUT_SHORT_MS = 1000;
+    public static final int POLL_TIMEOUT_LONG_MS = 10000;
+
     private static final String TAG = "VrTestRule";
     static final String TEST_DIR = "chrome/test/data/android/webvr_instrumentation";
-    static final int PAGE_LOAD_TIMEOUT_S = 10;
 
     private WebContents mFirstTabWebContents;
     private ContentViewCore mFirstTabCvc;
@@ -113,66 +106,11 @@
      * @param webContents The WebContents to run the JavaScript through.
      * @return Whether a VRDisplay was found.
      */
-    public boolean vrDisplayFound(WebContents webContents) {
+    public static boolean vrDisplayFound(WebContents webContents) {
         return !runJavaScriptOrFail("vrDisplay", POLL_TIMEOUT_SHORT_MS, webContents).equals("null");
     }
 
     /**
-     * Use to simulate a cardboard click by sending a touch event to the device.
-     * @param activity The activity to send the cardboard click to.
-     */
-    public void sendCardboardClick(Activity activity) {
-        ClickUtils.mouseSingleClickView(InstrumentationRegistry.getInstrumentation(),
-                activity.getWindow().getDecorView().getRootView());
-    }
-
-    /**
-     * Sends a cardboard click then waits for the JavaScript step to finish.
-     * @param activity The activity to send the cardboard click to.
-     * @param webContents The WebContents for the tab the JavaScript step is in.
-     */
-    public void sendCardboardClickAndWait(Activity activity, WebContents webContents) {
-        sendCardboardClick(activity);
-        waitOnJavaScriptStep(webContents);
-    }
-
-    /**
-     * Sends a click event directly to the WebGL canvas, triggering its onclick
-     * that by default calls WebVR's requestPresent. Will have a similar result to
-     * sendCardboardClick if not already presenting, but less prone to errors, e.g.
-     * if there's dialog in the center of the screen blocking the canvas.
-     * @param cvc The ContentViewCore for the tab the canvas is in.
-     */
-    public void enterPresentation(ContentViewCore cvc) {
-        try {
-            DOMUtils.clickNode(cvc, "webgl-canvas");
-        } catch (InterruptedException | TimeoutException e) {
-            Assert.fail("Failed to click canvas to enter presentation: " + e.toString());
-        }
-    }
-
-    /**
-     * Sends a click event directly to the WebGL canvas then waits for the
-     * JavaScript step to finish.
-     * @param cvc The ContentViewCore for the tab the canvas is in.
-     * @param webContents The WebContents for the tab the JavaScript step is in.
-     */
-    public void enterPresentationAndWait(ContentViewCore cvc, WebContents webContents) {
-        enterPresentation(cvc);
-        waitOnJavaScriptStep(webContents);
-    }
-
-    /**
-     * Simulate an NFC scan and wait for the JavaScript code in the given
-     * WebContents to signal that it is done with the step.
-     * @param webContents The WebContents for the JavaScript that will be polled.
-     */
-    public void simNfcScanAndWait(ChromeTabbedActivity activity, WebContents webContents) {
-        VrUtils.simNfcScan(activity);
-        waitOnJavaScriptStep(webContents);
-    }
-
-    /**
      * Helper function to run the given JavaScript, return the return value,
      * and fail if a timeout/interrupt occurs so we don't have to catch or
      * declare exceptions all the time.
@@ -181,7 +119,7 @@
      * @param webContents The WebContents object to run the JavaScript in.
      * @return The return value of the JavaScript.
      */
-    public String runJavaScriptOrFail(String js, int timeout, WebContents webContents) {
+    public static String runJavaScriptOrFail(String js, int timeout, WebContents webContents) {
         try {
             return JavaScriptUtils.executeJavaScriptAndWaitForResult(
                     webContents, js, timeout, TimeUnit.MILLISECONDS);
@@ -196,7 +134,7 @@
      * @param webContents The WebContents for the tab to check results in.
      * @return "Passed" if test passed, String with failure reason otherwise.
      */
-    public String checkResults(WebContents webContents) {
+    public static String checkResults(WebContents webContents) {
         if (runJavaScriptOrFail("testPassed", POLL_TIMEOUT_SHORT_MS, webContents).equals("true")) {
             return "Passed";
         }
@@ -208,7 +146,7 @@
      * setting the failure reason as the description if it didn't.
      * @param webContents The WebContents for the tab to check test results in.
      */
-    public void endTest(WebContents webContents) {
+    public static void endTest(WebContents webContents) {
         Assert.assertEquals("Passed", checkResults(webContents));
     }
 
@@ -220,7 +158,7 @@
      * @param webContents The WebContents to run the JavaScript through.
      * @return True if the boolean evaluated to true, false if timed out.
      */
-    public boolean pollJavaScriptBoolean(
+    public static boolean pollJavaScriptBoolean(
             final String boolName, int timeoutMs, final WebContents webContents) {
         try {
             CriteriaHelper.pollInstrumentationThread(Criteria.equals(true, new Callable<Boolean>() {
@@ -248,7 +186,7 @@
      * instead of timing out.
      * @param webContents The WebContents for the tab the JavaScript step is in.
      */
-    public void waitOnJavaScriptStep(WebContents webContents) {
+    public static void waitOnJavaScriptStep(WebContents webContents) {
         Assert.assertTrue("Polling JavaScript boolean javascriptDone timed out",
                 pollJavaScriptBoolean("javascriptDone", POLL_TIMEOUT_LONG_MS, webContents));
         // Reset the synchronization boolean
@@ -260,7 +198,7 @@
      * @param stepFunction The JavaScript step function to call.
      * @param webContents The WebContents for the tab the JavaScript is in.
      */
-    public void executeStepAndWait(String stepFunction, WebContents webContents) {
+    public static void executeStepAndWait(String stepFunction, WebContents webContents) {
         // Run the step and block
         JavaScriptUtils.executeJavaScript(webContents, stepFunction);
         waitOnJavaScriptStep(webContents);
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/vr_shell/WebVrTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/vr_shell/WebVrTest.java
index 6cb8ad0..f4a8d39 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/vr_shell/WebVrTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/vr_shell/WebVrTest.java
@@ -5,7 +5,7 @@
 package org.chromium.chrome.browser.vr_shell;
 
 import static org.chromium.chrome.browser.vr_shell.VrTestRule.PAGE_LOAD_TIMEOUT_S;
-import static org.chromium.chrome.browser.vr_shell.util.VrUtils.POLL_TIMEOUT_SHORT_MS;
+import static org.chromium.chrome.browser.vr_shell.VrTestRule.POLL_TIMEOUT_SHORT_MS;
 import static org.chromium.chrome.test.util.ChromeRestriction.RESTRICTION_TYPE_VIEWER_DAYDREAM;
 import static org.chromium.chrome.test.util.ChromeRestriction.RESTRICTION_TYPE_VIEWER_NON_DAYDREAM;
 
@@ -30,7 +30,11 @@
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.ChromeSwitches;
 import org.chromium.chrome.browser.vr_shell.mock.MockVrCoreVersionCheckerImpl;
-import org.chromium.chrome.browser.vr_shell.util.VrUtils;
+import org.chromium.chrome.browser.vr_shell.util.CardboardUtils;
+import org.chromium.chrome.browser.vr_shell.util.NfcSimUtils;
+import org.chromium.chrome.browser.vr_shell.util.VrInfoBarUtils;
+import org.chromium.chrome.browser.vr_shell.util.VrShellDelegateUtils;
+import org.chromium.chrome.browser.vr_shell.util.VrTransitionUtils;
 import org.chromium.chrome.test.ChromeActivityTestRule;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
 
@@ -61,7 +65,7 @@
     public void testRequestPresentEntersVr() throws InterruptedException {
         mVrTestRule.loadUrlAndAwaitInitialization(
                 VrTestRule.getHtmlTestFile("test_requestPresent_enters_vr"), PAGE_LOAD_TIMEOUT_S);
-        mVrTestRule.enterPresentationAndWait(
+        VrTransitionUtils.enterPresentationAndWait(
                 mVrTestRule.getFirstTabCvc(), mVrTestRule.getFirstTabWebContents());
         Assert.assertTrue("VrShellDelegate is in VR", VrShellDelegate.isInVr());
         mVrTestRule.endTest(mVrTestRule.getFirstTabWebContents());
@@ -78,8 +82,8 @@
         mVrTestRule.loadUrlAndAwaitInitialization(
                 VrTestRule.getHtmlTestFile("test_nfc_fires_vrdisplayactivate"),
                 PAGE_LOAD_TIMEOUT_S);
-        mVrTestRule.simNfcScanAndWait(
-                mVrTestRule.getActivity(), mVrTestRule.getFirstTabWebContents());
+        NfcSimUtils.simNfcScan(mVrTestRule.getActivity());
+        mVrTestRule.waitOnJavaScriptStep(mVrTestRule.getFirstTabWebContents());
         mVrTestRule.endTest(mVrTestRule.getFirstTabWebContents());
     }
 
@@ -95,7 +99,7 @@
                 PAGE_LOAD_TIMEOUT_S);
         mVrTestRule.executeStepAndWait(
                 "stepVerifyNoInitialTaps()", mVrTestRule.getFirstTabWebContents());
-        mVrTestRule.enterPresentationAndWait(
+        VrTransitionUtils.enterPresentationAndWait(
                 mVrTestRule.getFirstTabCvc(), mVrTestRule.getFirstTabWebContents());
         // Wait on VrShellImpl to say that its parent consumed the touch event
         // Set to 2 because there's an ACTION_DOWN followed by ACTION_UP
@@ -108,7 +112,7 @@
                         touchRegisteredLatch.countDown();
                     }
                 });
-        mVrTestRule.sendCardboardClick(mVrTestRule.getActivity());
+        CardboardUtils.sendCardboardClick(mVrTestRule.getActivity());
         Assert.assertTrue("VrShellImpl dispatched touches",
                 touchRegisteredLatch.await(POLL_TIMEOUT_SHORT_MS, TimeUnit.MILLISECONDS));
         mVrTestRule.executeStepAndWait(
@@ -130,7 +134,7 @@
         mVrTestRule.executeStepAndWait(
                 "stepVerifyNoInitialTaps()", mVrTestRule.getFirstTabWebContents());
         // Wait to enter VR
-        mVrTestRule.enterPresentationAndWait(
+        VrTransitionUtils.enterPresentationAndWait(
                 mVrTestRule.getFirstTabCvc(), mVrTestRule.getFirstTabWebContents());
         // Send a controller click and wait for JavaScript to receive it
         controller.pressReleaseTouchpadButton();
@@ -153,10 +157,10 @@
         mVrTestRule.executeStepAndWait(
                 "stepVerifyNoInitialTaps()", mVrTestRule.getFirstTabWebContents());
         // Wait to enter VR
-        mVrTestRule.enterPresentationAndWait(
+        VrTransitionUtils.enterPresentationAndWait(
                 mVrTestRule.getFirstTabCvc(), mVrTestRule.getFirstTabWebContents());
         // Tap and wait for JavaScript to receive it
-        mVrTestRule.sendCardboardClickAndWait(
+        CardboardUtils.sendCardboardClickAndWait(
                 mVrTestRule.getActivity(), mVrTestRule.getFirstTabWebContents());
         mVrTestRule.endTest(mVrTestRule.getFirstTabWebContents());
     }
@@ -191,7 +195,7 @@
             public void run() {
                 MockVrCoreVersionCheckerImpl mockChecker = new MockVrCoreVersionCheckerImpl();
                 mockChecker.setMockReturnValue(new VrCoreInfo(null, checkerReturnCompatibility));
-                VrShellDelegate.getInstanceForTesting().overrideVrCoreVersionCheckerForTesting(
+                VrShellDelegateUtils.getDelegateInstance().overrideVrCoreVersionCheckerForTesting(
                         mockChecker);
                 Assert.assertEquals(
                         checkerReturnCompatibility, mockChecker.getLastReturnValue().compatibility);
@@ -199,7 +203,7 @@
         });
         View decorView = mVrTestRule.getActivity().getWindow().getDecorView();
         if (checkerReturnCompatibility == VrCoreCompatibility.VR_READY) {
-            VrUtils.expectInfoBarPresent(decorView, false);
+            VrInfoBarUtils.expectInfoBarPresent(mVrTestRule, false);
         } else if (checkerReturnCompatibility == VrCoreCompatibility.VR_OUT_OF_DATE
                 || checkerReturnCompatibility == VrCoreCompatibility.VR_NOT_AVAILABLE) {
             // Out of date and missing cases are the same, but with different text
@@ -215,16 +219,13 @@
                 expectedButton = mVrTestRule.getActivity().getString(
                         R.string.vr_services_check_infobar_install_button);
             }
-            VrUtils.expectInfoBarPresent(decorView, true);
-            TextView tempView =
-                    (TextView) mVrTestRule.getActivity().getWindow().getDecorView().findViewById(
-                            R.id.infobar_message);
+            VrInfoBarUtils.expectInfoBarPresent(mVrTestRule, true);
+            TextView tempView = (TextView) decorView.findViewById(R.id.infobar_message);
             Assert.assertEquals(expectedMessage, tempView.getText().toString());
-            tempView = (TextView) mVrTestRule.getActivity().getWindow().getDecorView().findViewById(
-                    R.id.button_primary);
+            tempView = (TextView) decorView.findViewById(R.id.button_primary);
             Assert.assertEquals(expectedButton, tempView.getText().toString());
         } else if (checkerReturnCompatibility == VrCoreCompatibility.VR_NOT_SUPPORTED) {
-            VrUtils.expectInfoBarPresent(decorView, false);
+            VrInfoBarUtils.expectInfoBarPresent(mVrTestRule, false);
         } else {
             Assert.fail("Invalid VrCoreVersionChecker compatibility: "
                     + String.valueOf(checkerReturnCompatibility));
@@ -292,7 +293,7 @@
     public void testPresentationLocksFocus() throws InterruptedException {
         mVrTestRule.loadUrlAndAwaitInitialization(
                 VrTestRule.getHtmlTestFile("test_presentation_locks_focus"), PAGE_LOAD_TIMEOUT_S);
-        mVrTestRule.enterPresentationAndWait(
+        VrTransitionUtils.enterPresentationAndWait(
                 mVrTestRule.getFirstTabCvc(), mVrTestRule.getFirstTabWebContents());
         mVrTestRule.endTest(mVrTestRule.getFirstTabWebContents());
     }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/vr_shell/util/CardboardUtils.java b/chrome/android/javatests/src/org/chromium/chrome/browser/vr_shell/util/CardboardUtils.java
new file mode 100644
index 0000000..9ecfe53
--- /dev/null
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/vr_shell/util/CardboardUtils.java
@@ -0,0 +1,39 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.vr_shell.util;
+
+import android.app.Activity;
+import android.support.test.InstrumentationRegistry;
+
+import org.chromium.chrome.browser.vr_shell.VrTestRule;
+import org.chromium.content.browser.test.util.ClickUtils;
+import org.chromium.content_public.browser.WebContents;
+
+/**
+ * Class containing utility functions for sending Cardboard clicks, aka
+ * screen taps.
+ */
+public class CardboardUtils {
+    /**
+     * Use to simulate a cardboard click by sending a touch event to the device.
+     * @param activity The activity to send the cardboard click to.
+     */
+    public static void sendCardboardClick(Activity activity) {
+        ClickUtils.mouseSingleClickView(InstrumentationRegistry.getInstrumentation(),
+                activity.getWindow().getDecorView().getRootView());
+    }
+
+    /**
+     * Sends a cardboard click then waits for the JavaScript step to finish.
+     *
+     * Only meant to be used alongside the test framework from VrTestRule.
+     * @param activity The activity to send the cardboard click to.
+     * @param webContents The WebContents for the tab the JavaScript step is in.
+     */
+    public static void sendCardboardClickAndWait(Activity activity, WebContents webContents) {
+        sendCardboardClick(activity);
+        VrTestRule.waitOnJavaScriptStep(webContents);
+    }
+}
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/vr_shell/util/NfcSimUtils.java b/chrome/android/javatests/src/org/chromium/chrome/browser/vr_shell/util/NfcSimUtils.java
index baf20eb..8f55e1e 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/vr_shell/util/NfcSimUtils.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/vr_shell/util/NfcSimUtils.java
@@ -4,7 +4,9 @@
 
 package org.chromium.chrome.browser.vr_shell.util;
 
+import android.content.ActivityNotFoundException;
 import android.content.ComponentName;
+import android.content.Context;
 import android.content.Intent;
 import android.nfc.NdefMessage;
 import android.nfc.NdefRecord;
@@ -14,10 +16,7 @@
 import java.nio.ByteOrder;
 
 /**
- * Utility class to simulate a Daydream View NFC tag being scanned. In its own
- * class so that both instrumentation tests and the standalone APK for use with
- * Telemetry can share code without the APK including instrumentation-test-only
- * code.
+ * Utility class to simulate a Daydream View NFC tag being scanned.
  */
 public class NfcSimUtils {
     private static final String DETECTION_ACTIVITY = ".nfc.ViewerDetectionActivity";
@@ -51,6 +50,19 @@
         return nfcIntent;
     }
 
+    /**
+     * Simulates the NFC tag of the Daydream headset being scanned.
+     * @param context The Context that the activity will be started from.
+     */
+    public static void simNfcScan(Context context) {
+        Intent nfcIntent = NfcSimUtils.makeNfcIntent();
+        try {
+            context.startActivity(nfcIntent);
+        } catch (ActivityNotFoundException e) {
+            // On unsupported devices, won't find VrCore -> Do nothing
+        }
+    }
+
     private static byte[] intToByteArray(int i) {
         final ByteBuffer bb = ByteBuffer.allocate(Integer.SIZE / Byte.SIZE);
         bb.order(BYTE_ORDER);
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/vr_shell/util/VrInfoBarUtils.java b/chrome/android/javatests/src/org/chromium/chrome/browser/vr_shell/util/VrInfoBarUtils.java
new file mode 100644
index 0000000..29a021a
--- /dev/null
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/vr_shell/util/VrInfoBarUtils.java
@@ -0,0 +1,89 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.vr_shell.util;
+
+import static org.chromium.chrome.browser.vr_shell.VrTestRule.POLL_CHECK_INTERVAL_SHORT_MS;
+import static org.chromium.chrome.browser.vr_shell.VrTestRule.POLL_TIMEOUT_SHORT_MS;
+
+import org.chromium.base.ThreadUtils;
+import org.chromium.chrome.browser.infobar.InfoBar;
+import org.chromium.chrome.browser.vr_shell.VrTestRule;
+import org.chromium.chrome.test.util.InfoBarUtil;
+import org.chromium.content.browser.test.util.Criteria;
+import org.chromium.content.browser.test.util.CriteriaHelper;
+
+import java.util.List;
+import java.util.concurrent.Callable;
+
+/**
+ * Class containing utility functions for interacting with InfoBars at
+ * a high level.
+ */
+public class VrInfoBarUtils {
+    public enum Button { PRIMARY, SECONDARY };
+
+    /**
+     * Determines whether InfoBars are present in the current activity.
+     * @return True if there are any InfoBars present, false otherwise
+     */
+    public static boolean isInfoBarPresent(VrTestRule rule) {
+        List<InfoBar> infoBars = rule.getInfoBars();
+        return infoBars != null && !infoBars.isEmpty();
+    }
+
+    /**
+     * Clicks on either the primary or secondary button of the first InfoBar
+     * in the activity.
+     * @param button Which button to click
+     * @param rule The VrTestRule to get the current activity from
+     */
+    public static void clickInfoBarButton(final Button button, VrTestRule rule) {
+        if (!isInfoBarPresent(rule)) return;
+        final List<InfoBar> infoBars = rule.getInfoBars();
+        ThreadUtils.runOnUiThreadBlocking(new Runnable() {
+            @Override
+            public void run() {
+                switch (button) {
+                    case PRIMARY:
+                        InfoBarUtil.clickPrimaryButton(infoBars.get(0));
+                        break;
+                    default:
+                        InfoBarUtil.clickSecondaryButton(infoBars.get(0));
+                }
+            }
+        });
+        InfoBarUtil.waitUntilNoInfoBarsExist(rule.getInfoBars());
+    }
+
+    /**
+     * Clicks on the close button of the first InfoBar in the activity.
+     * @param rule The VrTestRule to get the current activity from
+     */
+    public static void clickInfobarCloseButton(VrTestRule rule) {
+        if (!isInfoBarPresent(rule)) return;
+        final List<InfoBar> infoBars = rule.getInfoBars();
+        ThreadUtils.runOnUiThreadBlocking(new Runnable() {
+            @Override
+            public void run() {
+                InfoBarUtil.clickCloseButton(infoBars.get(0));
+            }
+        });
+        InfoBarUtil.waitUntilNoInfoBarsExist(rule.getInfoBars());
+    }
+
+    /**
+     * Determines is there is any InfoBar present in the given View hierarchy.
+     * @param parentView The View to start the search in
+     * @param present Whether an InfoBar should be present.
+     */
+    public static void expectInfoBarPresent(final VrTestRule rule, boolean present) {
+        CriteriaHelper.pollUiThread(Criteria.equals(present, new Callable<Boolean>() {
+            @Override
+            public Boolean call() {
+                return isInfoBarPresent(rule);
+            }
+        }), POLL_TIMEOUT_SHORT_MS, POLL_CHECK_INTERVAL_SHORT_MS);
+    }
+}
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/vr_shell/util/VrShellDelegateUtils.java b/chrome/android/javatests/src/org/chromium/chrome/browser/vr_shell/util/VrShellDelegateUtils.java
new file mode 100644
index 0000000..09ec927e
--- /dev/null
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/vr_shell/util/VrShellDelegateUtils.java
@@ -0,0 +1,32 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.vr_shell.util;
+
+import org.chromium.base.ThreadUtils;
+import org.chromium.chrome.browser.vr_shell.VrShellDelegate;
+
+import java.util.concurrent.atomic.AtomicReference;
+
+/**
+ * Class containing utility functions for interacting with VrShellDelegate
+ * during tests.
+ */
+public class VrShellDelegateUtils {
+    /**
+     * Retrieves the current VrShellDelegate instance from the UI thread.
+     * This is necessary in case acquiring the instance causes the delegate
+     * to be constructed, which must happen on the UI thread.
+     */
+    public static VrShellDelegate getDelegateInstance() {
+        final AtomicReference<VrShellDelegate> delegate = new AtomicReference<VrShellDelegate>();
+        ThreadUtils.runOnUiThreadBlocking(new Runnable() {
+            @Override
+            public void run() {
+                delegate.set(VrShellDelegate.getInstanceForTesting());
+            }
+        });
+        return delegate.get();
+    }
+}
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/vr_shell/util/VrTransitionUtils.java b/chrome/android/javatests/src/org/chromium/chrome/browser/vr_shell/util/VrTransitionUtils.java
new file mode 100644
index 0000000..47ec4006
--- /dev/null
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/vr_shell/util/VrTransitionUtils.java
@@ -0,0 +1,102 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.vr_shell.util;
+
+import static org.chromium.chrome.browser.vr_shell.VrTestRule.POLL_CHECK_INTERVAL_SHORT_MS;
+
+import org.junit.Assert;
+
+import org.chromium.base.ThreadUtils;
+import org.chromium.chrome.browser.vr_shell.VrShellDelegate;
+import org.chromium.chrome.browser.vr_shell.VrTestRule;
+import org.chromium.content.browser.ContentViewCore;
+import org.chromium.content.browser.test.util.Criteria;
+import org.chromium.content.browser.test.util.CriteriaHelper;
+import org.chromium.content.browser.test.util.DOMUtils;
+import org.chromium.content_public.browser.WebContents;
+
+import java.util.concurrent.Callable;
+import java.util.concurrent.TimeoutException;
+
+/**
+ * Class containing utility functions for transitioning between different
+ * states in VR, such as fullscreen, WebVR presentation, and the VR browser.
+ *
+ * All the transitions in this class are performed directly through Chrome,
+ * as opposed to NFC tag simulation which involves receiving an intent from
+ * an outside application (VR Services).
+ */
+public class VrTransitionUtils {
+    /**
+     * Forces the browser into VR mode via a VrShellDelegate call.
+     */
+    public static void forceEnterVr() {
+        ThreadUtils.runOnUiThreadBlocking(new Runnable() {
+            @Override
+            public void run() {
+                VrShellDelegate.enterVrIfNecessary();
+            }
+        });
+    }
+
+    /**
+     * Forces the browser out of VR mode via a VrShellDelegate call.
+     */
+    public static void forceExitVr() {
+        ThreadUtils.runOnUiThreadBlocking(new Runnable() {
+            @Override
+            public void run() {
+                VrShellDelegateUtils.getDelegateInstance().shutdownVr(true /* disableVrMode */,
+                        false /* canReenter */, true /* stayingInChrome */);
+            }
+        });
+    }
+
+    /**
+     * Waits until the given VrShellDelegate's isInVR() returns true. Should
+     * only be used when VR support is expected.
+     * @param timeout How long to wait before giving up, in milliseconds
+     */
+    public static void waitForVrEntry(final int timeout) {
+        // If VR Shell is supported, mInVr should eventually go to true
+        // Relatively long timeout because sometimes GVR takes a while to enter VR
+        CriteriaHelper.pollUiThread(Criteria.equals(true, new Callable<Boolean>() {
+            @Override
+            public Boolean call() {
+                return VrShellDelegate.isInVr();
+            }
+        }), timeout, POLL_CHECK_INTERVAL_SHORT_MS);
+    }
+
+    /**
+     * Sends a click event directly to the WebGL canvas, triggering its onclick
+     * that by default calls WebVR's requestPresent. Will have a similar result to
+     * CardboardUtils.sendCardboardClick if not already presenting, but less prone
+     * to errors, e.g. if there's dialog in the center of the screen blocking the canvas.
+     *
+     * Only meant to be used alongside the test framework from VrTestRule.
+     * @param cvc The ContentViewCore for the tab the canvas is in.
+     */
+    public static void enterPresentation(ContentViewCore cvc) {
+        try {
+            DOMUtils.clickNode(cvc, "webgl-canvas");
+        } catch (InterruptedException | TimeoutException e) {
+            Assert.fail("Failed to click canvas to enter presentation: " + e.toString());
+        }
+    }
+
+    /**
+     * Sends a click event directly to the WebGL canvas then waits for the
+     * JavaScript step to finish.
+     *
+     * Only meant to be used alongside the test framework from VrTestRule.
+     * @param cvc The ContentViewCore for the tab the canvas is in.
+     * @param webContents The WebContents for the tab the JavaScript step is in.
+     */
+    public static void enterPresentationAndWait(ContentViewCore cvc, WebContents webContents) {
+        enterPresentation(cvc);
+        VrTestRule.waitOnJavaScriptStep(webContents);
+    }
+}
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/vr_shell/util/VrUtils.java b/chrome/android/javatests/src/org/chromium/chrome/browser/vr_shell/util/VrUtils.java
deleted file mode 100644
index 5aa5159..0000000
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/vr_shell/util/VrUtils.java
+++ /dev/null
@@ -1,138 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-package org.chromium.chrome.browser.vr_shell.util;
-
-import android.content.ActivityNotFoundException;
-import android.content.Context;
-import android.content.Intent;
-import android.view.View;
-import android.view.ViewGroup;
-
-import org.chromium.base.ThreadUtils;
-import org.chromium.chrome.browser.infobar.InfoBarLayout;
-import org.chromium.chrome.browser.vr_shell.VrShellDelegate;
-import org.chromium.content.browser.test.util.Criteria;
-import org.chromium.content.browser.test.util.CriteriaHelper;
-
-import java.util.concurrent.Callable;
-import java.util.concurrent.atomic.AtomicReference;
-
-/**
- * Class containing static functions and constants that are useful for VR
- * instrumentation testing.
- */
-public class VrUtils {
-    public static final int POLL_CHECK_INTERVAL_SHORT_MS = 50;
-    public static final int POLL_CHECK_INTERVAL_LONG_MS = 100;
-    public static final int POLL_TIMEOUT_SHORT_MS = 1000;
-    public static final int POLL_TIMEOUT_LONG_MS = 10000;
-
-    /**
-     * Gets the VrShellDelegate instance on the UI thread, as otherwise the Choreographer obtained
-     * in VrShellDelegate's constructor is for the instrumentation thread instead of the UI thread.
-     * @return The browser's current VrShellDelegate instance
-     */
-    public static VrShellDelegate getVrShellDelegateInstance() {
-        final AtomicReference<VrShellDelegate> delegate = new AtomicReference<VrShellDelegate>();
-        ThreadUtils.runOnUiThreadBlocking(new Runnable() {
-            @Override
-            public void run() {
-                delegate.set(VrShellDelegate.getInstanceForTesting());
-            }
-        });
-        return delegate.get();
-    }
-
-    /**
-     * Forces the browser into VR mode via a VrShellDelegate call.
-     */
-    public static void forceEnterVr() {
-        ThreadUtils.runOnUiThreadBlocking(new Runnable() {
-            @Override
-            public void run() {
-                VrShellDelegate.enterVrIfNecessary();
-            }
-        });
-    }
-
-    /**
-     * Forces the browser out of VR mode via a VrShellDelegate call.
-     * @param vrDelegate The VRShellDelegate associated with this activity.
-     */
-    public static void forceExitVr(final VrShellDelegate vrDelegate) {
-        ThreadUtils.runOnUiThreadBlocking(new Runnable() {
-            @Override
-            public void run() {
-                vrDelegate.shutdownVr(true /* disableVrMode */, false /* canReenter */,
-                        true /* stayingInChrome */);
-            }
-        });
-    }
-
-    /**
-     * Simulates the NFC tag of the Daydream headset being scanned.
-     * @param context The Context that the activity will be started from.
-     */
-    public static void simNfcScan(Context context) {
-        Intent nfcIntent = NfcSimUtils.makeNfcIntent();
-        try {
-            context.startActivity(nfcIntent);
-        } catch (ActivityNotFoundException e) {
-            // On unsupported devices, won't find VrCore -> Do nothing
-        }
-    }
-
-    /**
-     * Waits until the given VrShellDelegate's isInVR() returns true. Should
-     * only be used when VR Shell support is expected.
-     * @param timeout How long to wait before giving up, in milliseconds
-     */
-    public static void waitForVrSupported(final int timeout) {
-        // If VR Shell is supported, mInVr should eventually go to true
-        // Relatively long timeout because sometimes GVR takes a while to enter VR
-        CriteriaHelper.pollUiThread(Criteria.equals(true, new Callable<Boolean>() {
-            @Override
-            public Boolean call() {
-                return VrShellDelegate.isInVr();
-            }
-        }), timeout, POLL_CHECK_INTERVAL_SHORT_MS);
-    }
-
-    /**
-     * Determines is there is any InfoBar present in the given View hierarchy.
-     * @param parentView The View to start the search in
-     * @param present Whether an InfoBar should be present.
-     */
-    public static void expectInfoBarPresent(final View parentView, boolean present) {
-        CriteriaHelper.pollUiThread(Criteria.equals(present, new Callable<Boolean>() {
-            @Override
-            public Boolean call() {
-                return isInfoBarPresentInternal(parentView);
-            }
-        }), POLL_TIMEOUT_SHORT_MS, POLL_CHECK_INTERVAL_SHORT_MS);
-    }
-
-    /**
-     * Determines is there is any InfoBar present in the given View hierarchy.
-     * @param parentView The View to start the search in
-     * @return Whether the InfoBar is present
-     */
-    private static boolean isInfoBarPresentInternal(View parentView) {
-        // TODO(ymalik): This will return true if any infobar is present. Is it
-        // possible to determine the type of infobar present (e.g. Feedback)?
-        // InfoBarContainer will be present regardless of whether an InfoBar
-        // is actually there, but InfoBarLayout is only present if one is
-        // currently showing.
-        if (parentView instanceof InfoBarLayout) {
-            return true;
-        } else if (parentView instanceof ViewGroup) {
-            ViewGroup group = (ViewGroup) parentView;
-            for (int i = 0; i < group.getChildCount(); i++) {
-                if (isInfoBarPresentInternal(group.getChildAt(i))) return true;
-            }
-        }
-        return false;
-    }
-}
diff --git a/chrome/app/chrome_manifest.json b/chrome/app/chrome_manifest.json
index a3d96761..6c0181a5 100644
--- a/chrome/app/chrome_manifest.json
+++ b/chrome/app/chrome_manifest.json
@@ -4,6 +4,11 @@
   "interface_provider_specs": {
     "service_manager:connector": {
       "provides": {
+        // This is only used in classic ash. In mus InputDeviceController comes
+        // from mus.
+        "input_device_controller": [
+          "ui::mojom::InputDeviceController"
+        ],
         "mash:launchable": [
           "mash::mojom::Launchable"
         ]
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index aa464ff..ee3fae0 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -682,6 +682,8 @@
     "net/chrome_extensions_network_delegate.h",
     "net/chrome_http_user_agent_settings.cc",
     "net/chrome_http_user_agent_settings.h",
+    "net/chrome_mojo_proxy_resolver_factory.cc",
+    "net/chrome_mojo_proxy_resolver_factory.h",
     "net/chrome_net_log_helper.cc",
     "net/chrome_net_log_helper.h",
     "net/chrome_network_delegate.cc",
@@ -1659,6 +1661,7 @@
     "//mojo/public/cpp/bindings",
     "//mojo/public/js",
     "//net:extras",
+    "//net:net_browser_services",
     "//ppapi/features",
     "//printing/features",
     "//rlz/features",
@@ -1715,6 +1718,7 @@
   # platforms use OOP Mojo resolution.
   if (is_android) {
     deps += [
+      "//net:net_utility_services",
       "//net:net_with_v8",
       "//v8",
     ]
@@ -2481,6 +2485,13 @@
       "//components/metrics/leak_detector:interfaces",
       "//components/metrics/leak_detector:leak_detector",
     ]
+    if (use_ozone) {
+      deps += [
+        "//services/ui/public/cpp/input_devices",
+        "//services/ui/public/cpp/input_devices:input_device_controller",
+        "//ui/ozone",
+      ]
+    }
   } else {  # Non-ChromeOS.
     sources += [
       "policy/cloud/user_cloud_policy_manager_factory.cc",
@@ -3495,8 +3506,6 @@
       "metrics/tab_usage_recorder.h",
       "net/firefox_proxy_settings.cc",
       "net/firefox_proxy_settings.h",
-      "net/utility_process_mojo_proxy_resolver_factory.cc",
-      "net/utility_process_mojo_proxy_resolver_factory.h",
       "notifications/application_notifier_source.cc",
       "notifications/application_notifier_source.h",
       "notifications/arc_application_notifier_source_chromeos.cc",
@@ -3710,7 +3719,6 @@
       "//components/cryptauth/proto",
       "//components/feedback",
       "//components/web_modal",
-      "//net:net_browser_services",
     ]
   }
 
diff --git a/chrome/browser/android/offline_pages/prefetch/prefetch_background_task.cc b/chrome/browser/android/offline_pages/prefetch/prefetch_background_task.cc
index 231be79f..43b46cc9 100644
--- a/chrome/browser/android/offline_pages/prefetch/prefetch_background_task.cc
+++ b/chrome/browser/android/offline_pages/prefetch/prefetch_background_task.cc
@@ -53,7 +53,7 @@
   prefetch_service->GetPrefetchDispatcher()->BeginBackgroundTask(
       base::MakeUnique<PrefetchBackgroundTask>(env, jcaller, prefetch_service,
                                                profile));
-  return false;  // true;
+  return true;
 }
 
 }  // namespace prefetch
diff --git a/chrome/browser/browser_process_platform_part_chromeos.cc b/chrome/browser/browser_process_platform_part_chromeos.cc
index 08f4c21..af96a8b 100644
--- a/chrome/browser/browser_process_platform_part_chromeos.cc
+++ b/chrome/browser/browser_process_platform_part_chromeos.cc
@@ -9,6 +9,8 @@
 #include "base/time/default_tick_clock.h"
 #include "base/time/tick_clock.h"
 #include "chrome/browser/browser_process.h"
+#include "chrome/browser/chromeos/ash_config.h"
+#include "chrome/browser/chromeos/chrome_service_name.h"
 #include "chrome/browser/chromeos/login/session/chrome_session_manager.h"
 #include "chrome/browser/chromeos/login/users/chrome_user_manager_impl.h"
 #include "chrome/browser/chromeos/net/delay_network_call.h"
@@ -29,6 +31,14 @@
 #include "components/session_manager/core/session_manager.h"
 #include "components/user_manager/user_manager.h"
 
+#if defined(USE_OZONE)
+#include "ash/public/interfaces/constants.mojom.h"
+#include "content/public/common/service_manager_connection.h"
+#include "services/service_manager/runner/common/client_util.h"
+#include "services/ui/public/cpp/input_devices/input_device_controller_client.h"
+#include "services/ui/public/interfaces/constants.mojom.h"
+#endif
+
 BrowserProcessPlatformPart::BrowserProcessPlatformPart()
     : created_profile_helper_(false) {}
 
@@ -166,6 +176,23 @@
   return compatible_cros_components_.count(name) > 0;
 }
 
+#if defined(USE_OZONE)
+ui::InputDeviceControllerClient*
+BrowserProcessPlatformPart::GetInputDeviceControllerClient() {
+  if (!input_device_controller_client_) {
+    const std::string service_name =
+        chromeos::GetAshConfig() == ash::Config::CLASSIC
+            ? chromeos::kChromeServiceName
+            : ui::mojom::kServiceName;
+    input_device_controller_client_ =
+        base::MakeUnique<ui::InputDeviceControllerClient>(
+            content::ServiceManagerConnection::GetForProcess()->GetConnector(),
+            service_name);
+  }
+  return input_device_controller_client_.get();
+}
+#endif
+
 void BrowserProcessPlatformPart::CreateProfileHelper() {
   DCHECK(!created_profile_helper_ && !profile_helper_);
   created_profile_helper_ = true;
diff --git a/chrome/browser/browser_process_platform_part_chromeos.h b/chrome/browser/browser_process_platform_part_chromeos.h
index 11348eff..2aa21c1 100644
--- a/chrome/browser/browser_process_platform_part_chromeos.h
+++ b/chrome/browser/browser_process_platform_part_chromeos.h
@@ -36,6 +36,10 @@
 class BrowserPolicyConnectorChromeOS;
 }
 
+namespace ui {
+class InputDeviceControllerClient;
+}
+
 class ScopedKeepAlive;
 
 class BrowserProcessPlatformPart : public BrowserProcessPlatformPartBase {
@@ -103,6 +107,10 @@
 
   bool IsCompatibleCrOSComponent(const std::string& name);
 
+#if defined(USE_OZONE)
+  ui::InputDeviceControllerClient* GetInputDeviceControllerClient();
+#endif
+
  private:
   void CreateProfileHelper();
 
@@ -131,6 +139,11 @@
 
   base::flat_set<std::string> compatible_cros_components_;
 
+#if defined(USE_OZONE)
+  std::unique_ptr<ui::InputDeviceControllerClient>
+      input_device_controller_client_;
+#endif
+
   SEQUENCE_CHECKER(sequence_checker_);
 
   DISALLOW_COPY_AND_ASSIGN(BrowserProcessPlatformPart);
diff --git a/chrome/browser/chrome_content_browser_client.cc b/chrome/browser/chrome_content_browser_client.cc
index 6435b992..a607f6a0 100644
--- a/chrome/browser/chrome_content_browser_client.cc
+++ b/chrome/browser/chrome_content_browser_client.cc
@@ -234,6 +234,7 @@
 #include "chrome/browser/chromeos/arc/intent_helper/arc_navigation_throttle.h"
 #include "chrome/browser/chromeos/attestation/platform_verification_impl.h"
 #include "chrome/browser/chromeos/chrome_browser_main_chromeos.h"
+#include "chrome/browser/chromeos/chrome_service_name.h"
 #include "chrome/browser/chromeos/drive/fileapi/file_system_backend_delegate.h"
 #include "chrome/browser/chromeos/file_manager/app_id.h"
 #include "chrome/browser/chromeos/file_system_provider/fileapi/backend_delegate.h"
@@ -244,11 +245,13 @@
 #include "chrome/browser/chromeos/login/startup_utils.h"
 #include "chrome/browser/chromeos/policy/browser_policy_connector_chromeos.h"
 #include "chrome/browser/chromeos/system/input_device_settings.h"
+#include "chrome/browser/metrics/leak_detector/leak_detector_remote_controller.h"
 #include "chrome/browser/profiles/profile_manager.h"
 #include "chrome/browser/ui/ash/ash_util.h"
 #include "chrome/browser/ui/browser_commands.h"
 #include "chrome/browser/ui/browser_dialogs.h"
 #include "chrome/browser/ui/browser_finder.h"
+#include "chrome/browser/ui/views/ash/chrome_browser_main_extra_parts_ash.h"
 #include "chromeos/chromeos_constants.h"
 #include "chromeos/chromeos_switches.h"
 #include "components/user_manager/user_manager.h"
@@ -258,16 +261,30 @@
 #include "chrome/browser/chrome_browser_main_linux.h"
 #elif defined(OS_ANDROID)
 #include "chrome/browser/android/app_hooks.h"
+#include "chrome/browser/android/chrome_context_util.h"
+#include "chrome/browser/android/devtools_manager_delegate_android.h"
+#include "chrome/browser/android/ntp/new_tab_page_url_handler.h"
+#include "chrome/browser/android/service_tab_launcher.h"
+#include "chrome/browser/android/tab_android.h"
+#include "chrome/browser/android/webapps/single_tab_mode_tab_helper.h"
 #include "chrome/browser/chrome_browser_main_android.h"
 #include "chrome/common/descriptors_android.h"
 #include "components/crash/content/browser/crash_dump_manager_android.h"
 #include "components/navigation_interception/intercept_navigation_delegate.h"
+#include "components/payments/mojom/payment_request.mojom.h"
+#include "content/public/browser/android/java_interfaces.h"
 #include "ui/base/resource/resource_bundle_android.h"
+#include "ui/base/ui_base_paths.h"
 #elif defined(OS_POSIX)
 #include "chrome/browser/chrome_browser_main_posix.h"
 #endif
 
+#if defined(OS_CHROMEOS) && defined(USE_OZONE)
+#include "services/ui/public/cpp/input_devices/input_device_controller.h"
+#endif
+
 #if !defined(OS_ANDROID)
+#include "chrome/browser/devtools/chrome_devtools_manager_delegate.h"
 #include "chrome/browser/payments/payment_request_factory.h"
 #endif
 
@@ -281,20 +298,6 @@
 #include "components/crash/content/browser/crash_handler_host_linux.h"
 #endif
 
-#if defined(OS_ANDROID)
-#include "chrome/browser/android/chrome_context_util.h"
-#include "chrome/browser/android/devtools_manager_delegate_android.h"
-#include "chrome/browser/android/ntp/new_tab_page_url_handler.h"
-#include "chrome/browser/android/service_tab_launcher.h"
-#include "chrome/browser/android/tab_android.h"
-#include "chrome/browser/android/webapps/single_tab_mode_tab_helper.h"
-#include "components/payments/mojom/payment_request.mojom.h"
-#include "content/public/browser/android/java_interfaces.h"
-#include "ui/base/ui_base_paths.h"
-#else
-#include "chrome/browser/devtools/chrome_devtools_manager_delegate.h"
-#endif
-
 #if defined(TOOLKIT_VIEWS)
 #include "chrome/browser/ui/views/chrome_browser_main_extra_parts_views.h"
 #endif
@@ -310,10 +313,6 @@
 #include "ui/views/mus/mus_client.h"
 #endif
 
-#if defined(USE_ASH)
-#include "chrome/browser/ui/views/ash/chrome_browser_main_extra_parts_ash.h"
-#endif
-
 #if defined(USE_X11)
 #include "chrome/browser/chrome_browser_main_extra_parts_x11.h"
 #endif
@@ -401,10 +400,6 @@
 #include "media/mojo/services/media_service_factory.h"  // nogncheck
 #endif
 
-#if defined(OS_CHROMEOS)
-#include "chrome/browser/metrics/leak_detector/leak_detector_remote_controller.h"
-#endif
-
 #if BUILDFLAG(ENABLE_SUPERVISED_USERS)
 #include "chrome/browser/supervised_user/supervised_user_navigation_throttle.h"
 #endif
@@ -506,10 +501,6 @@
 
 #if defined(OS_CHROMEOS)
 
-// The name of the packaged service used to expose miscellaneous application
-// control features such as the mash Launchable interface.
-const char kChromeServiceName[] = "chrome";
-
 // Packaged service implementation used to expose miscellaneous application
 // control features. This is a singleton service which runs on the main thread
 // and never stops.
@@ -517,6 +508,9 @@
                               public mash::mojom::Launchable {
  public:
   ChromeServiceChromeOS() {
+#if defined(USE_OZONE)
+    input_device_controller_.AddInterface(&interfaces_);
+#endif
     interfaces_.AddInterface<mash::mojom::Launchable>(
         base::Bind(&ChromeServiceChromeOS::Create, base::Unretained(this)));
   }
@@ -575,6 +569,9 @@
 
   service_manager::BinderRegistry interfaces_;
   mojo::BindingSet<mash::mojom::Launchable> bindings_;
+#if defined(USE_OZONE)
+  ui::InputDeviceController input_device_controller_;
+#endif
 
   DISALLOW_COPY_AND_ASSIGN(ChromeServiceChromeOS);
 };
@@ -3154,7 +3151,7 @@
     content::ServiceInfo info;
     info.factory = base::Bind(&ChromeServiceChromeOS::CreateService);
     info.task_runner = base::ThreadTaskRunnerHandle::Get();
-    services->insert(std::make_pair(kChromeServiceName, info));
+    services->insert(std::make_pair(chromeos::kChromeServiceName, info));
   }
 
   if (features::PrefServiceEnabled()) {
diff --git a/chrome/browser/chrome_content_browser_manifest_overlay.json b/chrome/browser/chrome_content_browser_manifest_overlay.json
index cd6f9bbb..df431cad0 100644
--- a/chrome/browser/chrome_content_browser_manifest_overlay.json
+++ b/chrome/browser/chrome_content_browser_manifest_overlay.json
@@ -27,13 +27,20 @@
       "requires": {
         "accessibility_autoclick": [ "ash:autoclick" ],
         "ash": [ "ash", "display" ],
+        // Only used in classic ash case.
+        "chrome": [ "input_device_controller" ],
         "device": [ "device:fingerprint" ],
         "identity": [ "identity_manager" ],
         "nacl_broker": [ "browser" ],
         "nacl_loader": [ "browser" ],
         "preferences_forwarder": [ "pref_client" ],
         "preferences": [ "pref_client", "pref_control" ],
-        "ui": [ "display_controller", "ime_registrar", "window_manager" ]
+        "ui": [
+          "display_controller",
+          "ime_registrar",
+          "input_device_controller",
+          "window_manager"
+        ]
       }
     },
     "navigation:frame": {
diff --git a/chrome/browser/chromeos/BUILD.gn b/chrome/browser/chromeos/BUILD.gn
index 363edaaa2..4b8b2e5 100644
--- a/chrome/browser/chromeos/BUILD.gn
+++ b/chrome/browser/chromeos/BUILD.gn
@@ -390,6 +390,8 @@
     "certificate_provider/thread_safe_certificate_map.h",
     "chrome_browser_main_chromeos.cc",
     "chrome_browser_main_chromeos.h",
+    "chrome_service_name.cc",
+    "chrome_service_name.h",
     "customization/customization_document.cc",
     "customization/customization_document.h",
     "customization/customization_wallpaper_downloader.cc",
diff --git a/chrome/browser/chromeos/arc/arc_play_store_enabled_preference_handler.cc b/chrome/browser/chromeos/arc/arc_play_store_enabled_preference_handler.cc
index e7de3ac..4496078 100644
--- a/chrome/browser/chromeos/arc/arc_play_store_enabled_preference_handler.cc
+++ b/chrome/browser/chromeos/arc/arc_play_store_enabled_preference_handler.cc
@@ -134,7 +134,8 @@
   if (!base::CommandLine::ForCurrentProcess()->HasSwitch(
           chromeos::switches::kEnableArcOOBEOptIn) &&
       profile_->IsNewProfile() &&
-      !profile_->GetPrefs()->HasPrefPath(prefs::kArcEnabled)) {
+      !profile_->GetPrefs()->HasPrefPath(prefs::kArcEnabled) &&
+      arc::IsPlayStoreAvailable()) {
     ArcAuthNotification::Show(profile_);
   }
 }
diff --git a/chrome/browser/chromeos/arc/arc_session_manager.cc b/chrome/browser/chromeos/arc/arc_session_manager.cc
index e623a1d..f930bcc 100644
--- a/chrome/browser/chromeos/arc/arc_session_manager.cc
+++ b/chrome/browser/chromeos/arc/arc_session_manager.cc
@@ -307,7 +307,8 @@
     //   the whole OptIn flow should happen as seamless as possible for the
     //   user.
     const bool suppress_play_store_app =
-        IsArcOptInVerificationDisabled() || IsArcKioskMode() ||
+        !IsPlayStoreAvailable() || IsArcOptInVerificationDisabled() ||
+        IsArcKioskMode() ||
         (IsArcPlayStoreEnabledPreferenceManagedForProfile(profile_) &&
          AreArcAllOptInPreferencesManagedForProfile(profile_));
     if (!suppress_play_store_app) {
diff --git a/chrome/browser/chromeos/arc/arc_session_manager_unittest.cc b/chrome/browser/chromeos/arc/arc_session_manager_unittest.cc
index b251fd0..b3e21db 100644
--- a/chrome/browser/chromeos/arc/arc_session_manager_unittest.cc
+++ b/chrome/browser/chromeos/arc/arc_session_manager_unittest.cc
@@ -469,7 +469,7 @@
   ArcSessionManagerArcAlwaysStartTest() = default;
 
   void SetUp() override {
-    SetArcAlwaysStartForTesting();
+    SetArcAlwaysStartForTesting(true);
     ArcSessionManagerTest::SetUp();
   }
 
diff --git a/chrome/browser/chromeos/chrome_service_name.cc b/chrome/browser/chromeos/chrome_service_name.cc
new file mode 100644
index 0000000..faed9da
--- /dev/null
+++ b/chrome/browser/chromeos/chrome_service_name.cc
@@ -0,0 +1,11 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/chromeos/chrome_service_name.h"
+
+namespace chromeos {
+
+const char kChromeServiceName[] = "chrome";
+
+}  // namespace chromeos
diff --git a/chrome/browser/chromeos/chrome_service_name.h b/chrome/browser/chromeos/chrome_service_name.h
new file mode 100644
index 0000000..248becbe
--- /dev/null
+++ b/chrome/browser/chromeos/chrome_service_name.h
@@ -0,0 +1,16 @@
+// 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 CHROME_BROWSER_CHROMEOS_CHROME_SERVICE_NAME_H_
+#define CHROME_BROWSER_CHROMEOS_CHROME_SERVICE_NAME_H_
+
+namespace chromeos {
+
+// This is the service name used for services exposed by Chrome. Interfaces
+// exported (and potentially used inside Chrome) are registered under this name.
+extern const char kChromeServiceName[];
+
+}  // namespace chromeos
+
+#endif  // CHROME_BROWSER_CHROMEOS_CHROME_SERVICE_NAME_H_
diff --git a/chrome/browser/chromeos/input_method/input_method_manager_impl.cc b/chrome/browser/chromeos/input_method/input_method_manager_impl.cc
index 059746f..703e114 100644
--- a/chrome/browser/chromeos/input_method/input_method_manager_impl.cc
+++ b/chrome/browser/chromeos/input_method/input_method_manager_impl.cc
@@ -12,7 +12,9 @@
 #include <sstream>
 #include <utility>
 
+#include "ash/ime/ime_controller.h"
 #include "ash/shell.h"
+#include "ash/system/tray/ime_info.h"
 #include "base/bind.h"
 #include "base/feature_list.h"
 #include "base/hash.h"
@@ -21,6 +23,7 @@
 #include "base/metrics/sparse_histogram.h"
 #include "base/strings/string_split.h"
 #include "base/strings/string_util.h"
+#include "base/strings/utf_string_conversions.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/chromeos/ash_config.h"
 #include "chrome/browser/chromeos/input_method/candidate_window_controller.h"
@@ -52,6 +55,19 @@
 
 namespace {
 
+InputMethodManagerImpl* g_instance = nullptr;
+
+ash::IMEInfo GetAshImeInfo(const InputMethodDescriptor& ime,
+                           const InputMethodUtil& util) {
+  ash::IMEInfo info;
+  info.id = ime.id();
+  info.name = util.GetInputMethodLongName(ime);
+  info.medium_name = util.GetInputMethodMediumName(ime);
+  info.short_name = util.GetInputMethodShortName(ime);
+  info.third_party = extension_ime_util::IsExtensionIME(ime.id());
+  return info;
+}
+
 bool Contains(const std::vector<std::string>& container,
               const std::string& value) {
   return std::find(container.begin(), container.end(), value) !=
@@ -939,14 +955,25 @@
       component_extension_ime_manager_->GetAllIMEAsInputMethodDescriptor();
   util_.ResetInputMethods(descriptors);
   chromeos::UserAddingScreen::Get()->AddObserver(this);
+
+  DCHECK(!g_instance);
+  g_instance = this;
 }
 
 InputMethodManagerImpl::~InputMethodManagerImpl() {
+  DCHECK_EQ(g_instance, this);
+  g_instance = nullptr;
+
   if (candidate_window_controller_.get())
     candidate_window_controller_->RemoveObserver(this);
   chromeos::UserAddingScreen::Get()->RemoveObserver(this);
 }
 
+// static
+InputMethodManagerImpl* InputMethodManagerImpl::Get() {
+  return g_instance;
+}
+
 void InputMethodManagerImpl::RecordInputMethodUsage(
     const std::string& input_method_id) {
   UMA_HISTOGRAM_ENUMERATION("InputMethod.Category",
@@ -1335,5 +1362,55 @@
   return base::FeatureList::IsEnabled(features::kEHVInputOnImeMenu);
 }
 
+ash::IMEInfo InputMethodManagerImpl::GetCurrentIme() const {
+  if (!state_)
+    return ash::IMEInfo();
+
+  InputMethodDescriptor ime = state_->GetCurrentInputMethod();
+  ash::IMEInfo info = GetAshImeInfo(ime, util_);
+  info.selected = true;
+  return info;
+}
+
+std::vector<ash::IMEPropertyInfo>
+InputMethodManagerImpl::GetCurrentImeProperties() const {
+  std::vector<ash::IMEPropertyInfo> properties;
+  ui::ime::InputMethodMenuItemList menu_list =
+      ui::ime::InputMethodMenuManager::GetInstance()
+          ->GetCurrentInputMethodMenuItemList();
+  for (size_t i = 0; i < menu_list.size(); ++i) {
+    ash::IMEPropertyInfo property;
+    property.key = menu_list[i].key;
+    property.name = base::UTF8ToUTF16(menu_list[i].label);
+    property.selected = menu_list[i].is_selection_item_checked;
+    properties.push_back(property);
+  }
+  return properties;
+}
+
+std::vector<ash::IMEInfo> InputMethodManagerImpl::GetAvailableImes() const {
+  if (!state_)
+    return std::vector<ash::IMEInfo>();
+
+  std::vector<ash::IMEInfo> imes;
+  std::string current_ime_id = state_->GetCurrentInputMethod().id();
+  std::unique_ptr<InputMethodDescriptors> descriptors =
+      state_->GetActiveInputMethods();
+  for (const InputMethodDescriptor& descriptor : *descriptors) {
+    ash::IMEInfo info = GetAshImeInfo(descriptor, util_);
+    info.selected = descriptor.id() == current_ime_id;
+    imes.push_back(info);
+  }
+  return imes;
+}
+
+bool InputMethodManagerImpl::IsImeManaged() const {
+  if (!state_)
+    return false;
+
+  // Having a non-empty "allowed" list indicates that IMEs are managed.
+  return !state_->GetAllowedInputMethods().empty();
+}
+
 }  // namespace input_method
 }  // namespace chromeos
diff --git a/chrome/browser/chromeos/input_method/input_method_manager_impl.h b/chrome/browser/chromeos/input_method/input_method_manager_impl.h
index dfd8fdc4..a3ba164 100644
--- a/chrome/browser/chromeos/input_method/input_method_manager_impl.h
+++ b/chrome/browser/chromeos/input_method/input_method_manager_impl.h
@@ -12,6 +12,7 @@
 #include <string>
 #include <vector>
 
+#include "ash/ime/ime_controller.h"
 #include "base/macros.h"
 #include "base/observer_list.h"
 #include "base/threading/thread_checker.h"
@@ -35,7 +36,9 @@
 class ImeKeyboard;
 
 // The implementation of InputMethodManager.
+// TODO(jamescook): Replace ash::ImeController with mojo interface.
 class InputMethodManagerImpl : public InputMethodManager,
+                               public ash::ImeController,
                                public CandidateWindowController::Observer,
                                public UserAddingScreen::Observer {
  public:
@@ -164,6 +167,8 @@
                          bool enable_extension_loading);
   ~InputMethodManagerImpl() override;
 
+  static InputMethodManagerImpl* Get();
+
   // Receives notification of an InputMethodManager::UISessionState transition.
   void SetUISessionState(UISessionState new_ui_session);
 
@@ -191,6 +196,12 @@
   void OverrideKeyboardUrlRef(const std::string& keyset) override;
   bool IsEmojiHandwritingVoiceOnImeMenuEnabled() override;
 
+  // ash::ImeController:
+  ash::IMEInfo GetCurrentIme() const override;
+  std::vector<ash::IMEPropertyInfo> GetCurrentImeProperties() const override;
+  std::vector<ash::IMEInfo> GetAvailableImes() const override;
+  bool IsImeManaged() const override;
+
   // chromeos::UserAddingScreen:
   void OnUserAddingStarted() override;
   void OnUserAddingFinished() override;
diff --git a/chrome/browser/chromeos/note_taking_helper_unittest.cc b/chrome/browser/chromeos/note_taking_helper_unittest.cc
index 50434e7..a13c584c 100644
--- a/chrome/browser/chromeos/note_taking_helper_unittest.cc
+++ b/chrome/browser/chromeos/note_taking_helper_unittest.cc
@@ -139,7 +139,7 @@
 
   void SetUp() override {
     if (GetParam())
-      arc::SetArcAlwaysStartForTesting();
+      arc::SetArcAlwaysStartForTesting(true);
 
     // This is needed to avoid log spam due to ArcSessionManager's
     // RemoveArcData() calls failing.
diff --git a/chrome/browser/chromeos/system/fake_input_device_settings.cc b/chrome/browser/chromeos/system/fake_input_device_settings.cc
index 6638773..e8cd17b6 100644
--- a/chrome/browser/chromeos/system/fake_input_device_settings.cc
+++ b/chrome/browser/chromeos/system/fake_input_device_settings.cc
@@ -4,6 +4,10 @@
 
 #include "chrome/browser/chromeos/system/fake_input_device_settings.h"
 
+#include <utility>
+
+#include "base/callback.h"
+
 namespace chromeos {
 namespace system {
 
@@ -12,9 +16,8 @@
 FakeInputDeviceSettings::~FakeInputDeviceSettings() {}
 
 // Overriden from InputDeviceSettings.
-void FakeInputDeviceSettings::TouchpadExists(
-    const DeviceExistsCallback& callback) {
-  callback.Run(touchpad_exists_);
+void FakeInputDeviceSettings::TouchpadExists(DeviceExistsCallback callback) {
+  std::move(callback).Run(touchpad_exists_);
 }
 
 void FakeInputDeviceSettings::UpdateTouchpadSettings(
@@ -52,9 +55,8 @@
   UpdateTouchpadSettings(settings);
 }
 
-void FakeInputDeviceSettings::MouseExists(
-    const DeviceExistsCallback& callback) {
-  callback.Run(mouse_exists_);
+void FakeInputDeviceSettings::MouseExists(DeviceExistsCallback callback) {
+  std::move(callback).Run(mouse_exists_);
 }
 
 void FakeInputDeviceSettings::UpdateMouseSettings(
diff --git a/chrome/browser/chromeos/system/fake_input_device_settings.h b/chrome/browser/chromeos/system/fake_input_device_settings.h
index 3da56e8..1f0568d 100644
--- a/chrome/browser/chromeos/system/fake_input_device_settings.h
+++ b/chrome/browser/chromeos/system/fake_input_device_settings.h
@@ -20,13 +20,13 @@
   ~FakeInputDeviceSettings() override;
 
   // Overridden from InputDeviceSettings.
-  void TouchpadExists(const DeviceExistsCallback& callback) override;
+  void TouchpadExists(DeviceExistsCallback callback) override;
   void UpdateTouchpadSettings(const TouchpadSettings& settings) override;
   void SetTouchpadSensitivity(int value) override;
   void SetTapToClick(bool enabled) override;
   void SetThreeFingerClick(bool enabled) override;
   void SetTapDragging(bool enabled) override;
-  void MouseExists(const DeviceExistsCallback& callback) override;
+  void MouseExists(DeviceExistsCallback callback) override;
   void UpdateMouseSettings(const MouseSettings& settings) override;
   void SetMouseSensitivity(int value) override;
   void SetPrimaryButtonRight(bool right) override;
diff --git a/chrome/browser/chromeos/system/input_device_settings.h b/chrome/browser/chromeos/system/input_device_settings.h
index 430d738..9f0b63b 100644
--- a/chrome/browser/chromeos/system/input_device_settings.h
+++ b/chrome/browser/chromeos/system/input_device_settings.h
@@ -5,7 +5,7 @@
 #ifndef CHROME_BROWSER_CHROMEOS_SYSTEM_INPUT_DEVICE_SETTINGS_H_
 #define CHROME_BROWSER_CHROMEOS_SYSTEM_INPUT_DEVICE_SETTINGS_H_
 
-#include "base/callback.h"
+#include "base/callback_forward.h"
 #include "base/optional.h"
 #include "chromeos/chromeos_export.h"
 
@@ -104,7 +104,7 @@
 // Interface for configuring input device settings.
 class CHROMEOS_EXPORT InputDeviceSettings {
  public:
-  typedef base::Callback<void(bool)> DeviceExistsCallback;
+  using DeviceExistsCallback = base::OnceCallback<void(bool)>;
 
   // Interface for faking touchpad and mouse. Accessed through
   // GetFakeInterface(), implemented only in FakeInputDeviceSettings.
@@ -152,7 +152,7 @@
 
   // Calls |callback|, possibly asynchronously, after determining if a touchpad
   // is connected.
-  virtual void TouchpadExists(const DeviceExistsCallback& callback) = 0;
+  virtual void TouchpadExists(DeviceExistsCallback callback) = 0;
 
   // Updates several touchpad settings at a time. Updates only settings that
   // are set in |settings| object. It is more efficient to use this method to
@@ -177,7 +177,7 @@
 
   // Calls |callback|, possibly asynchronously, after determining if a mouse is
   // connected.
-  virtual void MouseExists(const DeviceExistsCallback& callback) = 0;
+  virtual void MouseExists(DeviceExistsCallback callback) = 0;
 
   // Updates several mouse settings at a time. Updates only settings that
   // are set in |settings| object. It is more efficient to use this method to
diff --git a/chrome/browser/chromeos/system/input_device_settings_impl_ozone.cc b/chrome/browser/chromeos/system/input_device_settings_impl_ozone.cc
index 412b742..98a97cbd 100644
--- a/chrome/browser/chromeos/system/input_device_settings_impl_ozone.cc
+++ b/chrome/browser/chromeos/system/input_device_settings_impl_ozone.cc
@@ -5,25 +5,21 @@
 #include "chrome/browser/chromeos/system/input_device_settings.h"
 
 #include "base/macros.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/browser_process_platform_part_chromeos.h"
 #include "chrome/browser/chromeos/system/fake_input_device_settings.h"
 #include "chromeos/system/devicemode.h"
 #include "content/public/browser/browser_thread.h"
-#include "services/service_manager/runner/common/client_util.h"
-#include "ui/ozone/public/input_controller.h"
-#include "ui/ozone/public/ozone_platform.h"
+#include "services/ui/public/cpp/input_devices/input_device_controller_client.h"
 
 namespace chromeos {
 namespace system {
-
 namespace {
 
 InputDeviceSettings* g_instance = nullptr;
 
-std::unique_ptr<ui::InputController> CreateStubInputControllerIfNecessary() {
-  return service_manager::ServiceManagerIsRemote()
-             ? ui::CreateStubInputController()
-             : nullptr;
-}
+// Callback from SetInternalTouchpadEnabled().
+void OnSetInternalTouchpadEnabled(bool result) {}
 
 // InputDeviceSettings for Linux without X11 (a.k.a. Ozone).
 class InputDeviceSettingsImplOzone : public InputDeviceSettings {
@@ -35,14 +31,14 @@
 
  private:
   // Overridden from InputDeviceSettings.
-  void TouchpadExists(const DeviceExistsCallback& callback) override;
+  void TouchpadExists(DeviceExistsCallback callback) override;
   void UpdateTouchpadSettings(const TouchpadSettings& settings) override;
   void SetTouchpadSensitivity(int value) override;
   void SetTapToClick(bool enabled) override;
   void SetThreeFingerClick(bool enabled) override;
   void SetTapDragging(bool enabled) override;
   void SetNaturalScroll(bool enabled) override;
-  void MouseExists(const DeviceExistsCallback& callback) override;
+  void MouseExists(DeviceExistsCallback callback) override;
   void UpdateMouseSettings(const MouseSettings& settings) override;
   void SetMouseSensitivity(int value) override;
   void SetPrimaryButtonRight(bool right) override;
@@ -52,13 +48,8 @@
   void SetInternalTouchpadEnabled(bool enabled) override;
   void SetTouchscreensEnabled(bool enabled) override;
 
-  // TODO(sad): A stub input controller is used when running inside mus.
-  // http://crbug.com/601981
-  std::unique_ptr<ui::InputController> stub_controller_;
-
-  // Cached InputController pointer. It should be fixed throughout the browser
-  // session.
-  ui::InputController* input_controller_;
+  // Cached InputDeviceControllerClient. It is owned by BrowserProcess.
+  ui::InputDeviceControllerClient* input_device_controller_client_;
 
   // Respective device setting objects.
   TouchpadSettings current_touchpad_settings_;
@@ -68,19 +59,16 @@
 };
 
 InputDeviceSettingsImplOzone::InputDeviceSettingsImplOzone()
-    : stub_controller_(CreateStubInputControllerIfNecessary()),
-      input_controller_(
-          stub_controller_
-              ? stub_controller_.get()
-              : ui::OzonePlatform::GetInstance()->GetInputController()) {
+    : input_device_controller_client_(g_browser_process->platform_part()
+                                          ->GetInputDeviceControllerClient()) {
   // Make sure the input controller does exist.
-  DCHECK(input_controller_);
+  DCHECK(input_device_controller_client_);
 }
 
 void InputDeviceSettingsImplOzone::TouchpadExists(
-    const DeviceExistsCallback& callback) {
+    DeviceExistsCallback callback) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-  callback.Run(input_controller_->HasTouchpad());
+  input_device_controller_client_->GetHasTouchpad(std::move(callback));
 }
 
 void InputDeviceSettingsImplOzone::UpdateTouchpadSettings(
@@ -92,34 +80,33 @@
 void InputDeviceSettingsImplOzone::SetTouchpadSensitivity(int value) {
   DCHECK(value >= kMinPointerSensitivity && value <= kMaxPointerSensitivity);
   current_touchpad_settings_.SetSensitivity(value);
-  input_controller_->SetTouchpadSensitivity(value);
+  input_device_controller_client_->SetTouchpadSensitivity(value);
 }
 
 void InputDeviceSettingsImplOzone::SetNaturalScroll(bool enabled) {
   current_touchpad_settings_.SetNaturalScroll(enabled);
-  input_controller_->SetNaturalScroll(enabled);
+  input_device_controller_client_->SetNaturalScroll(enabled);
 }
 
 void InputDeviceSettingsImplOzone::SetTapToClick(bool enabled) {
   current_touchpad_settings_.SetTapToClick(enabled);
-  input_controller_->SetTapToClick(enabled);
+  input_device_controller_client_->SetTapToClick(enabled);
 }
 
 void InputDeviceSettingsImplOzone::SetThreeFingerClick(bool enabled) {
   // For Alex/ZGB.
   current_touchpad_settings_.SetThreeFingerClick(enabled);
-  input_controller_->SetThreeFingerClick(enabled);
+  input_device_controller_client_->SetThreeFingerClick(enabled);
 }
 
 void InputDeviceSettingsImplOzone::SetTapDragging(bool enabled) {
   current_touchpad_settings_.SetTapDragging(enabled);
-  input_controller_->SetTapDragging(enabled);
+  input_device_controller_client_->SetTapDragging(enabled);
 }
 
-void InputDeviceSettingsImplOzone::MouseExists(
-    const DeviceExistsCallback& callback) {
+void InputDeviceSettingsImplOzone::MouseExists(DeviceExistsCallback callback) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-  callback.Run(input_controller_->HasMouse());
+  input_device_controller_client_->GetHasMouse(std::move(callback));
 }
 
 void InputDeviceSettingsImplOzone::UpdateMouseSettings(
@@ -131,12 +118,12 @@
 void InputDeviceSettingsImplOzone::SetMouseSensitivity(int value) {
   DCHECK(value >= kMinPointerSensitivity && value <= kMaxPointerSensitivity);
   current_mouse_settings_.SetSensitivity(value);
-  input_controller_->SetMouseSensitivity(value);
+  input_device_controller_client_->SetMouseSensitivity(value);
 }
 
 void InputDeviceSettingsImplOzone::SetPrimaryButtonRight(bool right) {
   current_mouse_settings_.SetPrimaryButtonRight(right);
-  input_controller_->SetPrimaryButtonRight(right);
+  input_device_controller_client_->SetPrimaryButtonRight(right);
 }
 
 void InputDeviceSettingsImplOzone::ReapplyTouchpadSettings() {
@@ -153,11 +140,12 @@
 }
 
 void InputDeviceSettingsImplOzone::SetInternalTouchpadEnabled(bool enabled) {
-  input_controller_->SetInternalTouchpadEnabled(enabled);
+  input_device_controller_client_->SetInternalTouchpadEnabled(
+      enabled, base::BindOnce(&OnSetInternalTouchpadEnabled));
 }
 
 void InputDeviceSettingsImplOzone::SetTouchscreensEnabled(bool enabled) {
-  input_controller_->SetTouchscreensEnabled(enabled);
+  input_device_controller_client_->SetTouchscreensEnabled(enabled);
 }
 
 }  // namespace
diff --git a/chrome/browser/chromeos/system/input_device_settings_impl_x11.cc b/chrome/browser/chromeos/system/input_device_settings_impl_x11.cc
index 99e1a394..59a83f8 100644
--- a/chrome/browser/chromeos/system/input_device_settings_impl_x11.cc
+++ b/chrome/browser/chromeos/system/input_device_settings_impl_x11.cc
@@ -8,6 +8,7 @@
 #include <vector>
 
 #include "base/bind.h"
+#include "base/callback.h"
 #include "base/command_line.h"
 #include "base/files/file_path.h"
 #include "base/files/file_util.h"
@@ -122,16 +123,15 @@
   DVLOG(1) << "DeviceExistsBlockingPool:" << device_type << "=" << exists->data;
 }
 
-void RunCallbackUIThread(
-    scoped_refptr<RefCountedBool> exists,
-    const InputDeviceSettings::DeviceExistsCallback& callback) {
+void RunCallbackUIThread(scoped_refptr<RefCountedBool> exists,
+                         InputDeviceSettings::DeviceExistsCallback callback) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
   DVLOG(1) << "RunCallbackUIThread " << exists->data;
-  callback.Run(exists->data);
+  std::move(callback).Run(exists->data);
 }
 
 void DeviceExists(const char* script,
-                  const InputDeviceSettings::DeviceExistsCallback& callback) {
+                  InputDeviceSettings::DeviceExistsCallback callback) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
 
   // One or both of the control scripts can apparently hang during shutdown
@@ -144,7 +144,7 @@
           base::SequencedWorkerPool::CONTINUE_ON_SHUTDOWN);
   runner->PostTaskAndReply(
       FROM_HERE, base::Bind(&DeviceExistsBlockingPool, script, exists),
-      base::Bind(&RunCallbackUIThread, exists, callback));
+      base::BindOnce(&RunCallbackUIThread, exists, std::move(callback)));
 }
 
 // InputDeviceSettings for Linux with X11.
@@ -157,14 +157,14 @@
 
  private:
   // Overridden from InputDeviceSettings.
-  void TouchpadExists(const DeviceExistsCallback& callback) override;
+  void TouchpadExists(DeviceExistsCallback callback) override;
   void UpdateTouchpadSettings(const TouchpadSettings& settings) override;
   void SetTouchpadSensitivity(int value) override;
   void SetTapToClick(bool enabled) override;
   void SetThreeFingerClick(bool enabled) override;
   void SetTapDragging(bool enabled) override;
   void SetNaturalScroll(bool enabled) override;
-  void MouseExists(const DeviceExistsCallback& callback) override;
+  void MouseExists(DeviceExistsCallback callback) override;
   void UpdateMouseSettings(const MouseSettings& settings) override;
   void SetMouseSensitivity(int value) override;
   void SetPrimaryButtonRight(bool right) override;
@@ -190,9 +190,8 @@
 InputDeviceSettingsImplX11::InputDeviceSettingsImplX11() {
 }
 
-void InputDeviceSettingsImplX11::TouchpadExists(
-    const DeviceExistsCallback& callback) {
-  DeviceExists(kDeviceTypeTouchpad, callback);
+void InputDeviceSettingsImplX11::TouchpadExists(DeviceExistsCallback callback) {
+  DeviceExists(kDeviceTypeTouchpad, std::move(callback));
 }
 
 void InputDeviceSettingsImplX11::UpdateTouchpadSettings(
@@ -235,10 +234,9 @@
   UpdateTouchpadSettings(settings);
 }
 
-void InputDeviceSettingsImplX11::MouseExists(
-    const DeviceExistsCallback& callback) {
+void InputDeviceSettingsImplX11::MouseExists(DeviceExistsCallback callback) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-  DeviceExists(kDeviceTypeMouse, callback);
+  DeviceExists(kDeviceTypeMouse, std::move(callback));
 }
 
 void InputDeviceSettingsImplX11::UpdateMouseSettings(
diff --git a/chrome/browser/devtools/devtools_file_helper.cc b/chrome/browser/devtools/devtools_file_helper.cc
index 28599b7b..463cedea 100644
--- a/chrome/browser/devtools/devtools_file_helper.cc
+++ b/chrome/browser/devtools/devtools_file_helper.cc
@@ -212,12 +212,6 @@
       delegate_(delegate),
       weak_factory_(this) {
   pref_change_registrar_.Init(profile_->GetPrefs());
-  pref_change_registrar_.Add(prefs::kDevToolsFileSystemPaths,
-      base::Bind(&DevToolsFileHelper::FileSystemPathsSettingChanged,
-                 base::Unretained(this)));
-  file_watcher_.reset(new DevToolsFileWatcher(
-      base::Bind(&DevToolsFileHelper::FilePathsChanged,
-                 weak_factory_.GetWeakPtr())));
 }
 
 DevToolsFileHelper::~DevToolsFileHelper() {
@@ -392,6 +386,14 @@
 DevToolsFileHelper::GetFileSystems() {
   file_system_paths_ = GetAddedFileSystemPaths(profile_);
   std::vector<FileSystem> file_systems;
+  if (!file_watcher_) {
+    file_watcher_.reset(new DevToolsFileWatcher(base::Bind(
+        &DevToolsFileHelper::FilePathsChanged, weak_factory_.GetWeakPtr())));
+    pref_change_registrar_.Add(
+        prefs::kDevToolsFileSystemPaths,
+        base::Bind(&DevToolsFileHelper::FileSystemPathsSettingChanged,
+                   base::Unretained(this)));
+  }
   for (auto file_system_path : file_system_paths_) {
     base::FilePath path = base::FilePath::FromUTF8Unsafe(file_system_path);
     std::string file_system_id = RegisterFileSystem(web_contents_, path);
@@ -428,6 +430,7 @@
 void DevToolsFileHelper::FileSystemPathsSettingChanged() {
   std::set<std::string> remaining;
   remaining.swap(file_system_paths_);
+  DCHECK(file_watcher_.get());
 
   for (auto file_system_path : GetAddedFileSystemPaths(profile_)) {
     if (remaining.find(file_system_path) == remaining.end()) {
diff --git a/chrome/browser/devtools/devtools_ui_bindings.cc b/chrome/browser/devtools/devtools_ui_bindings.cc
index cbdff1523..55158c2 100644
--- a/chrome/browser/devtools/devtools_ui_bindings.cc
+++ b/chrome/browser/devtools/devtools_ui_bindings.cc
@@ -1354,6 +1354,8 @@
 
 void DevToolsUIBindings::Reload() {
   reloading_ = true;
+  if (agent_host_)
+    agent_host_->DetachClient(this);
   web_contents_->GetController().Reload(content::ReloadType::NORMAL, false);
 }
 
@@ -1397,10 +1399,8 @@
   if (!reloading_)
     return;
   reloading_ = false;
-  if (agent_host_.get()) {
-    agent_host_->DetachClient(this);
+  if (agent_host_.get())
     agent_host_->AttachClient(this);
-  }
 }
 
 void DevToolsUIBindings::DocumentOnLoadCompletedInMainFrame() {
diff --git a/chrome/browser/extensions/api/font_settings/font_settings_api.cc b/chrome/browser/extensions/api/font_settings/font_settings_api.cc
index 31f9f9d..3f56296 100644
--- a/chrome/browser/extensions/api/font_settings/font_settings_api.cc
+++ b/chrome/browser/extensions/api/font_settings/font_settings_api.cc
@@ -27,7 +27,7 @@
 #include "chrome/browser/extensions/api/preference/preference_helpers.h"
 #include "chrome/browser/extensions/extension_service.h"
 #include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/ui/webui/options/font_settings_utils.h"
+#include "chrome/browser/ui/webui/settings_utils.h"
 #include "chrome/common/extensions/api/font_settings.h"
 #include "chrome/common/pref_names.h"
 #include "chrome/common/pref_names_util.h"
@@ -41,7 +41,6 @@
 namespace extensions {
 
 namespace fonts = api::font_settings;
-using options::FontSettingsUtilities;
 
 namespace {
 
@@ -161,7 +160,7 @@
     NOTREACHED();
     return;
   }
-  font_name = FontSettingsUtilities::MaybeGetLocalizedFontName(font_name);
+  font_name = settings_utils::MaybeGetLocalizedFontName(font_name);
 
   base::ListValue args;
   std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue());
@@ -247,7 +246,7 @@
   std::string font_name;
   EXTENSION_FUNCTION_VALIDATE(
       pref && pref->GetValue()->GetAsString(&font_name));
-  font_name = FontSettingsUtilities::MaybeGetLocalizedFontName(font_name);
+  font_name = settings_utils::MaybeGetLocalizedFontName(font_name);
 
   // We don't support incognito-specific font prefs, so don't consider them when
   // getting level of control.
diff --git a/chrome/browser/net/chrome_mojo_proxy_resolver_factory.cc b/chrome/browser/net/chrome_mojo_proxy_resolver_factory.cc
new file mode 100644
index 0000000..3ed6244
--- /dev/null
+++ b/chrome/browser/net/chrome_mojo_proxy_resolver_factory.cc
@@ -0,0 +1,133 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/net/chrome_mojo_proxy_resolver_factory.h"
+
+#include <utility>
+
+#include "base/logging.h"
+#include "base/memory/ptr_util.h"
+#include "base/memory/singleton.h"
+#include "base/single_thread_task_runner.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "content/public/browser/browser_thread.h"
+
+#if !defined(OS_ANDROID)
+#include "chrome/grit/generated_resources.h"
+#include "content/public/browser/utility_process_host.h"
+#include "content/public/browser/utility_process_host_client.h"
+#include "services/service_manager/public/cpp/interface_provider.h"
+#include "ui/base/l10n/l10n_util.h"
+#else  // defined(OS_ANDROID)
+#include "mojo/public/cpp/bindings/strong_binding.h"
+#include "net/proxy/mojo_proxy_resolver_factory_impl.h"
+#endif  // !defined(OS_ANDROID)
+
+namespace {
+const int kUtilityProcessIdleTimeoutSeconds = 5;
+}
+
+// static
+ChromeMojoProxyResolverFactory* ChromeMojoProxyResolverFactory::GetInstance() {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
+  return base::Singleton<
+      ChromeMojoProxyResolverFactory,
+      base::LeakySingletonTraits<ChromeMojoProxyResolverFactory>>::get();
+}
+
+ChromeMojoProxyResolverFactory::ChromeMojoProxyResolverFactory() {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
+}
+
+ChromeMojoProxyResolverFactory::~ChromeMojoProxyResolverFactory() {
+  DCHECK(thread_checker_.CalledOnValidThread());
+}
+
+std::unique_ptr<base::ScopedClosureRunner>
+ChromeMojoProxyResolverFactory::CreateResolver(
+    const std::string& pac_script,
+    mojo::InterfaceRequest<net::interfaces::ProxyResolver> req,
+    net::interfaces::ProxyResolverFactoryRequestClientPtr client) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  if (!resolver_factory_)
+    CreateFactory();
+
+  if (!resolver_factory_) {
+    // If factory creation failed, close |req|'s message pipe, which should
+    // cause a connection error.
+    req = nullptr;
+    return nullptr;
+  }
+  idle_timer_.Stop();
+  num_proxy_resolvers_++;
+  resolver_factory_->CreateResolver(pac_script, std::move(req),
+                                    std::move(client));
+  return base::MakeUnique<base::ScopedClosureRunner>(
+      base::Bind(&ChromeMojoProxyResolverFactory::OnResolverDestroyed,
+                 base::Unretained(this)));
+}
+
+void ChromeMojoProxyResolverFactory::CreateFactory() {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK(!resolver_factory_);
+
+#if defined(OS_ANDROID)
+  mojo::MakeStrongBinding(base::MakeUnique<net::MojoProxyResolverFactoryImpl>(),
+                          mojo::MakeRequest(&resolver_factory_));
+#else   // !defined(OS_ANDROID)
+  DCHECK(!weak_utility_process_host_);
+
+  DVLOG(1) << "Attempting to create utility process for proxy resolver";
+  content::UtilityProcessHost* utility_process_host =
+      content::UtilityProcessHost::Create(
+          scoped_refptr<content::UtilityProcessHostClient>(),
+          base::ThreadTaskRunnerHandle::Get());
+  utility_process_host->SetName(
+      l10n_util::GetStringUTF16(IDS_UTILITY_PROCESS_PROXY_RESOLVER_NAME));
+  bool process_started = utility_process_host->Start();
+  if (process_started) {
+    BindInterface(utility_process_host, &resolver_factory_);
+    weak_utility_process_host_ = utility_process_host->AsWeakPtr();
+  } else {
+    LOG(ERROR) << "Unable to connect to utility process";
+    return;
+  }
+#endif  // defined(OS_ANDROID)
+
+  resolver_factory_.set_connection_error_handler(base::Bind(
+      &ChromeMojoProxyResolverFactory::DestroyFactory, base::Unretained(this)));
+}
+
+void ChromeMojoProxyResolverFactory::DestroyFactory() {
+  resolver_factory_.reset();
+#if !defined(OS_ANDROID)
+  delete weak_utility_process_host_.get();
+  weak_utility_process_host_.reset();
+#endif
+}
+
+void ChromeMojoProxyResolverFactory::OnResolverDestroyed() {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK_GT(num_proxy_resolvers_, 0u);
+  if (--num_proxy_resolvers_ == 0) {
+    // When all proxy resolvers have been destroyed, the proxy resolver factory
+    // is no longer needed. However, new proxy resolvers may be created
+    // shortly after being destroyed (e.g. due to a network change).
+    //
+    // On desktop, where a utility process is used, if the utility process is
+    // shut down immediately, this would cause unnecessary process churn, so
+    // wait for an idle timeout before shutting down the proxy resolver utility
+    // process.
+    idle_timer_.Start(
+        FROM_HERE,
+        base::TimeDelta::FromSeconds(kUtilityProcessIdleTimeoutSeconds), this,
+        &ChromeMojoProxyResolverFactory::OnIdleTimeout);
+  }
+}
+
+void ChromeMojoProxyResolverFactory::OnIdleTimeout() {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK_EQ(num_proxy_resolvers_, 0u);
+  DestroyFactory();
+}
diff --git a/chrome/browser/net/chrome_mojo_proxy_resolver_factory.h b/chrome/browser/net/chrome_mojo_proxy_resolver_factory.h
new file mode 100644
index 0000000..610b146
--- /dev/null
+++ b/chrome/browser/net/chrome_mojo_proxy_resolver_factory.h
@@ -0,0 +1,76 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_NET_CHROME_MOJO_PROXY_RESOLVER_FACTORY_H_
+#define CHROME_BROWSER_NET_CHROME_MOJO_PROXY_RESOLVER_FACTORY_H_
+
+#include <stddef.h>
+
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "base/threading/thread_checker.h"
+#include "base/timer/timer.h"
+#include "net/proxy/mojo_proxy_resolver_factory.h"
+
+#if !defined(OS_ANDROID)
+namespace content {
+class UtilityProcessHost;
+}
+#endif
+
+namespace base {
+template <typename Type>
+struct DefaultSingletonTraits;
+}  // namespace base
+
+// A factory used to create connections to Mojo proxy resolver services.  On
+// Android, the proxy resolvers will run in the browser process, and on other
+// platforms, they'll all be run in the same utility process. Utility process
+// crashes are detected and the utility process is automatically restarted.
+class ChromeMojoProxyResolverFactory : public net::MojoProxyResolverFactory {
+ public:
+  static ChromeMojoProxyResolverFactory* GetInstance();
+
+  // Overridden from net::MojoProxyResolverFactory:
+  std::unique_ptr<base::ScopedClosureRunner> CreateResolver(
+      const std::string& pac_script,
+      mojo::InterfaceRequest<net::interfaces::ProxyResolver> req,
+      net::interfaces::ProxyResolverFactoryRequestClientPtr client) override;
+
+ private:
+  friend struct base::DefaultSingletonTraits<ChromeMojoProxyResolverFactory>;
+  ChromeMojoProxyResolverFactory();
+  ~ChromeMojoProxyResolverFactory() override;
+
+  // Creates the proxy resolver factory. On desktop, creates a new utility
+  // process before creating it out of process. On Android, creates it on the
+  // current thread.
+  void CreateFactory();
+
+  // Destroys |resolver_factory_|.
+  void DestroyFactory();
+
+  // Invoked each time a proxy resolver is destroyed.
+  void OnResolverDestroyed();
+
+  // Invoked once an idle timeout has elapsed after all proxy resolvers are
+  // destroyed.
+  void OnIdleTimeout();
+
+  net::interfaces::ProxyResolverFactoryPtr resolver_factory_;
+
+#if !defined(OS_ANDROID)
+  base::WeakPtr<content::UtilityProcessHost> weak_utility_process_host_;
+#endif
+
+  size_t num_proxy_resolvers_ = 0;
+
+  base::OneShotTimer idle_timer_;
+
+  base::ThreadChecker thread_checker_;
+
+  DISALLOW_COPY_AND_ASSIGN(ChromeMojoProxyResolverFactory);
+};
+
+#endif  // CHROME_BROWSER_NET_CHROME_MOJO_PROXY_RESOLVER_FACTORY_H_
diff --git a/chrome/browser/net/proxy_service_factory.cc b/chrome/browser/net/proxy_service_factory.cc
index c08f382..dcb7c5c 100644
--- a/chrome/browser/net/proxy_service_factory.cc
+++ b/chrome/browser/net/proxy_service_factory.cc
@@ -15,16 +15,16 @@
 #include "build/build_config.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/io_thread.h"
+#include "chrome/browser/net/chrome_mojo_proxy_resolver_factory.h"
 #include "chrome/common/chrome_switches.h"
 #include "components/proxy_config/pref_proxy_config_tracker_impl.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/common/content_switches.h"
 #include "net/proxy/dhcp_proxy_script_fetcher_factory.h"
 #include "net/proxy/proxy_config_service.h"
-#include "net/proxy/proxy_resolver_v8.h"
 #include "net/proxy/proxy_script_fetcher_impl.h"
 #include "net/proxy/proxy_service.h"
-#include "net/proxy/proxy_service_v8.h"
+#include "net/proxy/proxy_service_mojo.h"
 #include "net/url_request/url_request_context.h"
 
 #if defined(OS_CHROMEOS)
@@ -32,11 +32,6 @@
 #include "chromeos/network/proxy/proxy_config_service_impl.h"
 #endif  // defined(OS_CHROMEOS)
 
-#if !defined(OS_ANDROID)
-#include "chrome/browser/net/utility_process_mojo_proxy_resolver_factory.h"
-#include "net/proxy/proxy_service_mojo.h"
-#endif
-
 using content::BrowserThread;
 
 // static
@@ -128,20 +123,12 @@
     dhcp_proxy_script_fetcher = dhcp_factory.Create(context);
 #endif
 
-#if !defined(OS_ANDROID)
     proxy_service = net::CreateProxyServiceUsingMojoFactory(
-        UtilityProcessMojoProxyResolverFactory::GetInstance(),
+        ChromeMojoProxyResolverFactory::GetInstance(),
         std::move(proxy_config_service),
         new net::ProxyScriptFetcherImpl(context),
         std::move(dhcp_proxy_script_fetcher), context->host_resolver(), net_log,
         network_delegate);
-#else
-    proxy_service = net::CreateProxyServiceUsingV8ProxyResolver(
-        std::move(proxy_config_service),
-        new net::ProxyScriptFetcherImpl(context),
-        std::move(dhcp_proxy_script_fetcher), context->host_resolver(), net_log,
-        network_delegate);
-#endif  // !defined(OS_ANDROID)
   } else {
     proxy_service = net::ProxyService::CreateUsingSystemProxyResolver(
         std::move(proxy_config_service), net_log);
diff --git a/chrome/browser/net/utility_process_mojo_proxy_resolver_factory.cc b/chrome/browser/net/utility_process_mojo_proxy_resolver_factory.cc
deleted file mode 100644
index 4a88a57..0000000
--- a/chrome/browser/net/utility_process_mojo_proxy_resolver_factory.cc
+++ /dev/null
@@ -1,121 +0,0 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/net/utility_process_mojo_proxy_resolver_factory.h"
-
-#include <utility>
-
-#include "base/logging.h"
-#include "base/memory/ptr_util.h"
-#include "base/memory/singleton.h"
-#include "base/single_thread_task_runner.h"
-#include "base/threading/thread_task_runner_handle.h"
-#include "chrome/grit/generated_resources.h"
-#include "content/public/browser/browser_thread.h"
-#include "content/public/browser/utility_process_host.h"
-#include "content/public/browser/utility_process_host_client.h"
-#include "services/service_manager/public/cpp/interface_provider.h"
-#include "ui/base/l10n/l10n_util.h"
-
-namespace {
-const int kUtilityProcessIdleTimeoutSeconds = 5;
-}
-
-// static
-UtilityProcessMojoProxyResolverFactory*
-UtilityProcessMojoProxyResolverFactory::GetInstance() {
-  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
-  return base::Singleton<UtilityProcessMojoProxyResolverFactory,
-                         base::LeakySingletonTraits<
-                             UtilityProcessMojoProxyResolverFactory>>::get();
-}
-
-UtilityProcessMojoProxyResolverFactory::
-    UtilityProcessMojoProxyResolverFactory() {
-  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
-}
-
-UtilityProcessMojoProxyResolverFactory::
-    ~UtilityProcessMojoProxyResolverFactory() {
-  DCHECK(thread_checker_.CalledOnValidThread());
-}
-
-void UtilityProcessMojoProxyResolverFactory::CreateProcessAndConnect() {
-  DCHECK(thread_checker_.CalledOnValidThread());
-  DCHECK(!resolver_factory_);
-  DCHECK(!weak_utility_process_host_);
-  DVLOG(1) << "Attempting to create utility process for proxy resolver";
-  content::UtilityProcessHost* utility_process_host =
-      content::UtilityProcessHost::Create(
-          scoped_refptr<content::UtilityProcessHostClient>(),
-          base::ThreadTaskRunnerHandle::Get());
-  utility_process_host->SetName(l10n_util::GetStringUTF16(
-      IDS_UTILITY_PROCESS_PROXY_RESOLVER_NAME));
-  bool process_started = utility_process_host->Start();
-  if (process_started) {
-    BindInterface(utility_process_host, &resolver_factory_);
-    resolver_factory_.set_connection_error_handler(
-        base::Bind(&UtilityProcessMojoProxyResolverFactory::OnConnectionError,
-                   base::Unretained(this)));
-    weak_utility_process_host_ = utility_process_host->AsWeakPtr();
-  } else {
-    LOG(ERROR) << "Unable to connect to utility process";
-  }
-}
-
-std::unique_ptr<base::ScopedClosureRunner>
-UtilityProcessMojoProxyResolverFactory::CreateResolver(
-    const std::string& pac_script,
-    mojo::InterfaceRequest<net::interfaces::ProxyResolver> req,
-    net::interfaces::ProxyResolverFactoryRequestClientPtr client) {
-  DCHECK(thread_checker_.CalledOnValidThread());
-  if (!resolver_factory_)
-    CreateProcessAndConnect();
-
-  if (!resolver_factory_) {
-    // If there's still no factory, then utility process creation failed so
-    // close |req|'s message pipe, which should cause a connection error.
-    req = nullptr;
-    return nullptr;
-  }
-  idle_timer_.Stop();
-  num_proxy_resolvers_++;
-  resolver_factory_->CreateResolver(pac_script, std::move(req),
-                                    std::move(client));
-  return base::MakeUnique<base::ScopedClosureRunner>(
-      base::Bind(&UtilityProcessMojoProxyResolverFactory::OnResolverDestroyed,
-                 base::Unretained(this)));
-}
-
-void UtilityProcessMojoProxyResolverFactory::OnConnectionError() {
-  DVLOG(1) << "Disconnection from utility process detected";
-  resolver_factory_.reset();
-  delete weak_utility_process_host_.get();
-  weak_utility_process_host_.reset();
-}
-
-void UtilityProcessMojoProxyResolverFactory::OnResolverDestroyed() {
-  DCHECK(thread_checker_.CalledOnValidThread());
-  DCHECK_GT(num_proxy_resolvers_, 0u);
-  if (--num_proxy_resolvers_ == 0) {
-    // When all proxy resolvers have been destroyed, the proxy resolver utility
-    // process is no longer needed. However, new proxy resolvers may be created
-    // shortly after being destroyed (e.g. due to a network change). If the
-    // utility process is shut down immediately, this would cause unnecessary
-    // process churn, so wait for an idle timeout before shutting down the
-    // proxy resolver utility process.
-    idle_timer_.Start(
-        FROM_HERE,
-        base::TimeDelta::FromSeconds(kUtilityProcessIdleTimeoutSeconds), this,
-        &UtilityProcessMojoProxyResolverFactory::OnIdleTimeout);
-  }
-}
-
-void UtilityProcessMojoProxyResolverFactory::OnIdleTimeout() {
-  DCHECK(thread_checker_.CalledOnValidThread());
-  DCHECK_EQ(num_proxy_resolvers_, 0u);
-  delete weak_utility_process_host_.get();
-  weak_utility_process_host_.reset();
-  resolver_factory_.reset();
-}
diff --git a/chrome/browser/net/utility_process_mojo_proxy_resolver_factory.h b/chrome/browser/net/utility_process_mojo_proxy_resolver_factory.h
deleted file mode 100644
index b39362372..0000000
--- a/chrome/browser/net/utility_process_mojo_proxy_resolver_factory.h
+++ /dev/null
@@ -1,71 +0,0 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_NET_UTILITY_PROCESS_MOJO_PROXY_RESOLVER_FACTORY_H_
-#define CHROME_BROWSER_NET_UTILITY_PROCESS_MOJO_PROXY_RESOLVER_FACTORY_H_
-
-#include <stddef.h>
-
-#include "base/macros.h"
-#include "base/memory/weak_ptr.h"
-#include "base/threading/thread_checker.h"
-#include "base/timer/timer.h"
-#include "net/proxy/mojo_proxy_resolver_factory.h"
-
-namespace content {
-class UtilityProcessHost;
-}
-namespace base {
-template <typename Type>
-struct DefaultSingletonTraits;
-}  // namespace base
-
-// A factory used to create connections to Mojo proxy resolver services run in a
-// utility process. All Mojo proxy resolver services will be run in the same
-// utility process. Utility process crashes are detected and the utility
-// process is automatically restarted.
-class UtilityProcessMojoProxyResolverFactory
-    : public net::MojoProxyResolverFactory {
- public:
-  static UtilityProcessMojoProxyResolverFactory* GetInstance();
-
-  // Overridden from net::MojoProxyResolverFactory:
-  std::unique_ptr<base::ScopedClosureRunner> CreateResolver(
-      const std::string& pac_script,
-      mojo::InterfaceRequest<net::interfaces::ProxyResolver> req,
-      net::interfaces::ProxyResolverFactoryRequestClientPtr client) override;
-
- private:
-  friend struct base::DefaultSingletonTraits<
-      UtilityProcessMojoProxyResolverFactory>;
-  UtilityProcessMojoProxyResolverFactory();
-  ~UtilityProcessMojoProxyResolverFactory() override;
-
-  // Error handler callback for |resolver_factory_|.
-  void OnConnectionError();
-
-  // Invoked each time a proxy resolver is destroyed.
-  void OnResolverDestroyed();
-
-  // Invoked once an idle timeout has elapsed after all proxy resolvers are
-  // destroyed.
-  void OnIdleTimeout();
-
-  // Creates a new utility process and connects to its Mojo proxy resolver
-  // factory.
-  void CreateProcessAndConnect();
-
-  net::interfaces::ProxyResolverFactoryPtr resolver_factory_;
-
-  base::WeakPtr<content::UtilityProcessHost> weak_utility_process_host_;
-  size_t num_proxy_resolvers_ = 0;
-
-  base::OneShotTimer idle_timer_;
-
-  base::ThreadChecker thread_checker_;
-
-  DISALLOW_COPY_AND_ASSIGN(UtilityProcessMojoProxyResolverFactory);
-};
-
-#endif  // CHROME_BROWSER_NET_UTILITY_PROCESS_MOJO_PROXY_RESOLVER_FACTORY_H_
diff --git a/chrome/browser/policy/policy_prefs_browsertest.cc b/chrome/browser/policy/policy_prefs_browsertest.cc
index d7f2b93..4235de509 100644
--- a/chrome/browser/policy/policy_prefs_browsertest.cc
+++ b/chrome/browser/policy/policy_prefs_browsertest.cc
@@ -32,6 +32,7 @@
 #include "chrome/browser/search_engines/template_url_service_factory.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
+#include "chrome/common/url_constants.h"
 #include "chrome/test/base/in_process_browser_test.h"
 #include "chrome/test/base/search_test_utils.h"
 #include "chrome/test/base/ui_test_utils.h"
@@ -58,8 +59,6 @@
 
 namespace {
 
-const char kMainSettingsPage[] = "chrome://settings-frame";
-
 const char kCrosSettingsPrefix[] = "cros.";
 
 std::string GetPolicyName(const std::string& policy_name_decorated) {
@@ -375,6 +374,8 @@
   DISALLOW_COPY_AND_ASSIGN(PolicyTestCases);
 };
 
+#if defined(OS_CHROMEOS)
+
 // Returns a pseudo-random integer distributed in [0, range).
 int GetRandomNumber(int range) {
   return rand() % range;
@@ -480,6 +481,8 @@
   }
 }
 
+#endif  // defined(OS_CHROMEOS)
+
 }  // namespace
 
 typedef InProcessBrowserTest PolicyPrefsTestCoverageTest;
@@ -604,6 +607,8 @@
       public testing::WithParamInterface<std::vector<std::string> > {
 };
 
+#if defined(OS_CHROMEOS)
+
 // Verifies that controlled setting indicators correctly show whether a pref's
 // value is recommended or enforced by a corresponding policy.
 IN_PROC_BROWSER_TEST_P(PolicyPrefIndicatorTest, CheckPolicyIndicators) {
@@ -611,7 +616,8 @@
   PrefService* local_state = g_browser_process->local_state();
   PrefService* user_prefs = browser()->profile()->GetPrefs();
 
-  ui_test_utils::NavigateToURL(browser(), GURL(kMainSettingsPage));
+  ui_test_utils::NavigateToURL(browser(),
+                               GURL(chrome::kChromeUISettingsFrameURL));
 
   for (std::vector<std::string>::const_iterator policy = GetParam().begin();
        policy != GetParam().end();
@@ -763,8 +769,10 @@
           prefs->ClearPref(pref_mapping->pref().c_str());
         }
 
-        if (!pref_mapping->indicator_test_url().empty())
-          ui_test_utils::NavigateToURL(browser(), GURL(kMainSettingsPage));
+        if (!pref_mapping->indicator_test_url().empty()) {
+          ui_test_utils::NavigateToURL(browser(),
+                                       GURL(chrome::kChromeUISettingsFrameURL));
+        }
       }
     }
   }
@@ -774,4 +782,6 @@
                         PolicyPrefIndicatorTest,
                         testing::ValuesIn(SplitPoliciesIntoChunks(10)));
 
+#endif  // defined(OS_CHROMEOS)
+
 }  // namespace policy
diff --git a/chrome/browser/resources/BUILD.gn b/chrome/browser/resources/BUILD.gn
index 16864c45..479e357f 100644
--- a/chrome/browser/resources/BUILD.gn
+++ b/chrome/browser/resources/BUILD.gn
@@ -106,16 +106,6 @@
     output_dir = "$root_gen_dir/chrome"
   }
 
-  grit("options_resources") {
-    source = "options_resources.grd"
-    defines = chrome_grit_defines
-    outputs = [
-      "grit/options_resources.h",
-      "options_resources.pak",
-    ]
-    output_dir = "$root_gen_dir/chrome"
-  }
-
   grit("settings_resources") {
     if (use_vulcanize) {
       source = "settings/settings_resources_vulcanized.grd"
@@ -141,6 +131,18 @@
   }
 }
 
+if (is_chromeos) {
+  grit("options_resources") {
+    source = "options_resources.grd"
+    defines = chrome_grit_defines
+    outputs = [
+      "grit/options_resources.h",
+      "options_resources.pak",
+    ]
+    output_dir = "$root_gen_dir/chrome"
+  }
+}
+
 if (enable_extensions) {
   grit("sync_file_system_internals_resources") {
     source = "sync_file_system_internals_resources.grd"
diff --git a/chrome/browser/resources/chromeos/login/oobe_buttons.html b/chrome/browser/resources/chromeos/login/oobe_buttons.html
index 03487202..1272d9f 100644
--- a/chrome/browser/resources/chromeos/login/oobe_buttons.html
+++ b/chrome/browser/resources/chromeos/login/oobe_buttons.html
@@ -6,7 +6,6 @@
 <link rel="import" href="chrome://resources/polymer/v1_0/iron-icon/iron-icon.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/iron-iconset-svg/iron-iconset-svg.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/paper-button/paper-button.html">
-<link rel="import" href="chrome://resources/polymer/v1_0/paper-fab/paper-fab.html">
 
 <iron-iconset-svg name="oobe-buttons" size="24">
   <svg>
diff --git a/chrome/browser/resources/md_extensions/manager.html b/chrome/browser/resources/md_extensions/manager.html
index 0ad334b..237ed717 100644
--- a/chrome/browser/resources/md_extensions/manager.html
+++ b/chrome/browser/resources/md_extensions/manager.html
@@ -48,6 +48,22 @@
         overflow-y: auto;
       }
 
+      /* Neon-animated pages set the position of the content to be absolute,
+       * which is necessary for animations, but breaks sub-page layout. Apply a
+       * static position to the selected page. See
+       * https://github.com/PolymerElements/neon-animation/issues/101 for
+       * conversations.
+       * Note: Another proposed solution was
+       *   neon-animated-pages .iron-selected:not(.neon-animating) {
+       *     position: relative;
+       *   }
+       * but the below appears to work fine, and is in line with what settings
+       * does.
+       */
+      neon-animated-pages ::content > .iron-selected {
+        position: static;
+      }
+
       extensions-item {
         display: inline-block;
       }
diff --git a/chrome/browser/resources/settings/focus_row_behavior.js b/chrome/browser/resources/settings/focus_row_behavior.js
index 286c4eb6..0946758d 100644
--- a/chrome/browser/resources/settings/focus_row_behavior.js
+++ b/chrome/browser/resources/settings/focus_row_behavior.js
@@ -14,12 +14,15 @@
 
 FocusRowDelegate.prototype = {
   /**
+   * This function gets called when the [focus-row-control] element receives
+   * the focus event.
    * @override
    * @param {!cr.ui.FocusRow} row
    * @param {!Event} e
    */
   onFocus: function(row, e) {
     this.listItem_.lastFocused = e.path[0];
+    this.listItem_.tabIndex = -1;
   },
 
   /**
@@ -137,7 +140,10 @@
     }
   },
 
-  /** @private */
+  /**
+   * This function gets called when the row itself receives the focus event.
+   * @private
+   */
   onFocus_: function() {
     if (this.mouseFocused_) {
       this.mouseFocused_ = false;  // Consume and reset flag.
diff --git a/chrome/browser/resources/settings/settings_ui/settings_ui.js b/chrome/browser/resources/settings/settings_ui/settings_ui.js
index 3b15d6d..de88756f 100644
--- a/chrome/browser/resources/settings/settings_ui/settings_ui.js
+++ b/chrome/browser/resources/settings/settings_ui/settings_ui.js
@@ -156,8 +156,8 @@
       };
     }
 
-    this.showAndroidApps_ = loadTimeData.valueExists('androidAppsAllowed') &&
-        loadTimeData.getBoolean('androidAppsAllowed');
+    this.showAndroidApps_ = loadTimeData.valueExists('androidAppsVisible') &&
+        loadTimeData.getBoolean('androidAppsVisible');
 
     this.addEventListener('show-container', function() {
       this.$.container.style.visibility = 'visible';
diff --git a/chrome/browser/safe_browsing/chrome_password_protection_service.cc b/chrome/browser/safe_browsing/chrome_password_protection_service.cc
index 63168e7a..86be5bd66 100644
--- a/chrome/browser/safe_browsing/chrome_password_protection_service.cc
+++ b/chrome/browser/safe_browsing/chrome_password_protection_service.cc
@@ -55,8 +55,15 @@
   if (content_settings()) {
     CleanUpExpiredVerdicts();
     UMA_HISTOGRAM_COUNTS_1000(
-        "PasswordProtection.NumberOfCachedVerdictBeforeShutdown",
-        GetStoredVerdictCount());
+        "PasswordProtection.NumberOfCachedVerdictBeforeShutdown."
+        "PasswordOnFocus",
+        GetStoredVerdictCount(
+            LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE));
+    UMA_HISTOGRAM_COUNTS_1000(
+        "PasswordProtection.NumberOfCachedVerdictBeforeShutdown."
+        "ProtectedPasswordEntry",
+        GetStoredVerdictCount(
+            LoginReputationClientRequest::PASSWORD_REUSE_EVENT));
   }
 }
 
diff --git a/chrome/browser/safe_browsing/chrome_password_protection_service_unittest.cc b/chrome/browser/safe_browsing/chrome_password_protection_service_unittest.cc
index e7670c2..02b84c1 100644
--- a/chrome/browser/safe_browsing/chrome_password_protection_service_unittest.cc
+++ b/chrome/browser/safe_browsing/chrome_password_protection_service_unittest.cc
@@ -75,6 +75,7 @@
   }
 
   void CacheVerdict(const GURL& url,
+                    LoginReputationClientRequest::TriggerType trigger_type,
                     LoginReputationClientResponse* verdict,
                     const base::Time& receive_time) override {}
 
diff --git a/chrome/browser/signin/signin_promo.cc b/chrome/browser/signin/signin_promo.cc
index cfbc4eb..968fd3f 100644
--- a/chrome/browser/signin/signin_promo.cc
+++ b/chrome/browser/signin/signin_promo.cc
@@ -21,7 +21,6 @@
 #include "chrome/browser/signin/signin_error_controller_factory.h"
 #include "chrome/browser/signin/signin_manager_factory.h"
 #include "chrome/browser/signin/signin_promo_util.h"
-#include "chrome/browser/ui/webui/options/core_options_handler.h"
 #include "chrome/browser/ui/webui/theme_source.h"
 #include "chrome/common/pref_names.h"
 #include "chrome/common/url_constants.h"
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn
index 27d85bf..2cd97f9 100644
--- a/chrome/browser/ui/BUILD.gn
+++ b/chrome/browser/ui/BUILD.gn
@@ -211,10 +211,8 @@
     "webui/bluetooth_internals/bluetooth_internals_ui.h",
     "webui/chrome_web_ui_controller_factory.cc",
     "webui/chrome_web_ui_controller_factory.h",
-    "webui/chromeos/bluetooth_pairing_ui.cc",
-    "webui/chromeos/bluetooth_pairing_ui.h",
-    "webui/chromeos/certificate_manager_dialog_ui.cc",
-    "webui/chromeos/certificate_manager_dialog_ui.h",
+
+    # TODO(dbeam): why are all these /chromeos/ files on all platforms?
     "webui/chromeos/choose_mobile_network_ui.cc",
     "webui/chromeos/choose_mobile_network_ui.h",
     "webui/chromeos/cryptohome_ui.cc",
@@ -320,8 +318,6 @@
     "webui/chromeos/network_ui.h",
     "webui/chromeos/power_ui.cc",
     "webui/chromeos/power_ui.h",
-    "webui/chromeos/proxy_settings_ui.cc",
-    "webui/chromeos/proxy_settings_ui.h",
     "webui/chromeos/set_time_ui.cc",
     "webui/chromeos/set_time_ui.h",
     "webui/chromeos/sim_unlock_ui.cc",
@@ -977,103 +973,6 @@
       "webui/ntp/ntp_resource_cache.h",
       "webui/ntp/ntp_resource_cache_factory.cc",
       "webui/ntp/ntp_resource_cache_factory.h",
-      "webui/options/autofill_options_handler.cc",
-      "webui/options/autofill_options_handler.h",
-      "webui/options/automatic_settings_reset_handler.cc",
-      "webui/options/automatic_settings_reset_handler.h",
-      "webui/options/browser_options_handler.cc",
-      "webui/options/browser_options_handler.h",
-      "webui/options/chromeos/accounts_options_handler.cc",
-      "webui/options/chromeos/accounts_options_handler.h",
-      "webui/options/chromeos/bluetooth_options_handler.cc",
-      "webui/options/chromeos/bluetooth_options_handler.h",
-      "webui/options/chromeos/change_picture_options_handler.cc",
-      "webui/options/chromeos/change_picture_options_handler.h",
-      "webui/options/chromeos/core_chromeos_options_handler.cc",
-      "webui/options/chromeos/core_chromeos_options_handler.h",
-      "webui/options/chromeos/cros_language_options_handler.cc",
-      "webui/options/chromeos/cros_language_options_handler.h",
-      "webui/options/chromeos/date_time_options_handler.cc",
-      "webui/options/chromeos/date_time_options_handler.h",
-      "webui/options/chromeos/display_options_handler.cc",
-      "webui/options/chromeos/display_options_handler.h",
-      "webui/options/chromeos/display_overscan_handler.cc",
-      "webui/options/chromeos/display_overscan_handler.h",
-      "webui/options/chromeos/internet_options_handler.cc",
-      "webui/options/chromeos/internet_options_handler.h",
-      "webui/options/chromeos/internet_options_handler_strings.cc",
-      "webui/options/chromeos/internet_options_handler_strings.h",
-      "webui/options/chromeos/keyboard_handler.cc",
-      "webui/options/chromeos/keyboard_handler.h",
-      "webui/options/chromeos/options_stylus_handler.cc",
-      "webui/options/chromeos/options_stylus_handler.h",
-      "webui/options/chromeos/pointer_handler.cc",
-      "webui/options/chromeos/pointer_handler.h",
-      "webui/options/chromeos/power_handler.cc",
-      "webui/options/chromeos/power_handler.h",
-      "webui/options/chromeos/proxy_handler.cc",
-      "webui/options/chromeos/proxy_handler.h",
-      "webui/options/chromeos/stats_options_handler.cc",
-      "webui/options/chromeos/stats_options_handler.h",
-      "webui/options/chromeos/storage_manager_handler.cc",
-      "webui/options/chromeos/storage_manager_handler.h",
-      "webui/options/chromeos/user_image_source.cc",
-      "webui/options/chromeos/user_image_source.h",
-      "webui/options/clear_browser_data_handler.cc",
-      "webui/options/clear_browser_data_handler.h",
-      "webui/options/content_settings_handler.cc",
-      "webui/options/content_settings_handler.h",
-      "webui/options/cookies_view_handler.cc",
-      "webui/options/cookies_view_handler.h",
-      "webui/options/core_options_handler.cc",
-      "webui/options/core_options_handler.h",
-      "webui/options/create_profile_handler.cc",
-      "webui/options/create_profile_handler.h",
-      "webui/options/easy_unlock_handler.cc",
-      "webui/options/easy_unlock_handler.h",
-      "webui/options/font_settings_handler.cc",
-      "webui/options/font_settings_handler.h",
-      "webui/options/font_settings_utils.cc",
-      "webui/options/font_settings_utils.h",
-      "webui/options/font_settings_utils_linux.cc",
-      "webui/options/font_settings_utils_mac.mm",
-      "webui/options/font_settings_utils_win.cc",
-      "webui/options/handler_options_handler.cc",
-      "webui/options/handler_options_handler.h",
-      "webui/options/help_overlay_handler.cc",
-      "webui/options/help_overlay_handler.h",
-      "webui/options/home_page_overlay_handler.cc",
-      "webui/options/home_page_overlay_handler.h",
-      "webui/options/import_data_handler.cc",
-      "webui/options/import_data_handler.h",
-      "webui/options/language_dictionary_overlay_handler.cc",
-      "webui/options/language_dictionary_overlay_handler.h",
-      "webui/options/language_options_handler.cc",
-      "webui/options/language_options_handler.h",
-      "webui/options/language_options_handler_common.cc",
-      "webui/options/language_options_handler_common.h",
-      "webui/options/manage_profile_handler.cc",
-      "webui/options/manage_profile_handler.h",
-      "webui/options/media_devices_selection_handler.cc",
-      "webui/options/media_devices_selection_handler.h",
-      "webui/options/options_ui.cc",
-      "webui/options/options_ui.h",
-      "webui/options/password_manager_handler.cc",
-      "webui/options/password_manager_handler.h",
-      "webui/options/reset_profile_settings_handler.cc",
-      "webui/options/reset_profile_settings_handler.h",
-      "webui/options/search_engine_manager_handler.cc",
-      "webui/options/search_engine_manager_handler.h",
-      "webui/options/startup_pages_handler.cc",
-      "webui/options/startup_pages_handler.h",
-      "webui/options/supervised_user_create_confirm_handler.cc",
-      "webui/options/supervised_user_create_confirm_handler.h",
-      "webui/options/supervised_user_import_handler.cc",
-      "webui/options/supervised_user_import_handler.h",
-      "webui/options/supervised_user_learn_more_handler.cc",
-      "webui/options/supervised_user_learn_more_handler.h",
-      "webui/options/sync_setup_handler.cc",
-      "webui/options/sync_setup_handler.h",
       "webui/plural_string_handler.cc",
       "webui/plural_string_handler.h",
       "webui/policy_indicator_localized_strings_provider.cc",
@@ -1281,6 +1180,104 @@
       "views/select_file_dialog_extension.h",
       "views/select_file_dialog_extension_factory.cc",
       "views/select_file_dialog_extension_factory.h",
+      "webui/chromeos/bluetooth_pairing_ui.cc",
+      "webui/chromeos/bluetooth_pairing_ui.h",
+      "webui/chromeos/certificate_manager_dialog_ui.cc",
+      "webui/chromeos/certificate_manager_dialog_ui.h",
+      "webui/chromeos/proxy_settings_ui.cc",
+      "webui/chromeos/proxy_settings_ui.h",
+      "webui/options/autofill_options_handler.cc",
+      "webui/options/autofill_options_handler.h",
+      "webui/options/automatic_settings_reset_handler.cc",
+      "webui/options/automatic_settings_reset_handler.h",
+      "webui/options/browser_options_handler.cc",
+      "webui/options/browser_options_handler.h",
+      "webui/options/chromeos/accounts_options_handler.cc",
+      "webui/options/chromeos/accounts_options_handler.h",
+      "webui/options/chromeos/bluetooth_options_handler.cc",
+      "webui/options/chromeos/bluetooth_options_handler.h",
+      "webui/options/chromeos/change_picture_options_handler.cc",
+      "webui/options/chromeos/change_picture_options_handler.h",
+      "webui/options/chromeos/core_chromeos_options_handler.cc",
+      "webui/options/chromeos/core_chromeos_options_handler.h",
+      "webui/options/chromeos/cros_language_options_handler.cc",
+      "webui/options/chromeos/cros_language_options_handler.h",
+      "webui/options/chromeos/date_time_options_handler.cc",
+      "webui/options/chromeos/date_time_options_handler.h",
+      "webui/options/chromeos/display_options_handler.cc",
+      "webui/options/chromeos/display_options_handler.h",
+      "webui/options/chromeos/display_overscan_handler.cc",
+      "webui/options/chromeos/display_overscan_handler.h",
+      "webui/options/chromeos/internet_options_handler.cc",
+      "webui/options/chromeos/internet_options_handler.h",
+      "webui/options/chromeos/internet_options_handler_strings.cc",
+      "webui/options/chromeos/internet_options_handler_strings.h",
+      "webui/options/chromeos/keyboard_handler.cc",
+      "webui/options/chromeos/keyboard_handler.h",
+      "webui/options/chromeos/options_stylus_handler.cc",
+      "webui/options/chromeos/options_stylus_handler.h",
+      "webui/options/chromeos/pointer_handler.cc",
+      "webui/options/chromeos/pointer_handler.h",
+      "webui/options/chromeos/power_handler.cc",
+      "webui/options/chromeos/power_handler.h",
+      "webui/options/chromeos/proxy_handler.cc",
+      "webui/options/chromeos/proxy_handler.h",
+      "webui/options/chromeos/stats_options_handler.cc",
+      "webui/options/chromeos/stats_options_handler.h",
+      "webui/options/chromeos/storage_manager_handler.cc",
+      "webui/options/chromeos/storage_manager_handler.h",
+      "webui/options/chromeos/user_image_source.cc",
+      "webui/options/chromeos/user_image_source.h",
+      "webui/options/clear_browser_data_handler.cc",
+      "webui/options/clear_browser_data_handler.h",
+      "webui/options/content_settings_handler.cc",
+      "webui/options/content_settings_handler.h",
+      "webui/options/cookies_view_handler.cc",
+      "webui/options/cookies_view_handler.h",
+      "webui/options/core_options_handler.cc",
+      "webui/options/core_options_handler.h",
+      "webui/options/create_profile_handler.cc",
+      "webui/options/create_profile_handler.h",
+      "webui/options/easy_unlock_handler.cc",
+      "webui/options/easy_unlock_handler.h",
+      "webui/options/font_settings_handler.cc",
+      "webui/options/font_settings_handler.h",
+      "webui/options/handler_options_handler.cc",
+      "webui/options/handler_options_handler.h",
+      "webui/options/help_overlay_handler.cc",
+      "webui/options/help_overlay_handler.h",
+      "webui/options/home_page_overlay_handler.cc",
+      "webui/options/home_page_overlay_handler.h",
+      "webui/options/import_data_handler.cc",
+      "webui/options/import_data_handler.h",
+      "webui/options/language_dictionary_overlay_handler.cc",
+      "webui/options/language_dictionary_overlay_handler.h",
+      "webui/options/language_options_handler.cc",
+      "webui/options/language_options_handler.h",
+      "webui/options/language_options_handler_common.cc",
+      "webui/options/language_options_handler_common.h",
+      "webui/options/manage_profile_handler.cc",
+      "webui/options/manage_profile_handler.h",
+      "webui/options/media_devices_selection_handler.cc",
+      "webui/options/media_devices_selection_handler.h",
+      "webui/options/options_ui.cc",
+      "webui/options/options_ui.h",
+      "webui/options/password_manager_handler.cc",
+      "webui/options/password_manager_handler.h",
+      "webui/options/reset_profile_settings_handler.cc",
+      "webui/options/reset_profile_settings_handler.h",
+      "webui/options/search_engine_manager_handler.cc",
+      "webui/options/search_engine_manager_handler.h",
+      "webui/options/startup_pages_handler.cc",
+      "webui/options/startup_pages_handler.h",
+      "webui/options/supervised_user_create_confirm_handler.cc",
+      "webui/options/supervised_user_create_confirm_handler.h",
+      "webui/options/supervised_user_import_handler.cc",
+      "webui/options/supervised_user_import_handler.h",
+      "webui/options/supervised_user_learn_more_handler.cc",
+      "webui/options/supervised_user_learn_more_handler.h",
+      "webui/options/sync_setup_handler.cc",
+      "webui/options/sync_setup_handler.h",
     ]
     deps += [
       "//chrome/browser/chromeos",
@@ -2180,11 +2177,15 @@
       "crypto_module_delegate_nss.h",
       "crypto_module_password_dialog_nss.cc",
       "crypto_module_password_dialog_nss.h",
-      "webui/options/certificate_manager_handler.cc",
-      "webui/options/certificate_manager_handler.h",
       "webui/settings/certificates_handler.cc",
       "webui/settings/certificates_handler.h",
     ]
+    if (is_chromeos) {
+      sources += [
+        "webui/options/certificate_manager_handler.cc",
+        "webui/options/certificate_manager_handler.h",
+      ]
+    }
   }
   if (is_mac || is_win) {
     sources += [
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 bd91c22..8f2ec681 100644
--- a/chrome/browser/ui/app_list/arc/arc_app_unittest.cc
+++ b/chrome/browser/ui/app_list/arc/arc_app_unittest.cc
@@ -136,6 +136,8 @@
   ARC_PERSISTENT_PLAY_STORE_MANAGED_AND_ENABLED,
   // ARC is persistent and Play Store is managed and disabled.
   ARC_PERSISTENT_PLAY_STORE_MANAGED_AND_DISABLED,
+  // ARC is persistent but without Play Store UI support.
+  ARC_PERSISTENT_WITHOUT_PLAY_STORE,
 };
 
 constexpr ArcState kManagedArcStates[] = {
@@ -148,6 +150,7 @@
 constexpr ArcState kUnmanagedArcStates[] = {
     ArcState::ARC_PLAY_STORE_UNMANAGED,
     ArcState::ARC_PERSISTENT_PLAY_STORE_UNMANAGED,
+    ArcState::ARC_PERSISTENT_WITHOUT_PLAY_STORE,
 };
 
 }  // namespace
@@ -166,7 +169,10 @@
       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();
+        arc::SetArcAlwaysStartForTesting(true);
+        break;
+      case ArcState::ARC_PERSISTENT_WITHOUT_PLAY_STORE:
+        arc::SetArcAlwaysStartForTesting(false);
         break;
       default:
         break;
@@ -553,6 +559,7 @@
         return true;
       case ArcState::ARC_PLAY_STORE_MANAGED_AND_DISABLED:
       case ArcState::ARC_PERSISTENT_PLAY_STORE_MANAGED_AND_DISABLED:
+      case ArcState::ARC_PERSISTENT_WITHOUT_PLAY_STORE:
         return false;
       default:
         NOTREACHED();
@@ -1121,7 +1128,6 @@
 }
 
 TEST_P(ArcPlayStoreAppTest, PlayStore) {
-  // Make sure PlayStore is available.
   ASSERT_TRUE(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
 
   ArcAppListPrefs* prefs = ArcAppListPrefs::Get(profile_.get());
@@ -1129,15 +1135,22 @@
 
   std::unique_ptr<ArcAppListPrefs::AppInfo> app_info = prefs->GetApp(
       arc::kPlayStoreAppId);
-  ASSERT_TRUE(app_info);
-  EXPECT_FALSE(app_info->ready);
+  if (GetParam() != ArcState::ARC_PERSISTENT_WITHOUT_PLAY_STORE) {
+    // Make sure PlayStore is available.
+    ASSERT_TRUE(app_info);
+    EXPECT_FALSE(app_info->ready);
+  } else {
+    // By default Play Store is not available in case no Play Store mode. But
+    // explicitly adding it makes it appear as an ordinal app.
+    EXPECT_FALSE(app_info);
+  }
 
   arc::mojom::AppInfo app;
   std::vector<arc::mojom::AppInfo> apps;
   app.name = "Play Store";
   app.package_name = arc::kPlayStorePackage;
   app.activity = arc::kPlayStoreActivity;
-  app.sticky = false;
+  app.sticky = GetParam() != ArcState::ARC_PERSISTENT_WITHOUT_PLAY_STORE;
   apps.push_back(app);
 
   app_instance()->RefreshAppList();
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 102d2a8..df79f640 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
@@ -12,6 +12,7 @@
 #include "chrome/browser/ui/app_list/arc/arc_app_list_prefs.h"
 #include "chrome/browser/ui/app_list/arc/arc_app_utils.h"
 #include "chrome/common/chrome_paths.h"
+#include "components/arc/arc_util.h"
 #include "content/public/browser/browser_thread.h"
 #include "extensions/browser/extension_system.h"
 
@@ -147,7 +148,7 @@
       extensions::ExtensionSystem::Get(context_)->extension_service();
   const extensions::Extension* arc_host =
       service ? service->GetInstalledExtension(arc::kPlayStoreAppId) : nullptr;
-  if (arc_host) {
+  if (arc_host && arc::IsPlayStoreAvailable()) {
     std::unique_ptr<ArcDefaultAppList::AppInfo> play_store_app(
         new ArcDefaultAppList::AppInfo(arc_host->name(),
                                        arc::kPlayStorePackage,
diff --git a/chrome/browser/ui/ash/chrome_shell_delegate.cc b/chrome/browser/ui/ash/chrome_shell_delegate.cc
index 9aa1fd61..616f3cf 100644
--- a/chrome/browser/ui/ash/chrome_shell_delegate.cc
+++ b/chrome/browser/ui/ash/chrome_shell_delegate.cc
@@ -32,6 +32,7 @@
 #include "chrome/browser/chromeos/background/ash_wallpaper_delegate.h"
 #include "chrome/browser/chromeos/display/display_configuration_observer.h"
 #include "chrome/browser/chromeos/display/display_preferences.h"
+#include "chrome/browser/chromeos/input_method/input_method_manager_impl.h"
 #include "chrome/browser/chromeos/policy/display_rotation_default_handler.h"
 #include "chrome/browser/chromeos/profiles/profile_helper.h"
 #include "chrome/browser/chromeos/system/input_device_settings.h"
@@ -613,6 +614,10 @@
   return chromeos::CreateSystemTrayDelegate();
 }
 
+ash::ImeController* ChromeShellDelegate::GetImeController() {
+  return chromeos::input_method::InputMethodManagerImpl::Get();
+}
+
 std::unique_ptr<ash::WallpaperDelegate>
 ChromeShellDelegate::CreateWallpaperDelegate() {
   return base::WrapUnique(chromeos::CreateWallpaperDelegate());
diff --git a/chrome/browser/ui/ash/chrome_shell_delegate.h b/chrome/browser/ui/ash/chrome_shell_delegate.h
index 1002ad8..34d5717 100644
--- a/chrome/browser/ui/ash/chrome_shell_delegate.h
+++ b/chrome/browser/ui/ash/chrome_shell_delegate.h
@@ -45,6 +45,7 @@
   void ShelfInit() override;
   void ShelfShutdown() override;
   ash::SystemTrayDelegate* CreateSystemTrayDelegate() override;
+  ash::ImeController* GetImeController() override;
   std::unique_ptr<ash::WallpaperDelegate> CreateWallpaperDelegate() override;
   ash::AccessibilityDelegate* CreateAccessibilityDelegate() override;
   std::unique_ptr<ash::PaletteDelegate> CreatePaletteDelegate() override;
diff --git a/chrome/browser/ui/ash/launcher/chrome_launcher_controller_unittest.cc b/chrome/browser/ui/ash/launcher/chrome_launcher_controller_unittest.cc
index e4ae2d3..7d2a16f 100644
--- a/chrome/browser/ui/ash/launcher/chrome_launcher_controller_unittest.cc
+++ b/chrome/browser/ui/ash/launcher/chrome_launcher_controller_unittest.cc
@@ -1079,7 +1079,7 @@
 
   void SetUp() override {
     if (GetParam())
-      arc::SetArcAlwaysStartForTesting();
+      arc::SetArcAlwaysStartForTesting(true);
     ChromeLauncherControllerTest::SetUp();
   }
 
@@ -1334,7 +1334,7 @@
 
   void SetUp() override {
     if (GetParam())
-      arc::SetArcAlwaysStartForTesting();
+      arc::SetArcAlwaysStartForTesting(true);
     MultiProfileMultiBrowserShelfLayoutChromeLauncherControllerTest::SetUp();
   }
 
@@ -3870,7 +3870,7 @@
  protected:
   void SetUp() override {
     if (GetParam())
-      arc::SetArcAlwaysStartForTesting();
+      arc::SetArcAlwaysStartForTesting(true);
     ArcDefaultAppList::UseTestAppsDirectory();
     ChromeLauncherControllerTest::SetUp();
   }
diff --git a/chrome/browser/ui/ash/system_tray_delegate_chromeos.cc b/chrome/browser/ui/ash/system_tray_delegate_chromeos.cc
index 67648c4c..7e207451 100644
--- a/chrome/browser/ui/ash/system_tray_delegate_chromeos.cc
+++ b/chrome/browser/ui/ash/system_tray_delegate_chromeos.cc
@@ -17,7 +17,6 @@
 #include "ash/shell.h"
 #include "ash/shell_delegate.h"
 #include "ash/system/date/clock_observer.h"
-#include "ash/system/ime/ime_observer.h"
 #include "ash/system/power/power_status.h"
 #include "ash/system/session/logout_button_observer.h"
 #include "ash/system/tray/system_tray_notifier.h"
@@ -56,29 +55,14 @@
 #include "components/user_manager/user_type.h"
 #include "content/public/browser/notification_observer.h"
 #include "content/public/browser/notification_service.h"
-#include "ui/base/ime/chromeos/extension_ime_util.h"
-#include "ui/base/ime/chromeos/input_method_manager.h"
-#include "ui/base/ime/chromeos/input_method_util.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/base/l10n/time_format.h"
 #include "ui/chromeos/events/pref_names.h"
-#include "ui/chromeos/ime/input_method_menu_item.h"
-#include "ui/chromeos/ime/input_method_menu_manager.h"
 
 namespace chromeos {
 
 namespace {
 
-void ExtractIMEInfo(const input_method::InputMethodDescriptor& ime,
-                    const input_method::InputMethodUtil& util,
-                    ash::IMEInfo* info) {
-  info->id = ime.id();
-  info->name = util.GetInputMethodLongName(ime);
-  info->medium_name = util.GetInputMethodMediumName(ime);
-  info->short_name = util.GetInputMethodShortName(ime);
-  info->third_party = extension_ime_util::IsExtensionIME(ime.id());
-}
-
 void OnAcceptMultiprofilesIntro(bool no_show_again) {
   PrefService* prefs = ProfileManager::GetActiveUserProfile()->GetPrefs();
   prefs->SetBoolean(prefs::kMultiProfileNeverShowIntro, no_show_again);
@@ -181,54 +165,6 @@
   }
 }
 
-void SystemTrayDelegateChromeOS::GetCurrentIME(ash::IMEInfo* info) {
-  input_method::InputMethodManager* manager =
-      input_method::InputMethodManager::Get();
-  input_method::InputMethodUtil* util = manager->GetInputMethodUtil();
-  input_method::InputMethodDescriptor ime =
-      manager->GetActiveIMEState()->GetCurrentInputMethod();
-  ExtractIMEInfo(ime, *util, info);
-  info->selected = true;
-}
-
-void SystemTrayDelegateChromeOS::GetAvailableIMEList(ash::IMEInfoList* list) {
-  input_method::InputMethodManager* manager =
-      input_method::InputMethodManager::Get();
-  input_method::InputMethodUtil* util = manager->GetInputMethodUtil();
-  std::unique_ptr<input_method::InputMethodDescriptors> ime_descriptors(
-      manager->GetActiveIMEState()->GetActiveInputMethods());
-  std::string current =
-      manager->GetActiveIMEState()->GetCurrentInputMethod().id();
-  for (size_t i = 0; i < ime_descriptors->size(); i++) {
-    input_method::InputMethodDescriptor& ime = ime_descriptors->at(i);
-    ash::IMEInfo info;
-    ExtractIMEInfo(ime, *util, &info);
-    info.selected = ime.id() == current;
-    list->push_back(info);
-  }
-}
-
-void SystemTrayDelegateChromeOS::GetCurrentIMEProperties(
-    ash::IMEPropertyInfoList* list) {
-  ui::ime::InputMethodMenuItemList menu_list =
-      ui::ime::InputMethodMenuManager::GetInstance()->
-      GetCurrentInputMethodMenuItemList();
-  for (size_t i = 0; i < menu_list.size(); ++i) {
-    ash::IMEPropertyInfo property;
-    property.key = menu_list[i].key;
-    property.name = base::UTF8ToUTF16(menu_list[i].label);
-    property.selected = menu_list[i].is_selection_item_checked;
-    list->push_back(property);
-  }
-}
-
-base::string16 SystemTrayDelegateChromeOS::GetIMEManagedMessage() {
-  auto ime_state = input_method::InputMethodManager::Get()->GetActiveIMEState();
-  return ime_state->GetAllowedInputMethods().empty()
-             ? base::string16()
-             : l10n_util::GetStringUTF16(IDS_OPTIONS_CONTROLLED_SETTING_POLICY);
-}
-
 ash::NetworkingConfigDelegate*
 SystemTrayDelegateChromeOS::GetNetworkingConfigDelegate() const {
   return networking_config_delegate_.get();
diff --git a/chrome/browser/ui/ash/system_tray_delegate_chromeos.h b/chrome/browser/ui/ash/system_tray_delegate_chromeos.h
index a7d6a9b..2e267bd 100644
--- a/chrome/browser/ui/ash/system_tray_delegate_chromeos.h
+++ b/chrome/browser/ui/ash/system_tray_delegate_chromeos.h
@@ -12,7 +12,6 @@
 #include <vector>
 
 #include "ash/accessibility_types.h"
-#include "ash/system/tray/ime_info.h"
 #include "ash/system/tray/system_tray_delegate.h"
 #include "base/macros.h"
 #include "base/observer_list.h"
@@ -48,10 +47,6 @@
   // Overridden from ash::SystemTrayDelegate:
   void Initialize() override;
   void ShowUserLogin() override;
-  void GetCurrentIME(ash::IMEInfo* info) override;
-  void GetAvailableIMEList(ash::IMEInfoList* list) override;
-  void GetCurrentIMEProperties(ash::IMEPropertyInfoList* list) override;
-  base::string16 GetIMEManagedMessage() override;
   ash::NetworkingConfigDelegate* GetNetworkingConfigDelegate() const override;
   void ActiveUserWasChanged() override;
   bool IsSearchKeyMappedToCapsLock() override;
diff --git a/chrome/browser/ui/chrome_pages.cc b/chrome/browser/ui/chrome_pages.cc
index 115eb7cf..4b191db 100644
--- a/chrome/browser/ui/chrome_pages.cc
+++ b/chrome/browser/ui/chrome_pages.cc
@@ -29,7 +29,6 @@
 #include "chrome/browser/ui/singleton_tabs.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
 #include "chrome/browser/ui/webui/md_bookmarks/md_bookmarks_ui.h"
-#include "chrome/browser/ui/webui/options/content_settings_handler.h"
 #include "chrome/browser/ui/webui/site_settings_helper.h"
 #include "chrome/common/chrome_switches.h"
 #include "chrome/common/url_constants.h"
diff --git a/chrome/browser/ui/cocoa/omnibox/omnibox_popup_cell.mm b/chrome/browser/ui/cocoa/omnibox/omnibox_popup_cell.mm
index d0f2a2e3..2aef7d8 100644
--- a/chrome/browser/ui/cocoa/omnibox/omnibox_popup_cell.mm
+++ b/chrome/browser/ui/cocoa/omnibox/omnibox_popup_cell.mm
@@ -636,7 +636,8 @@
       cellFrame, NSOffsetRect(cellFrame, origin.x, origin.y));
   renderRect.size.width =
       std::min(NSWidth(renderRect), static_cast<CGFloat>(maxWidth));
-  renderRect.size.height = std::min(NSWidth(renderRect), kDefaultTextHeight);
+  renderRect.size.height =
+      std::min(NSHeight(renderRect), [attributedString size].height);
   if (!NSIsEmptyRect(renderRect)) {
     [attributedString drawWithRect:FlipIfRTL(renderRect, cellFrame)
                            options:NSStringDrawingUsesLineFragmentOrigin |
diff --git a/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc b/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc
index d0520cce..2d56788 100644
--- a/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc
+++ b/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc
@@ -43,7 +43,6 @@
 #include "chrome/browser/ui/webui/net_internals/net_internals_ui.h"
 #include "chrome/browser/ui/webui/ntp_tiles_internals_ui.h"
 #include "chrome/browser/ui/webui/omnibox/omnibox_ui.h"
-#include "chrome/browser/ui/webui/options/options_ui.h"
 #include "chrome/browser/ui/webui/password_manager_internals/password_manager_internals_ui.h"
 #include "chrome/browser/ui/webui/physical_web/physical_web_ui.h"
 #include "chrome/browser/ui/webui/policy_material_design_ui.h"
@@ -147,6 +146,7 @@
 #include "chrome/browser/ui/webui/chromeos/sim_unlock_ui.h"
 #include "chrome/browser/ui/webui/chromeos/slow_trace_ui.h"
 #include "chrome/browser/ui/webui/chromeos/slow_ui.h"
+#include "chrome/browser/ui/webui/options/options_ui.h"
 #include "chrome/browser/ui/webui/voice_search_ui.h"
 #include "components/proximity_auth/webui/proximity_auth_ui.h"
 #include "components/proximity_auth/webui/url_constants.h"
@@ -398,7 +398,7 @@
       extensions::ExtensionSystem::Get(profile)->extension_service()) {
     return &NewWebUI<AppLauncherPageUI>;
   }
-#endif  // !defined(OS_CHROMEOS)
+#endif  // defined(OS_CHROMEOS)
 
   // Bookmarks are part of NTP on Android.
   if (url.host_piece() == chrome::kChromeUIBookmarksHost) {
@@ -427,8 +427,6 @@
       url.host_piece() == chrome::kChromeUIMdSettingsHost) {
     return &NewWebUI<settings::MdSettingsUI>;
   }
-  if (url.host_piece() == chrome::kChromeUISettingsFrameHost)
-    return &NewWebUI<options::OptionsUI>;
   // If the material design extensions page is enabled, it gets its own host.
   // Otherwise, it's handled by the uber settings page.
   if (url.host_piece() == chrome::kChromeUIExtensionsHost &&
@@ -483,6 +481,8 @@
     return &NewWebUI<chromeos::ProxySettingsUI>;
   if (url.host_piece() == chrome::kChromeUISetTimeHost)
     return &NewWebUI<chromeos::SetTimeUI>;
+  if (url.host_piece() == chrome::kChromeUISettingsFrameHost)
+    return &NewWebUI<options::OptionsUI>;
   if (url.host_piece() == chrome::kChromeUISimUnlockHost)
     return &NewWebUI<chromeos::SimUnlockUI>;
   if (url.host_piece() == chrome::kChromeUISlowHost)
diff --git a/chrome/browser/ui/webui/options/font_settings_handler.cc b/chrome/browser/ui/webui/options/font_settings_handler.cc
index f5a045c..7b1ca1f 100644
--- a/chrome/browser/ui/webui/options/font_settings_handler.cc
+++ b/chrome/browser/ui/webui/options/font_settings_handler.cc
@@ -23,7 +23,7 @@
 #include "chrome/browser/extensions/extension_tab_util.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/browser_finder.h"
-#include "chrome/browser/ui/webui/options/font_settings_utils.h"
+#include "chrome/browser/ui/webui/settings_utils.h"
 #include "chrome/common/pref_names.h"
 #include "chrome/grit/generated_resources.h"
 #include "components/prefs/pref_service.h"
@@ -116,7 +116,9 @@
 void FontSettingsHandler::RegisterMessages() {
   // Perform validation for saved fonts.
   PrefService* pref_service = Profile::FromWebUI(web_ui())->GetPrefs();
-  FontSettingsUtilities::ValidateSavedFonts(pref_service);
+#if defined(OS_MACOSX)
+  settings_utils::ValidateSavedFonts(pref_service);
+#endif
 
   // Register for preferences that we need to observe manually.
   standard_font_.Init(prefs::kWebKitStandardFontFamily,
@@ -192,14 +194,14 @@
   }
 
   base::ListValue selected_values;
-  selected_values.AppendString(FontSettingsUtilities::MaybeGetLocalizedFontName(
-      standard_font_.GetValue()));
   selected_values.AppendString(
-      FontSettingsUtilities::MaybeGetLocalizedFontName(serif_font_.GetValue()));
-  selected_values.AppendString(FontSettingsUtilities::MaybeGetLocalizedFontName(
-      sans_serif_font_.GetValue()));
+      settings_utils::MaybeGetLocalizedFontName(standard_font_.GetValue()));
   selected_values.AppendString(
-      FontSettingsUtilities::MaybeGetLocalizedFontName(fixed_font_.GetValue()));
+      settings_utils::MaybeGetLocalizedFontName(serif_font_.GetValue()));
+  selected_values.AppendString(
+      settings_utils::MaybeGetLocalizedFontName(sans_serif_font_.GetValue()));
+  selected_values.AppendString(
+      settings_utils::MaybeGetLocalizedFontName(fixed_font_.GetValue()));
 
   web_ui()->CallJavascriptFunctionUnsafe(
       "FontSettings.setFontsData", *list.get(), selected_values);
@@ -207,7 +209,7 @@
 
 void FontSettingsHandler::SetUpStandardFontSample() {
   base::Value font_value(
-      FontSettingsUtilities::ResolveFontList(standard_font_.GetValue()));
+      settings_utils::ResolveFontList(standard_font_.GetValue()));
   base::Value size_value(default_font_size_.GetValue());
   web_ui()->CallJavascriptFunctionUnsafe("FontSettings.setUpStandardFontSample",
                                          font_value, size_value);
@@ -215,7 +217,7 @@
 
 void FontSettingsHandler::SetUpSerifFontSample() {
   base::Value font_value(
-      FontSettingsUtilities::ResolveFontList(serif_font_.GetValue()));
+      settings_utils::ResolveFontList(serif_font_.GetValue()));
   base::Value size_value(default_font_size_.GetValue());
   web_ui()->CallJavascriptFunctionUnsafe("FontSettings.setUpSerifFontSample",
                                          font_value, size_value);
@@ -223,7 +225,7 @@
 
 void FontSettingsHandler::SetUpSansSerifFontSample() {
   base::Value font_value(
-      FontSettingsUtilities::ResolveFontList(sans_serif_font_.GetValue()));
+      settings_utils::ResolveFontList(sans_serif_font_.GetValue()));
   base::Value size_value(default_font_size_.GetValue());
   web_ui()->CallJavascriptFunctionUnsafe(
       "FontSettings.setUpSansSerifFontSample", font_value, size_value);
@@ -231,7 +233,7 @@
 
 void FontSettingsHandler::SetUpFixedFontSample() {
   base::Value font_value(
-      FontSettingsUtilities::ResolveFontList(fixed_font_.GetValue()));
+      settings_utils::ResolveFontList(fixed_font_.GetValue()));
   base::Value size_value(default_fixed_font_size_.GetValue());
   web_ui()->CallJavascriptFunctionUnsafe("FontSettings.setUpFixedFontSample",
                                          font_value, size_value);
diff --git a/chrome/browser/ui/webui/options/font_settings_utils.cc b/chrome/browser/ui/webui/options/font_settings_utils.cc
deleted file mode 100644
index 516b138..0000000
--- a/chrome/browser/ui/webui/options/font_settings_utils.cc
+++ /dev/null
@@ -1,25 +0,0 @@
-// Copyright (c) 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/ui/webui/options/font_settings_utils.h"
-
-#include "ui/gfx/font_list.h"
-
-namespace options {
-
-std::string FontSettingsUtilities::ResolveFontList(
-    const std::string& font_name_or_list) {
-  if (!font_name_or_list.empty() && font_name_or_list[0] == ',')
-    return gfx::FontList::FirstAvailableOrFirst(font_name_or_list);
-  return font_name_or_list;
-}
-
-#if !defined(OS_WIN)
-std::string FontSettingsUtilities::MaybeGetLocalizedFontName(
-    const std::string& font_name_or_list) {
-  return ResolveFontList(font_name_or_list);
-}
-#endif
-
-}  // namespace options
diff --git a/chrome/browser/ui/webui/options/font_settings_utils.h b/chrome/browser/ui/webui/options/font_settings_utils.h
deleted file mode 100644
index 35801bb4..0000000
--- a/chrome/browser/ui/webui/options/font_settings_utils.h
+++ /dev/null
@@ -1,41 +0,0 @@
-// Copyright (c) 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_WEBUI_OPTIONS_FONT_SETTINGS_UTILS_H_
-#define CHROME_BROWSER_UI_WEBUI_OPTIONS_FONT_SETTINGS_UTILS_H_
-
-#include <string>
-
-#include "base/macros.h"
-
-class PrefService;
-
-namespace options {
-
-// Chrome advanced options utility methods.
-class FontSettingsUtilities {
- public:
-  static void ValidateSavedFonts(PrefService* prefs);
-
-  // When |font_name_or_list| starts with ",", it is a list of font names
-  // separated by "," and this function returns the first available font name.
-  // Otherwise returns |font_name_or_list| as is.
-  // Unlike gfx::FontList, this function picks one font, and character-level
-  // fallback is handled in CSS.
-  static std::string ResolveFontList(const std::string& font_name_or_list);
-
-  // Returns the localized name of a font so that settings can find it within
-  // the list of system fonts. On Windows, the list of system fonts has names
-  // only for the system locale, but the pref value may be in the English name.
-  // For example, "MS Gothic" becomes "MS ゴシック" on localized Windows.
-  static std::string MaybeGetLocalizedFontName(
-      const std::string& font_name_or_list);
-
- private:
-  DISALLOW_IMPLICIT_CONSTRUCTORS(FontSettingsUtilities);
-};
-
-}  // namespace options
-
-#endif  // CHROME_BROWSER_UI_WEBUI_OPTIONS_FONT_SETTINGS_UTILS_H_
diff --git a/chrome/browser/ui/webui/options/font_settings_utils_linux.cc b/chrome/browser/ui/webui/options/font_settings_utils_linux.cc
deleted file mode 100644
index 1576d03..0000000
--- a/chrome/browser/ui/webui/options/font_settings_utils_linux.cc
+++ /dev/null
@@ -1,14 +0,0 @@
-// Copyright (c) 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/webui/options/font_settings_utils.h"
-
-namespace options {
-
-// static
-void FontSettingsUtilities::ValidateSavedFonts(PrefService* prefs) {
-  // Nothing to do for X11.
-}
-
-}  // namespace options
diff --git a/chrome/browser/ui/webui/options/font_settings_utils_mac.mm b/chrome/browser/ui/webui/options/font_settings_utils_mac.mm
deleted file mode 100644
index 85c220f..0000000
--- a/chrome/browser/ui/webui/options/font_settings_utils_mac.mm
+++ /dev/null
@@ -1,42 +0,0 @@
-// Copyright (c) 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/webui/options/font_settings_utils.h"
-
-#import <Cocoa/Cocoa.h>
-
-#include "base/mac/scoped_nsautorelease_pool.h"
-#include "base/strings/sys_string_conversions.h"
-#include "base/values.h"
-#include "chrome/common/pref_names.h"
-#include "components/prefs/pref_service.h"
-
-namespace options {
-
-static void ValidateFontFamily(PrefService* prefs,
-                               const char* family_pref_name) {
-  // The native font settings dialog saved fonts by the font name, rather
-  // than the family name.  This worked for the old dialog since
-  // -[NSFont fontWithName:size] accepted a font or family name, but the
-  // behavior was technically wrong.  Since we really need the family name for
-  // the dom-ui options window, we will fix the saved preference if necessary.
-  NSString *family_name =
-      base::SysUTF8ToNSString(prefs->GetString(family_pref_name));
-  NSFont *font = [NSFont fontWithName:family_name
-                                 size:[NSFont systemFontSize]];
-  if (font &&
-      [[font familyName] caseInsensitiveCompare:family_name] != NSOrderedSame) {
-    std::string new_family_name = base::SysNSStringToUTF8([font familyName]);
-    prefs->SetString(family_pref_name, new_family_name);
-  }
-}
-
-// static
-void FontSettingsUtilities::ValidateSavedFonts(PrefService* prefs) {
-  ValidateFontFamily(prefs, prefs::kWebKitSerifFontFamily);
-  ValidateFontFamily(prefs, prefs::kWebKitSansSerifFontFamily);
-  ValidateFontFamily(prefs, prefs::kWebKitFixedFontFamily);
-}
-
-}  // namespace options
diff --git a/chrome/browser/ui/webui/options/font_settings_utils_win.cc b/chrome/browser/ui/webui/options/font_settings_utils_win.cc
deleted file mode 100644
index aa3dd79..0000000
--- a/chrome/browser/ui/webui/options/font_settings_utils_win.cc
+++ /dev/null
@@ -1,27 +0,0 @@
-// Copyright (c) 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/webui/options/font_settings_utils.h"
-
-#include "ui/gfx/font.h"
-#include "ui/gfx/platform_font_win.h"
-
-namespace options {
-
-// static
-void FontSettingsUtilities::ValidateSavedFonts(PrefService* prefs) {
-  // Nothing to do for Windows.
-}
-
-std::string FontSettingsUtilities::MaybeGetLocalizedFontName(
-    const std::string& font_name_or_list) {
-  std::string font_name = ResolveFontList(font_name_or_list);
-  if (font_name.empty())
-    return font_name;
-  gfx::Font font(font_name, 12);  // dummy font size
-  return static_cast<gfx::PlatformFontWin*>(font.platform_font())
-      ->GetLocalizedFontName();
-}
-
-}  // namespace options
diff --git a/chrome/browser/ui/webui/settings/font_handler.cc b/chrome/browser/ui/webui/settings/font_handler.cc
index 954f7f66..abd118e 100644
--- a/chrome/browser/ui/webui/settings/font_handler.cc
+++ b/chrome/browser/ui/webui/settings/font_handler.cc
@@ -16,7 +16,6 @@
 #include "chrome/browser/extensions/extension_tab_util.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/browser_finder.h"
-#include "chrome/browser/ui/webui/options/font_settings_utils.h"
 #include "chrome/common/pref_names.h"
 #include "components/prefs/pref_service.h"
 #include "content/public/browser/font_list_async.h"
@@ -25,6 +24,10 @@
 #include "extensions/browser/extension_system.h"
 #include "extensions/common/extension_urls.h"
 
+#if defined(OS_MACOSX)
+#include "chrome/browser/ui/webui/settings_utils.h"
+#endif
+
 namespace {
 
 const char kAdvancedFontSettingsExtensionId[] =
@@ -38,8 +41,10 @@
     : extension_registry_observer_(this),
       profile_(Profile::FromWebUI(webui)),
       weak_ptr_factory_(this) {
+#if defined(OS_MACOSX)
   // Perform validation for saved fonts.
-  options::FontSettingsUtilities::ValidateSavedFonts(profile_->GetPrefs());
+  settings_utils::ValidateSavedFonts(profile_->GetPrefs());
+#endif
 }
 
 FontHandler::~FontHandler() {}
diff --git a/chrome/browser/ui/webui/settings/md_settings_ui.cc b/chrome/browser/ui/webui/settings/md_settings_ui.cc
index 6d4d5df..83f48d8e 100644
--- a/chrome/browser/ui/webui/settings/md_settings_ui.cc
+++ b/chrome/browser/ui/webui/settings/md_settings_ui.cc
@@ -204,9 +204,10 @@
       chromeos::quick_unlock::IsPinEnabled(profile->GetPrefs()));
   html_source->AddBoolean("fingerprintUnlockEnabled",
                           chromeos::quick_unlock::IsFingerprintEnabled());
-  html_source->AddBoolean("androidAppsAllowed",
+  html_source->AddBoolean("androidAppsVisible",
                           arc::IsArcAllowedForProfile(profile) &&
-                              !arc::IsArcOptInVerificationDisabled());
+                              !arc::IsArcOptInVerificationDisabled() &&
+                              arc::IsPlayStoreAvailable());
 
   // TODO(mash): Support Chrome power settings in Mash. crbug.com/644348
   bool enable_power_settings =
diff --git a/chrome/browser/ui/webui/settings_utils.cc b/chrome/browser/ui/webui/settings_utils.cc
index 687fe4a..4347b44 100644
--- a/chrome/browser/ui/webui/settings_utils.cc
+++ b/chrome/browser/ui/webui/settings_utils.cc
@@ -8,6 +8,7 @@
 #include "chrome/grit/theme_resources.h"
 #include "components/url_formatter/url_fixer.h"
 #include "ui/base/resource/resource_bundle.h"
+#include "ui/gfx/font_list.h"
 #include "url/gurl.h"
 
 namespace settings_utils {
@@ -26,4 +27,16 @@
       IDR_SETTINGS_FAVICON, scale_factor);
 }
 
+std::string ResolveFontList(const std::string& font_name_or_list) {
+  if (!font_name_or_list.empty() && font_name_or_list[0] == ',')
+    return gfx::FontList::FirstAvailableOrFirst(font_name_or_list);
+  return font_name_or_list;
+}
+
+#if !defined(OS_WIN)
+std::string MaybeGetLocalizedFontName(const std::string& font_name_or_list) {
+  return ResolveFontList(font_name_or_list);
+}
+#endif
+
 }  // namespace settings_utils
diff --git a/chrome/browser/ui/webui/settings_utils.h b/chrome/browser/ui/webui/settings_utils.h
index c32310e..d0f82b4 100644
--- a/chrome/browser/ui/webui/settings_utils.h
+++ b/chrome/browser/ui/webui/settings_utils.h
@@ -11,6 +11,7 @@
 #include "ui/base/resource/scale_factor.h"
 
 class GURL;
+class PrefService;
 
 namespace base {
 class RefCountedMemory;
@@ -20,6 +21,7 @@
 class WebContents;
 }
 
+// Chrome settings utility methods.
 namespace settings_utils {
 
 // Invoke UI for network proxy settings.
@@ -35,6 +37,23 @@
 
 base::RefCountedMemory* GetFaviconResourceBytes(ui::ScaleFactor scale_factor);
 
+#if defined(OS_MACOSX)
+void ValidateSavedFonts(PrefService* prefs);
+#endif
+
+// When |font_name_or_list| starts with ",", it is a list of font names
+// separated by "," and this function returns the first available font name.
+// Otherwise returns |font_name_or_list| as is.
+// Unlike gfx::FontList, this function picks one font, and character-level
+// fallback is handled in CSS.
+std::string ResolveFontList(const std::string& font_name_or_list);
+
+// Returns the localized name of a font so that settings can find it within
+// the list of system fonts. On Windows, the list of system fonts has names
+// only for the system locale, but the pref value may be in the English name.
+// For example, "MS Gothic" becomes "MS ゴシック" on localized Windows.
+std::string MaybeGetLocalizedFontName(const std::string& font_name_or_list);
+
 }  // namespace settings_utils
 
 #endif  // CHROME_BROWSER_UI_WEBUI_SETTINGS_UTILS_H_
diff --git a/chrome/browser/ui/webui/settings_utils_mac.mm b/chrome/browser/ui/webui/settings_utils_mac.mm
index 1f68f49..0611e97 100644
--- a/chrome/browser/ui/webui/settings_utils_mac.mm
+++ b/chrome/browser/ui/webui/settings_utils_mac.mm
@@ -9,6 +9,28 @@
 #include "base/logging.h"
 #include "base/mac/mac_logging.h"
 #include "base/mac/scoped_aedesc.h"
+#include "base/mac/scoped_nsautorelease_pool.h"
+#include "base/strings/sys_string_conversions.h"
+#include "chrome/common/pref_names.h"
+#include "components/prefs/pref_service.h"
+
+namespace {
+void ValidateFontFamily(PrefService* prefs, const char* family_pref_name) {
+  // The native font settings dialog saved fonts by the font name, rather
+  // than the family name.  This worked for the old dialog since
+  // -[NSFont fontWithName:size] accepted a font or family name, but the
+  // behavior was technically wrong.  Since we really need the family name for
+  // the webui settings window, we will fix the saved preference if necessary.
+  NSString* family_name =
+      base::SysUTF8ToNSString(prefs->GetString(family_pref_name));
+  NSFont* font = [NSFont fontWithName:family_name size:[NSFont systemFontSize]];
+  if (font &&
+      [[font familyName] caseInsensitiveCompare:family_name] != NSOrderedSame) {
+    std::string new_family_name = base::SysNSStringToUTF8([font familyName]);
+    prefs->SetString(family_pref_name, new_family_name);
+  }
+}
+}  // namespace
 
 namespace settings_utils {
 
@@ -41,4 +63,10 @@
    launchIdentifier:nil];
 }
 
+void ValidateSavedFonts(PrefService* prefs) {
+  ValidateFontFamily(prefs, prefs::kWebKitSerifFontFamily);
+  ValidateFontFamily(prefs, prefs::kWebKitSansSerifFontFamily);
+  ValidateFontFamily(prefs, prefs::kWebKitFixedFontFamily);
+}
+
 }  // namespace settings_utils
diff --git a/chrome/browser/ui/webui/settings_utils_win.cc b/chrome/browser/ui/webui/settings_utils_win.cc
index 8380d43e..b9ed44f 100644
--- a/chrome/browser/ui/webui/settings_utils_win.cc
+++ b/chrome/browser/ui/webui/settings_utils_win.cc
@@ -19,6 +19,8 @@
 #include "chrome/browser/browser_process.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/web_contents.h"
+#include "ui/gfx/font.h"
+#include "ui/gfx/platform_font_win.h"
 #include "ui/shell_dialogs/base_shell_dialog_win.h"
 #include "ui/views/win/hwnd_util.h"
 
@@ -112,4 +114,13 @@
       base::Bind(&base::DeletePointer<ManageCertificatesDialog>, dialog));
 }
 
+std::string MaybeGetLocalizedFontName(const std::string& font_name_or_list) {
+  std::string font_name = ResolveFontList(font_name_or_list);
+  if (font_name.empty())
+    return font_name;
+  gfx::Font font(font_name, 12);  // dummy font size
+  return static_cast<gfx::PlatformFontWin*>(font.platform_font())
+      ->GetLocalizedFontName();
+}
+
 }  // namespace settings_utils
diff --git a/chrome/browser/ui/webui/uber/uber_ui.cc b/chrome/browser/ui/webui/uber/uber_ui.cc
index aa7a4f95..966231d 100644
--- a/chrome/browser/ui/webui/uber/uber_ui.cc
+++ b/chrome/browser/ui/webui/uber/uber_ui.cc
@@ -11,7 +11,6 @@
 #include "chrome/browser/ui/webui/chrome_web_ui_controller_factory.h"
 #include "chrome/browser/ui/webui/extensions/extensions_ui.h"
 #include "chrome/browser/ui/webui/log_web_ui_url.h"
-#include "chrome/browser/ui/webui/options/options_ui.h"
 #include "chrome/common/chrome_features.h"
 #include "chrome/common/chrome_switches.h"
 #include "chrome/common/extensions/chrome_manifest_url_handlers.h"
@@ -30,6 +29,10 @@
 #include "content/public/browser/web_ui_data_source.h"
 #include "content/public/common/browser_side_navigation_policy.h"
 
+#if defined(OS_CHROMEOS)
+#include "chrome/browser/ui/webui/options/options_ui.h"
+#endif
+
 using content::NavigationController;
 using content::NavigationEntry;
 using content::RenderFrameHost;
@@ -127,8 +130,10 @@
                   chrome::kChromeUIExtensionsHost);
   RegisterSubpage(chrome::kChromeUIHelpFrameURL,
                   chrome::kChromeUIHelpHost);
+#if defined(OS_CHROMEOS)
   RegisterSubpage(chrome::kChromeUISettingsFrameURL,
                   chrome::kChromeUISettingsHost);
+#endif
   RegisterSubpage(chrome::kChromeUIUberFrameURL,
                   chrome::kChromeUIUberHost);
 }
diff --git a/chrome/chrome_paks.gni b/chrome/chrome_paks.gni
index 953d88d..235ee70 100644
--- a/chrome/chrome_paks.gni
+++ b/chrome/chrome_paks.gni
@@ -137,14 +137,12 @@
       # New paks should be added here by default.
       sources += [
         "$root_gen_dir/chrome/component_extension_resources.pak",
-        "$root_gen_dir/chrome/options_resources.pak",
         "$root_gen_dir/chrome/settings_resources.pak",
         "$root_gen_dir/content/browser/devtools/devtools_resources.pak",
         "$root_gen_dir/headless/headless_lib_resources.pak",
       ]
       deps += [
         "//chrome/browser/resources:component_extension_resources",
-        "//chrome/browser/resources:options_resources",
         "//chrome/browser/resources:settings_resources",
         "//content/browser/devtools:devtools_resources",
         "//headless:resources",
@@ -152,10 +150,12 @@
     }
     if (is_chromeos) {
       sources += [
+        "$root_gen_dir/chrome/options_resources.pak",
         "$root_gen_dir/components/chrome_apps/chrome_apps_resources.pak",
         "$root_gen_dir/ui/file_manager/file_manager_resources.pak",
       ]
       deps += [
+        "//chrome/browser/resources:options_resources",
         "//components/chrome_apps:resources",
         "//ui/file_manager:resources",
       ]
diff --git a/chrome/common/url_constants.h b/chrome/common/url_constants.h
index a7e788c2..c6221cf 100644
--- a/chrome/common/url_constants.h
+++ b/chrome/common/url_constants.h
@@ -50,6 +50,7 @@
 extern const char kChromeUIFlagsURL[];
 extern const char kChromeUIFlashURL[];
 extern const char kChromeUIGCMInternalsURL[];
+// TODO(dbeam): remove help-frame.
 extern const char kChromeUIHelpFrameURL[];
 extern const char kChromeUIHelpURL[];
 extern const char kChromeUIHistoryURL[];
@@ -75,6 +76,7 @@
 extern const char kChromeUIMdSettingsURL[];
 extern const char kChromeUISettingsURL[];
 extern const char kChromeUIContentSettingsURL[];
+// TODO(dbeam): remove settings-frame.
 extern const char kChromeUISettingsFrameURL[];
 extern const char kChromeUISigninEmailConfirmationURL[];
 extern const char kChromeUISigninErrorURL[];
@@ -188,6 +190,7 @@
 extern const char kChromeUIFlagsHost[];
 extern const char kChromeUIFlashHost[];
 extern const char kChromeUIGCMInternalsHost[];
+// TODO(dbeam): remove help-frame.
 extern const char kChromeUIHelpFrameHost[];
 extern const char kChromeUIHelpHost[];
 extern const char kChromeUIHangHost[];
diff --git a/chrome/renderer/extensions/automation_internal_custom_bindings.cc b/chrome/renderer/extensions/automation_internal_custom_bindings.cc
index 4121198..d7bb2f65 100644
--- a/chrome/renderer/extensions/automation_internal_custom_bindings.cc
+++ b/chrome/renderer/extensions/automation_internal_custom_bindings.cc
@@ -88,61 +88,21 @@
   return result;
 }
 
-// Compute the bounding box of a node, fixing nodes with empty bounds by
-// unioning the bounds of their children.
-static gfx::RectF ComputeLocalNodeBounds(TreeCache* cache, ui::AXNode* node) {
-  gfx::RectF bounds = node->data().location;
-  if (bounds.width() > 0 && bounds.height() > 0)
-    return bounds;
-
-  // Compute the bounds of each child.
-  for (size_t i = 0; i < node->children().size(); i++) {
-    ui::AXNode* child = node->children()[i];
-    gfx::RectF child_bounds = ComputeLocalNodeBounds(cache, child);
-
-    // Ignore children that don't have valid bounds themselves.
-    if (child_bounds.width() == 0 || child_bounds.height() == 0)
-      continue;
-
-    // For the first valid child, just set the bounds to that child's bounds.
-    if (bounds.width() == 0 || bounds.height() == 0) {
-      bounds = child_bounds;
-      continue;
-    }
-
-    // Union each additional child's bounds.
-    bounds.Union(child_bounds);
-  }
-
-  return bounds;
-}
-
 // Adjust the bounding box of a node from local to global coordinates,
 // walking up the parent hierarchy to offset by frame offsets and
 // scroll offsets.
-static gfx::Rect ComputeGlobalNodeBounds(TreeCache* cache,
-                                         ui::AXNode* node,
-                                         gfx::RectF local_bounds) {
+static gfx::Rect ComputeGlobalNodeBounds(
+    TreeCache* cache,
+    ui::AXNode* node,
+    gfx::RectF local_bounds = gfx::RectF()) {
   gfx::RectF bounds = local_bounds;
+
   while (node) {
-    if (node->data().transform)
-      node->data().transform->TransformRect(&bounds);
+    bounds = cache->tree.RelativeToTreeBounds(node, bounds);
 
-    // Walk up to this node's container. This may cross a tree
-    // boundary, in which case GetParent() modifies |cache|, so we
-    // save the old cache temporarily.
     TreeCache* previous_cache = cache;
-    ui::AXNode* container =
-        cache->tree.GetFromId(node->data().offset_container_id);
-    if (!container) {
-      if (node == cache->tree.root()) {
-        container = cache->owner->GetParent(node, &cache);
-      } else {
-        container = cache->tree.root();
-      }
-    }
-
-    if (!container || container == node)
+    ui::AXNode* parent = cache->owner->GetParent(cache->tree.root(), &cache);
+    if (parent == node)
       break;
 
     // All trees other than the desktop tree are scaled by the device
@@ -155,17 +115,7 @@
         bounds.Scale(1.0 / scale_factor);
     }
 
-    gfx::RectF container_bounds = container->data().location;
-    bounds.Offset(container_bounds.x(), container_bounds.y());
-
-    int scroll_x = 0;
-    int scroll_y = 0;
-    if (container->data().GetIntAttribute(ui::AX_ATTR_SCROLL_X, &scroll_x) &&
-        container->data().GetIntAttribute(ui::AX_ATTR_SCROLL_Y, &scroll_y)) {
-      bounds.Offset(-scroll_x, -scroll_y);
-    }
-
-    node = container;
+    node = parent;
   }
 
   return gfx::ToEnclosingRect(bounds);
@@ -569,9 +519,7 @@
   RouteNodeIDFunction(
       "GetLocation", [](v8::Isolate* isolate, v8::ReturnValue<v8::Value> result,
                         TreeCache* cache, ui::AXNode* node) {
-        gfx::RectF local_bounds = ComputeLocalNodeBounds(cache, node);
-        gfx::Rect global_bounds =
-            ComputeGlobalNodeBounds(cache, node, local_bounds);
+        gfx::Rect global_bounds = ComputeGlobalNodeBounds(cache, node);
         result.Set(RectToV8Object(isolate, global_bounds));
       });
   RouteNodeIDFunction(
@@ -607,14 +555,14 @@
       "GetBoundsForRange",
       [](v8::Isolate* isolate, v8::ReturnValue<v8::Value> result,
          TreeCache* cache, ui::AXNode* node, int start, int end) {
-        gfx::RectF local_bounds = ComputeLocalNodeBounds(cache, node);
         if (node->data().role != ui::AX_ROLE_INLINE_TEXT_BOX) {
-          gfx::Rect global_bounds =
-              ComputeGlobalNodeBounds(cache, node, local_bounds);
+          gfx::Rect global_bounds = ComputeGlobalNodeBounds(cache, node);
           result.Set(RectToV8Object(isolate, global_bounds));
         }
 
         // Use character offsets to compute the local bounds of this subrange.
+        gfx::RectF local_bounds(0, 0, node->data().location.width(),
+                                node->data().location.height());
         std::string name = node->data().GetStringAttribute(ui::AX_ATTR_NAME);
         std::vector<int> character_offsets =
             node->data().GetIntListAttribute(ui::AX_ATTR_CHARACTER_OFFSETS);
diff --git a/chromeos/chromeos_switches.cc b/chromeos/chromeos_switches.cc
index 42245cb4..910a9f4 100644
--- a/chromeos/chromeos_switches.cc
+++ b/chromeos/chromeos_switches.cc
@@ -62,10 +62,6 @@
 // Path for app's OEM manifest file.
 const char kAppOemManifestFile[] = "app-mode-oem-manifest";
 
-// Always starts ARC after login screen without Play Store in almost all cases.
-// Secondary profile is an exception where ARC will not start.
-const char kArcAlwaysStart[] = "arc-always-start";
-
 // Signals ARC support status on this device. This can take one of the
 // following three values.
 // - none: ARC is not installed on this device. (default)
@@ -79,6 +75,14 @@
 // Signals the availability of the ARC instance on this device.
 const char kArcAvailable[] = "arc-available";
 
+// Defines how to start ARC. This can take one of the following values:
+// - always-start automatically start with Play Store UI support.
+// - always-start-with-no-play-store automatically start without Play Store UI.
+// In both cases ARC starts after login screen in almost all cases. Secondary
+// profile is an exception where ARC won't start.
+// If it is not set, then ARC is started in default mode.
+const char kArcStartMode[] = "arc-start-mode";
+
 // Screenshot testing: specifies the directoru where artifacts will be stored.
 const char kArtifactsDir[] = "artifacts-dir";
 
diff --git a/chromeos/chromeos_switches.h b/chromeos/chromeos_switches.h
index de55381e..9000ef0 100644
--- a/chromeos/chromeos_switches.h
+++ b/chromeos/chromeos_switches.h
@@ -28,9 +28,9 @@
 CHROMEOS_EXPORT extern const char kAllowRAInDevMode[];
 CHROMEOS_EXPORT extern const char kAppAutoLaunched[];
 CHROMEOS_EXPORT extern const char kAppOemManifestFile[];
-CHROMEOS_EXPORT extern const char kArcAlwaysStart[];
 CHROMEOS_EXPORT extern const char kArcAvailability[];
 CHROMEOS_EXPORT extern const char kArcAvailable[];
+CHROMEOS_EXPORT extern const char kArcStartMode[];
 CHROMEOS_EXPORT extern const char kArtifactsDir[];
 CHROMEOS_EXPORT extern const char kAshWebUIInit[];
 CHROMEOS_EXPORT extern const char kCellularFirst[];
diff --git a/components/arc/arc_util.cc b/components/arc/arc_util.cc
index 58133783..0942b9b 100644
--- a/components/arc/arc_util.cc
+++ b/components/arc/arc_util.cc
@@ -30,6 +30,9 @@
 constexpr char kAvailabilityNone[] = "none";
 constexpr char kAvailabilityInstalled[] = "installed";
 constexpr char kAvailabilityOfficiallySupported[] = "officially-supported";
+constexpr char kAlwaysStart[] = "always-start";
+constexpr char kAlwaysStartWithNoPlayStore[] =
+    "always-start-with-no-play-store";
 
 void SetArcCpuRestrictionCallback(
     login_manager::ContainerCpuRestrictionState state,
@@ -49,8 +52,8 @@
   const auto* command_line = base::CommandLine::ForCurrentProcess();
 
   if (command_line->HasSwitch(chromeos::switches::kArcAvailability)) {
-    std::string value = command_line->GetSwitchValueASCII(
-        chromeos::switches::kArcAvailability);
+    const std::string value =
+        command_line->GetSwitchValueASCII(chromeos::switches::kArcAvailability);
     DCHECK(value == kAvailabilityNone || value == kAvailabilityInstalled ||
            value == kAvailabilityOfficiallySupported)
         << "Unknown flag value: " << value;
@@ -67,14 +70,29 @@
        base::FeatureList::IsEnabled(kEnableArcFeature));
 }
 
-bool ShouldArcAlwaysStart() {
-  return base::CommandLine::ForCurrentProcess()->HasSwitch(
-      chromeos::switches::kArcAlwaysStart);
+bool IsPlayStoreAvailable() {
+  const auto* command_line = base::CommandLine::ForCurrentProcess();
+  if (!command_line->HasSwitch(chromeos::switches::kArcStartMode))
+    return true;
+
+  const std::string value =
+      command_line->GetSwitchValueASCII(chromeos::switches::kArcStartMode);
+  return value != kAlwaysStartWithNoPlayStore;
 }
 
-void SetArcAlwaysStartForTesting() {
-  base::CommandLine::ForCurrentProcess()->AppendSwitch(
-      chromeos::switches::kArcAlwaysStart);
+bool ShouldArcAlwaysStart() {
+  const auto* command_line = base::CommandLine::ForCurrentProcess();
+  if (!command_line->HasSwitch(chromeos::switches::kArcStartMode))
+    return false;
+  const std::string value =
+      command_line->GetSwitchValueASCII(chromeos::switches::kArcStartMode);
+  return value == kAlwaysStartWithNoPlayStore || value == kAlwaysStart;
+}
+
+void SetArcAlwaysStartForTesting(bool play_store_available) {
+  base::CommandLine::ForCurrentProcess()->AppendSwitchASCII(
+      chromeos::switches::kArcStartMode,
+      play_store_available ? kAlwaysStart : kAlwaysStartWithNoPlayStore);
 }
 
 bool IsArcKioskAvailable() {
diff --git a/components/arc/arc_util.h b/components/arc/arc_util.h
index d653b5ac..ce129eb03 100644
--- a/components/arc/arc_util.h
+++ b/components/arc/arc_util.h
@@ -34,13 +34,18 @@
 // check, so it is ok to access them directly.
 bool IsArcAvailable();
 
+// Returns true if ARC image has Play Store package.
+bool IsPlayStoreAvailable();
+
 // Returns true if ARC should always start within the primary user session
 // (opted in user or not), and other supported mode such as guest and Kiosk
 // mode.
 bool ShouldArcAlwaysStart();
 
 // Enables to always start ARC for testing, by appending the command line flag.
-void SetArcAlwaysStartForTesting();
+// If |bool play_store_available| is not set then flag that disables ARC Play
+// Store UI is added.
+void SetArcAlwaysStartForTesting(bool play_store_available);
 
 // Returns true if ARC is installed and running ARC kiosk apps on the current
 // device is officially supported.
diff --git a/components/arc/arc_util_unittest.cc b/components/arc/arc_util_unittest.cc
index 0a52662..b8775211 100644
--- a/components/arc/arc_util_unittest.cc
+++ b/components/arc/arc_util_unittest.cc
@@ -23,7 +23,7 @@
 namespace arc {
 namespace {
 
-// If an instance is created, based on the value passed to the consturctor,
+// If an instance is created, based on the value passed to the constructor,
 // EnableARC feature is enabled/disabled in the scope.
 class ScopedArcFeature {
  public:
@@ -239,5 +239,21 @@
   EXPECT_FALSE(IsArcAllowedForUser(ephemeral_user));
 }
 
+TEST_F(ArcUtilTest, ArcStartModeDefault) {
+  auto* command_line = base::CommandLine::ForCurrentProcess();
+  command_line->InitFromArgv({"", "--arc-availability=installed"});
+  EXPECT_FALSE(ShouldArcAlwaysStart());
+  EXPECT_TRUE(IsPlayStoreAvailable());
+}
+
+TEST_F(ArcUtilTest, ArcStartModeWithoutPlayStore) {
+  auto* command_line = base::CommandLine::ForCurrentProcess();
+  command_line->InitFromArgv(
+      {"", "--arc-availability=installed",
+       "--arc-start-mode=always-start-with-no-play-store"});
+  EXPECT_TRUE(ShouldArcAlwaysStart());
+  EXPECT_FALSE(IsPlayStoreAvailable());
+}
+
 }  // namespace
 }  // namespace arc
diff --git a/components/content_settings/core/common/content_settings.cc b/components/content_settings/core/common/content_settings.cc
index 4cf2d13..5e8a2b3 100644
--- a/components/content_settings/core/common/content_settings.cc
+++ b/components/content_settings/core/common/content_settings.cc
@@ -57,6 +57,7 @@
     {CONTENT_SETTINGS_TYPE_PERMISSION_AUTOBLOCKER_DATA, 31},
     {CONTENT_SETTINGS_TYPE_SUBRESOURCE_FILTER, 32},
     {CONTENT_SETTINGS_TYPE_SUBRESOURCE_FILTER_DATA, 33},
+    {CONTENT_SETTINGS_TYPE_PASSWORD_PROTECTION, 34},
 };
 
 int ContentSettingTypeToHistogramValue(ContentSettingsType content_setting,
diff --git a/components/guest_view/renderer/guest_view_request.cc b/components/guest_view/renderer/guest_view_request.cc
index ea96bc9..b9be455 100644
--- a/components/guest_view/renderer/guest_view_request.cc
+++ b/components/guest_view/renderer/guest_view_request.cc
@@ -91,14 +91,7 @@
 
   v8::HandleScope handle_scope(isolate());
   blink::WebFrame* frame = guest_proxy_render_view->GetWebView()->MainFrame();
-  // TODO(lazyboy,nasko): The WebLocalFrame branch is not used when running
-  // on top of out-of-process iframes. Remove it once the code is converted.
-  v8::Local<v8::Value> window;
-  if (frame->IsWebLocalFrame()) {
-    window = frame->MainWorldScriptContext()->Global();
-  } else {
-    window = frame->ToWebRemoteFrame()->GlobalProxy();
-  }
+  v8::Local<v8::Value> window = frame->GlobalProxy();
 
   const int argc = 1;
   std::unique_ptr<v8::Local<v8::Value>[]> argv(new v8::Local<v8::Value>[argc]);
diff --git a/components/safe_browsing/password_protection/password_protection_request.cc b/components/safe_browsing/password_protection/password_protection_request.cc
index 909c848..f23e47e 100644
--- a/components/safe_browsing/password_protection/password_protection_request.cc
+++ b/components/safe_browsing/password_protection/password_protection_request.cc
@@ -36,12 +36,14 @@
       password_form_action_(password_form_action),
       password_form_frame_url_(password_form_frame_url),
       saved_domain_(saved_domain),
-      request_type_(type),
+      trigger_type_(type),
       password_field_exists_(password_field_exists),
       password_protection_service_(pps),
       request_timeout_in_ms_(request_timeout_in_ms),
       weakptr_factory_(this) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  DCHECK(trigger_type_ == LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE ||
+         trigger_type_ == LoginReputationClientRequest::PASSWORD_REUSE_EVENT);
 }
 
 PasswordProtectionRequest::~PasswordProtectionRequest() {
@@ -85,7 +87,7 @@
   std::unique_ptr<LoginReputationClientResponse> cached_response =
       base::MakeUnique<LoginReputationClientResponse>();
   auto verdict = password_protection_service_->GetCachedVerdict(
-      main_frame_url_, cached_response.get());
+      main_frame_url_, trigger_type_, cached_response.get());
   if (verdict != LoginReputationClientResponse::VERDICT_TYPE_UNSPECIFIED)
     Finish(PasswordProtectionService::RESPONSE_ALREADY_CACHED,
            std::move(cached_response));
@@ -96,11 +98,11 @@
 void PasswordProtectionRequest::FillRequestProto() {
   request_proto_ = base::MakeUnique<LoginReputationClientRequest>();
   request_proto_->set_page_url(main_frame_url_.spec());
-  request_proto_->set_trigger_type(request_type_);
-  password_protection_service_->FillUserPopulation(request_type_,
+  request_proto_->set_trigger_type(trigger_type_);
+  password_protection_service_->FillUserPopulation(trigger_type_,
                                                    request_proto_.get());
   request_proto_->set_stored_verdict_cnt(
-      password_protection_service_->GetStoredVerdictCount());
+      password_protection_service_->GetStoredVerdictCount(trigger_type_));
   LoginReputationClientRequest::Frame* main_frame =
       request_proto_->add_frames();
   main_frame->set_url(main_frame_url_.spec());
@@ -108,7 +110,7 @@
   password_protection_service_->FillReferrerChain(
       main_frame_url_, -1 /* tab id not available */, main_frame);
 
-  switch (request_type_) {
+  switch (trigger_type_) {
     case LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE: {
       LoginReputationClientRequest::Frame::Form* password_form;
       if (password_form_frame_url_ == main_frame_url_) {
@@ -247,7 +249,7 @@
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
   tracker_.TryCancelAll();
 
-  if (request_type_ == LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE) {
+  if (trigger_type_ == LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE) {
     UMA_HISTOGRAM_ENUMERATION(kPasswordOnFocusRequestOutcomeHistogramName,
                               outcome, PasswordProtectionService::MAX_OUTCOME);
   } else {
@@ -256,7 +258,7 @@
   }
 
   if (outcome == PasswordProtectionService::SUCCEEDED && response) {
-    switch (request_type_) {
+    switch (trigger_type_) {
       case LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE:
         UMA_HISTOGRAM_ENUMERATION(
             "PasswordProtection.Verdict.PasswordFieldOnFocus",
diff --git a/components/safe_browsing/password_protection/password_protection_request.h b/components/safe_browsing/password_protection/password_protection_request.h
index 721739b..756a65d 100644
--- a/components/safe_browsing/password_protection/password_protection_request.h
+++ b/components/safe_browsing/password_protection/password_protection_request.h
@@ -74,8 +74,8 @@
 
   content::WebContents* web_contents() const { return web_contents_; }
 
-  LoginReputationClientRequest::TriggerType request_type() const {
-    return request_type_;
+  LoginReputationClientRequest::TriggerType trigger_type() const {
+    return trigger_type_;
   }
 
  private:
@@ -129,7 +129,7 @@
   const std::string saved_domain_;
 
   // If this request is for unfamiliar login page or for a password reuse event.
-  const LoginReputationClientRequest::TriggerType request_type_;
+  const LoginReputationClientRequest::TriggerType trigger_type_;
 
   // If there is a password field on the page.
   const bool password_field_exists_;
diff --git a/components/safe_browsing/password_protection/password_protection_service.cc b/components/safe_browsing/password_protection/password_protection_service.cc
index c7ec2a9d..230f115 100644
--- a/components/safe_browsing/password_protection/password_protection_service.cc
+++ b/components/safe_browsing/password_protection/password_protection_service.cc
@@ -38,6 +38,7 @@
 const int kRequestTimeoutMs = 10000;
 const char kPasswordProtectionRequestUrl[] =
     "https://sb-ssl.google.com/safebrowsing/clientreport/login";
+const char kPasswordOnFocusCacheKey[] = "password_on_focus_cache_key";
 
 // Helper function to determine if the given origin matches content settings
 // map's patterns.
@@ -87,7 +88,8 @@
     scoped_refptr<net::URLRequestContextGetter> request_context_getter,
     HistoryService* history_service,
     HostContentSettingsMap* host_content_settings_map)
-    : stored_verdict_count_(-1),
+    : stored_verdict_count_password_on_focus_(-1),
+      stored_verdict_count_password_entry_(-1),
       database_manager_(database_manager),
       request_context_getter_(request_context_getter),
       history_service_observer_(this),
@@ -122,24 +124,49 @@
          hostname.find('.') != std::string::npos;
 }
 
+// We cache both types of pings under the same content settings type (
+// CONTENT_SETTINGS_TYPE_PASSWORD_PROTECTION). Since UNFAMILIAR_LOGING_PAGE
+// verdicts are only enabled on extended reporting users, we cache them one
+// layer lower in the content setting DictionaryValue than PASSWORD_REUSE_EVENT
+// verdicts.
+// In other words, to cache a UNFAMILIAR_LOGIN_PAGE verdict we needs two levels
+// of keys: (1) origin, (2) cache expression returned in verdict.
+// To cache a PASSWORD_REUSE_EVENT, three levels of keys are used:
+// (1) origin, (2) 2nd level key is always |kPasswordOnFocusCacheKey|,
+// (3) cache expression.
 LoginReputationClientResponse::VerdictType
 PasswordProtectionService::GetCachedVerdict(
     const GURL& url,
+    TriggerType trigger_type,
     LoginReputationClientResponse* out_response) {
+  DCHECK(trigger_type == LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE ||
+         trigger_type == LoginReputationClientRequest::PASSWORD_REUSE_EVENT);
+
   if (!url.is_valid())
     return LoginReputationClientResponse::VERDICT_TYPE_UNSPECIFIED;
 
-  DCHECK(content_settings_);
-
   GURL hostname = GetHostNameWithHTTPScheme(url);
-  std::unique_ptr<base::DictionaryValue> verdict_dictionary =
+  std::unique_ptr<base::DictionaryValue> cache_dictionary =
       base::DictionaryValue::From(content_settings_->GetWebsiteSetting(
           hostname, GURL(), CONTENT_SETTINGS_TYPE_PASSWORD_PROTECTION,
           std::string(), nullptr));
-  // Return early if there is no verdict cached for this origin.
-  if (!verdict_dictionary.get() || verdict_dictionary->empty())
+
+  if (!cache_dictionary.get() || cache_dictionary->empty())
     return LoginReputationClientResponse::VERDICT_TYPE_UNSPECIFIED;
 
+  base::DictionaryValue* verdict_dictionary = nullptr;
+  if (trigger_type == LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE) {
+    // All UNFAMILIAR_LOGIN_PAGE verdicts (a.k.a password on focus ping)
+    // are cached under |kPasswordOnFocusCacheKey|.
+    if (!cache_dictionary->HasKey(kPasswordOnFocusCacheKey)) {
+      return LoginReputationClientResponse::VERDICT_TYPE_UNSPECIFIED;
+    }
+    DCHECK(cache_dictionary->GetDictionaryWithoutPathExpansion(
+        kPasswordOnFocusCacheKey, &verdict_dictionary));
+  } else {
+    verdict_dictionary = cache_dictionary.get();
+  }
+
   std::vector<std::string> paths;
   GeneratePathVariantsWithoutQuery(url, &paths);
   int max_path_depth = -1;
@@ -148,8 +175,12 @@
   // For all the verdicts of the same origin, we key them by |cache_expression|.
   // Its corresponding value is a DictionaryValue contains its creation time and
   // the serialized verdict proto.
-  for (base::DictionaryValue::Iterator it(*verdict_dictionary.get());
-       !it.IsAtEnd(); it.Advance()) {
+  for (base::DictionaryValue::Iterator it(*verdict_dictionary); !it.IsAtEnd();
+       it.Advance()) {
+    if (trigger_type == LoginReputationClientRequest::PASSWORD_REUSE_EVENT &&
+        it.key() == kPasswordOnFocusCacheKey) {
+      continue;
+    }
     base::DictionaryValue* verdict_entry = nullptr;
     CHECK(verdict_dictionary->GetDictionaryWithoutPathExpansion(
         it.key() /* cache_expression */, &verdict_entry));
@@ -180,39 +211,64 @@
 
 void PasswordProtectionService::CacheVerdict(
     const GURL& url,
+    TriggerType trigger_type,
     LoginReputationClientResponse* verdict,
     const base::Time& receive_time) {
   DCHECK(verdict);
   DCHECK(content_settings_);
+  DCHECK(trigger_type == LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE ||
+         trigger_type == LoginReputationClientRequest::PASSWORD_REUSE_EVENT);
 
   GURL hostname = GetHostNameWithHTTPScheme(url);
-  std::unique_ptr<base::DictionaryValue> verdict_dictionary =
+  int* stored_verdict_count =
+      trigger_type == LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE
+          ? &stored_verdict_count_password_on_focus_
+          : &stored_verdict_count_password_entry_;
+  std::unique_ptr<base::DictionaryValue> cache_dictionary =
       base::DictionaryValue::From(content_settings_->GetWebsiteSetting(
           hostname, GURL(), CONTENT_SETTINGS_TYPE_PASSWORD_PROTECTION,
           std::string(), nullptr));
 
-  if (!verdict_dictionary.get())
-    verdict_dictionary = base::MakeUnique<base::DictionaryValue>();
+  if (!cache_dictionary.get())
+    cache_dictionary = base::MakeUnique<base::DictionaryValue>();
 
   std::unique_ptr<base::DictionaryValue> verdict_entry =
       CreateDictionaryFromVerdict(verdict, receive_time);
 
+  base::DictionaryValue* verdict_dictionary = nullptr;
+  if (trigger_type == LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE) {
+    // All UNFAMILIAR_LOGIN_PAGE verdicts (a.k.a password on focus ping)
+    // are cached under |kPasswordOnFocusCacheKey|.
+    if (!cache_dictionary->HasKey(kPasswordOnFocusCacheKey)) {
+      cache_dictionary->SetDictionaryWithoutPathExpansion(
+          kPasswordOnFocusCacheKey, base::MakeUnique<base::DictionaryValue>());
+    }
+    DCHECK(cache_dictionary->GetDictionaryWithoutPathExpansion(
+        kPasswordOnFocusCacheKey, &verdict_dictionary));
+  } else {
+    verdict_dictionary = cache_dictionary.get();
+  }
+
   // Increases stored verdict count if we haven't seen this cache expression
   // before.
   if (!verdict_dictionary->HasKey(verdict->cache_expression()))
-    stored_verdict_count_ = GetStoredVerdictCount() + 1;
+    *stored_verdict_count = GetStoredVerdictCount(trigger_type) + 1;
   // If same cache_expression is already in this verdict_dictionary, we simply
   // override it.
   verdict_dictionary->SetWithoutPathExpansion(verdict->cache_expression(),
                                               std::move(verdict_entry));
   content_settings_->SetWebsiteSettingDefaultScope(
       hostname, GURL(), CONTENT_SETTINGS_TYPE_PASSWORD_PROTECTION,
-      std::string(), std::move(verdict_dictionary));
+      std::string(), std::move(cache_dictionary));
 }
 
 void PasswordProtectionService::CleanUpExpiredVerdicts() {
   DCHECK(content_settings_);
-  if (GetStoredVerdictCount() <= 0)
+
+  if (GetStoredVerdictCount(
+          LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE) <= 0 &&
+      GetStoredVerdictCount(
+          LoginReputationClientRequest::PASSWORD_REUSE_EVENT) <= 0)
     return;
 
   ContentSettingsForOneType password_protection_settings;
@@ -224,44 +280,29 @@
        password_protection_settings) {
     GURL primary_pattern_url = GURL(source.primary_pattern.ToString());
     // Find all verdicts associated with this origin.
-    std::unique_ptr<base::DictionaryValue> verdict_dictionary =
+    std::unique_ptr<base::DictionaryValue> cache_dictionary =
         base::DictionaryValue::From(content_settings_->GetWebsiteSetting(
             primary_pattern_url, GURL(),
             CONTENT_SETTINGS_TYPE_PASSWORD_PROTECTION, std::string(), nullptr));
-    std::vector<std::string> expired_keys;
-    for (base::DictionaryValue::Iterator it(*verdict_dictionary.get());
-         !it.IsAtEnd(); it.Advance()) {
-      base::DictionaryValue* verdict_entry = nullptr;
-      CHECK(verdict_dictionary->GetDictionaryWithoutPathExpansion(
-          it.key(), &verdict_entry));
-      int verdict_received_time;
-      LoginReputationClientResponse verdict;
-      CHECK(ParseVerdictEntry(verdict_entry, &verdict_received_time, &verdict));
+    bool has_expired_password_on_focus_entry = RemoveExpiredVerdicts(
+        LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE,
+        cache_dictionary.get());
+    bool has_expired_password_reuse_entry = RemoveExpiredVerdicts(
+        LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
+        cache_dictionary.get());
 
-      if (IsCacheExpired(verdict_received_time, verdict.cache_duration_sec())) {
-        // Since DictionaryValue::Iterator cannot be used to modify the
-        // dictionary, we record the keys of expired verdicts in |expired_keys|
-        // and remove them in the next for-loop.
-        expired_keys.push_back(it.key());
-      }
-    }
-
-    for (const std::string& key : expired_keys) {
-      verdict_dictionary->RemoveWithoutPathExpansion(key, nullptr);
-      stored_verdict_count_--;
-    }
-
-    if (verdict_dictionary->size() == 0u) {
+    if (cache_dictionary->size() == 0u) {
       content_settings_->ClearSettingsForOneTypeWithPredicate(
           CONTENT_SETTINGS_TYPE_PASSWORD_PROTECTION, base::Time(),
           base::Bind(&OriginMatchPrimaryPattern, primary_pattern_url));
-    } else if (expired_keys.size() > 0u) {
+    } else if (has_expired_password_on_focus_entry ||
+               has_expired_password_reuse_entry) {
       // Set the website setting of this origin with the updated
       // |verdict_diectionary|.
       content_settings_->SetWebsiteSettingDefaultScope(
           primary_pattern_url, GURL(),
           CONTENT_SETTINGS_TYPE_PASSWORD_PROTECTION, std::string(),
-          std::move(verdict_dictionary));
+          std::move(cache_dictionary));
     }
   }
 }
@@ -272,14 +313,15 @@
     const GURL& password_form_action,
     const GURL& password_form_frame_url,
     const std::string& saved_domain,
-    LoginReputationClientRequest::TriggerType type,
+    TriggerType trigger_type,
     bool password_field_exists) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
   scoped_refptr<PasswordProtectionRequest> request(
       new PasswordProtectionRequest(
           web_contents, main_frame_url, password_form_action,
-          password_form_frame_url, saved_domain, type, password_field_exists,
-          this, GetRequestTimeoutInMS()));
+          password_form_frame_url, saved_domain, trigger_type,
+          password_field_exists, this, GetRequestTimeoutInMS()));
+
   DCHECK(request);
   request->Start();
   requests_.insert(std::move(request));
@@ -332,11 +374,11 @@
 
   if (response) {
     if (!already_cached) {
-      CacheVerdict(request->main_frame_url(), response.get(),
-                   base::Time::Now());
+      CacheVerdict(request->main_frame_url(), request->trigger_type(),
+                   response.get(), base::Time::Now());
     }
 
-    if (request->request_type() ==
+    if (request->trigger_type() ==
             LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE &&
         response->verdict_type() == LoginReputationClientResponse::PHISHING &&
         base::FeatureList::IsEnabled(kPasswordProtectionInterstitial)) {
@@ -379,30 +421,49 @@
   return url.Resolve("?key=" + net::EscapeQueryParamValue(api_key, true));
 }
 
-int PasswordProtectionService::GetStoredVerdictCount() {
+int PasswordProtectionService::GetStoredVerdictCount(TriggerType trigger_type) {
   DCHECK(content_settings_);
+  DCHECK(trigger_type == LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE ||
+         trigger_type == LoginReputationClientRequest::PASSWORD_REUSE_EVENT);
+  int* stored_verdict_count =
+      trigger_type == LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE
+          ? &stored_verdict_count_password_on_focus_
+          : &stored_verdict_count_password_entry_;
   // If we have already computed this, return its value.
-  if (stored_verdict_count_ >= 0)
-    return stored_verdict_count_;
+  if (*stored_verdict_count >= 0)
+    return *stored_verdict_count;
 
   ContentSettingsForOneType password_protection_settings;
   content_settings_->GetSettingsForOneType(
       CONTENT_SETTINGS_TYPE_PASSWORD_PROTECTION, std::string(),
       &password_protection_settings);
-  stored_verdict_count_ = 0;
+  stored_verdict_count_password_on_focus_ = 0;
+  stored_verdict_count_password_entry_ = 0;
   if (password_protection_settings.empty())
     return 0;
 
   for (const ContentSettingPatternSource& source :
        password_protection_settings) {
-    std::unique_ptr<base::DictionaryValue> verdict_dictionary =
+    std::unique_ptr<base::DictionaryValue> cache_dictionary =
         base::DictionaryValue::From(content_settings_->GetWebsiteSetting(
             GURL(source.primary_pattern.ToString()), GURL(),
             CONTENT_SETTINGS_TYPE_PASSWORD_PROTECTION, std::string(), nullptr));
-    if (verdict_dictionary.get() && !verdict_dictionary->empty())
-      stored_verdict_count_ += static_cast<int>(verdict_dictionary->size());
+    if (cache_dictionary.get() && !cache_dictionary->empty()) {
+      stored_verdict_count_password_entry_ +=
+          static_cast<int>(cache_dictionary->size());
+      if (cache_dictionary->HasKey(kPasswordOnFocusCacheKey)) {
+        // Substracts 1 from password_entry count if |kPasswordOnFocusCacheKey|
+        // presents.
+        stored_verdict_count_password_entry_ -= 1;
+        base::DictionaryValue* password_on_focus_dict = nullptr;
+        cache_dictionary->GetDictionaryWithoutPathExpansion(
+            kPasswordOnFocusCacheKey, &password_on_focus_dict);
+        stored_verdict_count_password_on_focus_ +=
+            static_cast<int>(password_on_focus_dict->size());
+      }
+    }
   }
-  return stored_verdict_count_;
+  return *stored_verdict_count;
 }
 
 int PasswordProtectionService::GetRequestTimeoutInMS() {
@@ -410,7 +471,7 @@
 }
 
 void PasswordProtectionService::FillUserPopulation(
-    const LoginReputationClientRequest::TriggerType& request_type,
+    TriggerType trigger_type,
     LoginReputationClientRequest* request_proto) {
   ChromeUserPopulation* user_population = request_proto->mutable_population();
   user_population->set_user_population(
@@ -440,8 +501,10 @@
     bool expired,
     const history::URLRows& deleted_rows,
     const std::set<GURL>& favicon_urls) {
-  if (stored_verdict_count_ <= 0)
+  if (stored_verdict_count_password_on_focus_ <= 0 &&
+      stored_verdict_count_password_entry_ <= 0) {
     return;
+  }
 
   BrowserThread::PostTask(
       BrowserThread::UI, FROM_HERE,
@@ -463,7 +526,8 @@
   if (all_history) {
     content_settings_->ClearSettingsForOneType(
         CONTENT_SETTINGS_TYPE_PASSWORD_PROTECTION);
-    stored_verdict_count_ = 0;
+    stored_verdict_count_password_on_focus_ = 0;
+    stored_verdict_count_password_entry_ = 0;
     return;
   }
 
@@ -474,24 +538,104 @@
   for (const history::URLRow& row : deleted_rows) {
     if (!row.url().SchemeIsHTTPOrHTTPS())
       continue;
+
     GURL url_key = GetHostNameWithHTTPScheme(row.url());
-    std::unique_ptr<base::DictionaryValue> verdict_dictionary =
-        base::DictionaryValue::From(content_settings_->GetWebsiteSetting(
-            url_key, GURL(), CONTENT_SETTINGS_TYPE_PASSWORD_PROTECTION,
-            std::string(), nullptr));
-
-    // Move on if we have no cached verdict for this deleted history row.
-    if (!verdict_dictionary.get() || verdict_dictionary->empty())
-      continue;
-
-    int verdict_count = static_cast<int>(verdict_dictionary->size());
-    stored_verdict_count_ = GetStoredVerdictCount() - verdict_count;
+    stored_verdict_count_password_on_focus_ =
+        GetStoredVerdictCount(
+            LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE) -
+        GetVerdictCountForURL(
+            url_key, LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE);
+    stored_verdict_count_password_entry_ =
+        GetStoredVerdictCount(
+            LoginReputationClientRequest::PASSWORD_REUSE_EVENT) -
+        GetVerdictCountForURL(
+            url_key, LoginReputationClientRequest::PASSWORD_REUSE_EVENT);
     content_settings_->ClearSettingsForOneTypeWithPredicate(
         CONTENT_SETTINGS_TYPE_PASSWORD_PROTECTION, base::Time(),
         base::Bind(&OriginMatchPrimaryPattern, url_key));
   }
 }
 
+int PasswordProtectionService::GetVerdictCountForURL(const GURL& url,
+                                                     TriggerType trigger_type) {
+  DCHECK(trigger_type == LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE ||
+         trigger_type == LoginReputationClientRequest::PASSWORD_REUSE_EVENT);
+  std::unique_ptr<base::DictionaryValue> cache_dictionary =
+      base::DictionaryValue::From(content_settings_->GetWebsiteSetting(
+          url, GURL(), CONTENT_SETTINGS_TYPE_PASSWORD_PROTECTION, std::string(),
+          nullptr));
+  if (!cache_dictionary.get() || cache_dictionary->empty())
+    return 0;
+
+  if (trigger_type == LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE) {
+    base::DictionaryValue* password_on_focus_dict = nullptr;
+    cache_dictionary->GetDictionaryWithoutPathExpansion(
+        kPasswordOnFocusCacheKey, &password_on_focus_dict);
+    return password_on_focus_dict ? password_on_focus_dict->size() : 0;
+  } else {
+    return cache_dictionary->HasKey(kPasswordOnFocusCacheKey)
+               ? cache_dictionary->size() - 1
+               : cache_dictionary->size();
+  }
+}
+
+bool PasswordProtectionService::RemoveExpiredVerdicts(
+    TriggerType trigger_type,
+    base::DictionaryValue* cache_dictionary) {
+  DCHECK(trigger_type == LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE ||
+         trigger_type == LoginReputationClientRequest::PASSWORD_REUSE_EVENT);
+  base::DictionaryValue* verdict_dictionary = nullptr;
+  if (trigger_type == LoginReputationClientRequest::PASSWORD_REUSE_EVENT) {
+    verdict_dictionary = cache_dictionary;
+  } else {
+    if (!cache_dictionary->GetDictionaryWithoutPathExpansion(
+            kPasswordOnFocusCacheKey, &verdict_dictionary)) {
+      return false;
+    }
+  }
+
+  if (!verdict_dictionary || verdict_dictionary->empty())
+    return false;
+
+  std::vector<std::string> expired_keys;
+  for (base::DictionaryValue::Iterator it(*verdict_dictionary); !it.IsAtEnd();
+       it.Advance()) {
+    if (trigger_type == LoginReputationClientRequest::PASSWORD_REUSE_EVENT &&
+        it.key() == std::string(kPasswordOnFocusCacheKey))
+      continue;
+
+    base::DictionaryValue* verdict_entry = nullptr;
+    CHECK(verdict_dictionary->GetDictionaryWithoutPathExpansion(
+        it.key(), &verdict_entry));
+    int verdict_received_time;
+    LoginReputationClientResponse verdict;
+    CHECK(ParseVerdictEntry(verdict_entry, &verdict_received_time, &verdict));
+
+    if (IsCacheExpired(verdict_received_time, verdict.cache_duration_sec())) {
+      // Since DictionaryValue::Iterator cannot be used to modify the
+      // dictionary, we record the keys of expired verdicts in |expired_keys|
+      // and remove them in the next for-loop.
+      expired_keys.push_back(it.key());
+    }
+  }
+
+  for (const std::string& key : expired_keys) {
+    verdict_dictionary->RemoveWithoutPathExpansion(key, nullptr);
+    if (trigger_type == LoginReputationClientRequest::PASSWORD_REUSE_EVENT)
+      stored_verdict_count_password_entry_--;
+    else
+      stored_verdict_count_password_on_focus_--;
+  }
+
+  if (trigger_type == LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE &&
+      verdict_dictionary->size() == 0U) {
+    cache_dictionary->RemoveWithoutPathExpansion(kPasswordOnFocusCacheKey,
+                                                 nullptr);
+  }
+
+  return expired_keys.size() > 0U;
+}
+
 // static
 bool PasswordProtectionService::ParseVerdictEntry(
     base::DictionaryValue* verdict_entry,
diff --git a/components/safe_browsing/password_protection/password_protection_service.h b/components/safe_browsing/password_protection/password_protection_service.h
index 89870aa..10036f4 100644
--- a/components/safe_browsing/password_protection/password_protection_service.h
+++ b/components/safe_browsing/password_protection/password_protection_service.h
@@ -49,6 +49,7 @@
 // HostContentSettingsMap instance.
 class PasswordProtectionService : public history::HistoryServiceObserver {
  public:
+  using TriggerType = LoginReputationClientRequest::TriggerType;
   // The outcome of the request. These values are used for UMA.
   // DO NOT CHANGE THE ORDERING OF THESE VALUES.
   enum RequestOutcome {
@@ -87,11 +88,13 @@
   // any thread.
   LoginReputationClientResponse::VerdictType GetCachedVerdict(
       const GURL& url,
+      TriggerType trigger_type,
       LoginReputationClientResponse* out_response);
 
-  // Stores |verdict| in |settings| based on |url|, |verdict| and
-  // |receive_time|.
+  // Stores |verdict| in |settings| based on its |trigger_type|, |url|,
+  // |verdict| and |receive_time|.
   virtual void CacheVerdict(const GURL& url,
+                            TriggerType trigger_type,
                             LoginReputationClientResponse* verdict,
                             const base::Time& receive_time);
 
@@ -106,7 +109,7 @@
                     const GURL& password_form_action,
                     const GURL& password_form_frame_url,
                     const std::string& saved_domain,
-                    LoginReputationClientRequest::TriggerType type,
+                    TriggerType trigger_type,
                     bool password_field_exists);
 
   virtual void MaybeStartPasswordFieldOnFocusRequest(
@@ -152,9 +155,9 @@
   // the requests.
   void CancelPendingRequests();
 
-  // Gets the total number of verdict (no matter expired or not) we cached for
-  // current active profile.
-  virtual int GetStoredVerdictCount();
+  // Gets the total number of verdicts of the specified |trigger_type| we cached
+  // for this profile. This counts both expired and active verdicts.
+  virtual int GetStoredVerdictCount(TriggerType trigger_type);
 
   scoped_refptr<net::URLRequestContextGetter> request_context_getter() {
     return request_context_getter_;
@@ -173,9 +176,8 @@
       int event_tab_id,  // -1 if tab id is not available.
       LoginReputationClientRequest::Frame* frame) = 0;
 
-  void FillUserPopulation(
-      const LoginReputationClientRequest::TriggerType& request_type,
-      LoginReputationClientRequest* request_proto);
+  void FillUserPopulation(TriggerType trigger_type,
+                          LoginReputationClientRequest* request_proto);
 
   virtual bool IsExtendedReporting() = 0;
 
@@ -223,6 +225,15 @@
   void RemoveContentSettingsOnURLsDeleted(bool all_history,
                                           const history::URLRows& deleted_rows);
 
+  // Helper function called by RemoveContentSettingsOnURLsDeleted(..). It
+  // calculate the number of verdicts of |type| that associate with |url|.
+  int GetVerdictCountForURL(const GURL& url, TriggerType type);
+
+  // Remove verdict of |type| from |cache_dictionary|. Return false if no
+  // verdict removed, true otherwise.
+  bool RemoveExpiredVerdicts(TriggerType type,
+                             base::DictionaryValue* cache_dictionary);
+
   static bool ParseVerdictEntry(base::DictionaryValue* verdict_entry,
                                 int* out_verdict_received_time,
                                 LoginReputationClientResponse* out_verdict);
@@ -245,8 +256,12 @@
 
   static void RecordNoPingingReason(const base::Feature& feature,
                                     RequestOutcome reason);
-  // Number of verdict stored for this profile.
-  int stored_verdict_count_;
+  // Number of verdict stored for this profile for password on focus pings.
+  int stored_verdict_count_password_on_focus_;
+
+  // Number of verdict stored for this profile for protected password entry
+  // pings.
+  int stored_verdict_count_password_entry_;
 
   scoped_refptr<SafeBrowsingDatabaseManager> database_manager_;
 
diff --git a/components/safe_browsing/password_protection/password_protection_service_unittest.cc b/components/safe_browsing/password_protection/password_protection_service_unittest.cc
index d297ef1..1baee7c 100644
--- a/components/safe_browsing/password_protection/password_protection_service_unittest.cc
+++ b/components/safe_browsing/password_protection/password_protection_service_unittest.cc
@@ -201,18 +201,19 @@
   }
 
   void CacheVerdict(const GURL& url,
+                    LoginReputationClientRequest::TriggerType trigger,
                     LoginReputationClientResponse::VerdictType verdict,
                     int cache_duration_sec,
                     const std::string& cache_expression,
                     const base::Time& verdict_received_time) {
     LoginReputationClientResponse response(
         CreateVerdictProto(verdict, cache_duration_sec, cache_expression));
-    password_protection_service_->CacheVerdict(url, &response,
+    password_protection_service_->CacheVerdict(url, trigger, &response,
                                                verdict_received_time);
   }
 
-  size_t GetStoredVerdictCount() {
-    return password_protection_service_->GetStoredVerdictCount();
+  size_t GetStoredVerdictCount(LoginReputationClientRequest::TriggerType type) {
+    return password_protection_service_->GetStoredVerdictCount(type);
   }
 
  protected:
@@ -304,80 +305,173 @@
       GURL("http://evil.com/worse/index.html"), cache_expression_with_slash));
 }
 
-TEST_F(PasswordProtectionServiceTest, TestCachedVerdicts) {
-  ASSERT_EQ(0U, GetStoredVerdictCount());
+TEST_F(PasswordProtectionServiceTest, TestCachePasswordReuseVerdicts) {
+  ASSERT_EQ(0U, GetStoredVerdictCount(
+                    LoginReputationClientRequest::PASSWORD_REUSE_EVENT));
+
   // Assume each verdict has a TTL of 10 minutes.
   // Cache a verdict for http://www.test.com/foo/index.html
   CacheVerdict(GURL("http://www.test.com/foo/index.html"),
+               LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
                LoginReputationClientResponse::SAFE, 10 * 60, "test.com/foo",
                base::Time::Now());
 
-  EXPECT_EQ(1U, GetStoredVerdictCount());
+  EXPECT_EQ(1U, GetStoredVerdictCount(
+                    LoginReputationClientRequest::PASSWORD_REUSE_EVENT));
 
   // Cache another verdict with the some origin and cache_expression should
   // override the cache.
   CacheVerdict(GURL("http://www.test.com/foo/index2.html"),
+               LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
                LoginReputationClientResponse::PHISHING, 10 * 60, "test.com/foo",
                base::Time::Now());
-  EXPECT_EQ(1U, GetStoredVerdictCount());
+  EXPECT_EQ(1U, GetStoredVerdictCount(
+                    LoginReputationClientRequest::PASSWORD_REUSE_EVENT));
   LoginReputationClientResponse out_verdict;
-  EXPECT_EQ(LoginReputationClientResponse::PHISHING,
-            password_protection_service_->GetCachedVerdict(
-                GURL("http://www.test.com/foo/index2.html"), &out_verdict));
+  EXPECT_EQ(
+      LoginReputationClientResponse::PHISHING,
+      password_protection_service_->GetCachedVerdict(
+          GURL("http://www.test.com/foo/index2.html"),
+          LoginReputationClientRequest::PASSWORD_REUSE_EVENT, &out_verdict));
 
   // Cache another verdict with the same origin but different cache_expression
   // will not increase setting count, but will increase the number of verdicts
   // in the given origin.
   CacheVerdict(GURL("http://www.test.com/bar/index2.html"),
+               LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
                LoginReputationClientResponse::SAFE, 10 * 60, "test.com/bar",
                base::Time::Now());
-  EXPECT_EQ(2U, GetStoredVerdictCount());
+  EXPECT_EQ(2U, GetStoredVerdictCount(
+                    LoginReputationClientRequest::PASSWORD_REUSE_EVENT));
+
+  // Now cache a UNFAMILIAR_LOGIN_PAGE verdict, stored verdict count for
+  // PASSWORD_REUSE_EVENT should be the same.
+  CacheVerdict(GURL("http://www.test.com/foobar/index3.html"),
+               LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE,
+               LoginReputationClientResponse::SAFE, 10 * 60, "test.com/foobar",
+               base::Time::Now());
+  EXPECT_EQ(2U, GetStoredVerdictCount(
+                    LoginReputationClientRequest::PASSWORD_REUSE_EVENT));
+  EXPECT_EQ(1U, GetStoredVerdictCount(
+                    LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE));
+}
+
+TEST_F(PasswordProtectionServiceTest, TestCacheUnfamiliarLoginVerdicts) {
+  ASSERT_EQ(0U, GetStoredVerdictCount(
+                    LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE));
+
+  // Assume each verdict has a TTL of 10 minutes.
+  // Cache a verdict for http://www.test.com/foo/index.html
+  CacheVerdict(GURL("http://www.test.com/foo/index.html"),
+               LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE,
+               LoginReputationClientResponse::SAFE, 10 * 60, "test.com/foo",
+               base::Time::Now());
+
+  EXPECT_EQ(1U, GetStoredVerdictCount(
+                    LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE));
+
+  // Cache another verdict with the same origin but different cache_expression
+  // will not increase setting count, but will increase the number of verdicts
+  // in the given origin.
+  CacheVerdict(GURL("http://www.test.com/bar/index2.html"),
+               LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE,
+               LoginReputationClientResponse::SAFE, 10 * 60, "test.com/bar",
+               base::Time::Now());
+  EXPECT_EQ(2U, GetStoredVerdictCount(
+                    LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE));
+
+  // Now cache a PASSWORD_REUSE_EVENT verdict, stored verdict count for
+  // UNFAMILIAR_LOGIN_PAGE should be the same.
+  CacheVerdict(GURL("http://www.test.com/foobar/index3.html"),
+               LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
+               LoginReputationClientResponse::SAFE, 10 * 60, "test.com/foobar",
+               base::Time::Now());
+  EXPECT_EQ(2U, GetStoredVerdictCount(
+                    LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE));
+  EXPECT_EQ(1U, GetStoredVerdictCount(
+                    LoginReputationClientRequest::PASSWORD_REUSE_EVENT));
 }
 
 TEST_F(PasswordProtectionServiceTest, TestGetCachedVerdicts) {
-  ASSERT_EQ(0U, GetStoredVerdictCount());
-  // Prepare 2 verdicts of the same origin with different cache expressions,
-  // one is expired, the other is not.
+  ASSERT_EQ(0U, GetStoredVerdictCount(
+                    LoginReputationClientRequest::PASSWORD_REUSE_EVENT));
+  ASSERT_EQ(0U, GetStoredVerdictCount(
+                    LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE));
+  // Prepare 3 verdicts of the same origin with different cache expressions,
+  // one is expired, one is not, the other is of a different type.
   base::Time now = base::Time::Now();
   CacheVerdict(GURL("http://test.com/login.html"),
+               LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
                LoginReputationClientResponse::SAFE, 10 * 60, "test.com", now);
   CacheVerdict(
       GURL("http://test.com/def/index.jsp"),
+      LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
       LoginReputationClientResponse::PHISHING, 10 * 60, "test.com/def",
       base::Time::FromDoubleT(now.ToDoubleT() -
                               24.0 * 60.0 * 60.0));  // Yesterday, expired.
-  ASSERT_EQ(2U, GetStoredVerdictCount());
+  CacheVerdict(GURL("http://test.com/bar/login.html"),
+               LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE,
+               LoginReputationClientResponse::PHISHING, 10 * 60, "test.com/bar",
+               now);
+
+  ASSERT_EQ(2U, GetStoredVerdictCount(
+                    LoginReputationClientRequest::PASSWORD_REUSE_EVENT));
+  ASSERT_EQ(1U, GetStoredVerdictCount(
+                    LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE));
 
   // Return VERDICT_TYPE_UNSPECIFIED if look up for a URL with unknown origin.
   LoginReputationClientResponse actual_verdict;
-  EXPECT_EQ(LoginReputationClientResponse::VERDICT_TYPE_UNSPECIFIED,
-            password_protection_service_->GetCachedVerdict(
-                GURL("http://www.unknown.com/"), &actual_verdict));
+  EXPECT_EQ(
+      LoginReputationClientResponse::VERDICT_TYPE_UNSPECIFIED,
+      password_protection_service_->GetCachedVerdict(
+          GURL("http://www.unknown.com/"),
+          LoginReputationClientRequest::PASSWORD_REUSE_EVENT, &actual_verdict));
 
   // Return SAFE if look up for a URL that matches "test.com" cache expression.
-  EXPECT_EQ(LoginReputationClientResponse::SAFE,
-            password_protection_service_->GetCachedVerdict(
-                GURL("http://test.com/xyz/foo.jsp"), &actual_verdict));
+  EXPECT_EQ(
+      LoginReputationClientResponse::SAFE,
+      password_protection_service_->GetCachedVerdict(
+          GURL("http://test.com/xyz/foo.jsp"),
+          LoginReputationClientRequest::PASSWORD_REUSE_EVENT, &actual_verdict));
 
   // Return VERDICT_TYPE_UNSPECIFIED if look up for a URL whose variants match
   // test.com/def, but the corresponding verdict is expired.
-  EXPECT_EQ(LoginReputationClientResponse::VERDICT_TYPE_UNSPECIFIED,
-            password_protection_service_->GetCachedVerdict(
-                GURL("http://test.com/def/ghi/index.html"), &actual_verdict));
+  EXPECT_EQ(
+      LoginReputationClientResponse::VERDICT_TYPE_UNSPECIFIED,
+      password_protection_service_->GetCachedVerdict(
+          GURL("http://test.com/def/ghi/index.html"),
+          LoginReputationClientRequest::PASSWORD_REUSE_EVENT, &actual_verdict));
 }
 
 TEST_F(PasswordProtectionServiceTest, TestRemoveCachedVerdictOnURLsDeleted) {
-  ASSERT_EQ(0U, GetStoredVerdictCount());
+  ASSERT_EQ(0U, GetStoredVerdictCount(
+                    LoginReputationClientRequest::PASSWORD_REUSE_EVENT));
+  ASSERT_EQ(0U, GetStoredVerdictCount(
+                    LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE));
   // Prepare 2 verdicts. One is for origin "http://foo.com", and the other is
   // for "http://bar.com".
   base::Time now = base::Time::Now();
   CacheVerdict(GURL("http://foo.com/abc/index.jsp"),
+               LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
                LoginReputationClientResponse::LOW_REPUTATION, 10 * 60,
                "foo.com/abc", now);
   CacheVerdict(GURL("http://bar.com/index.jsp"),
+               LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
                LoginReputationClientResponse::PHISHING, 10 * 60, "bar.com",
                now);
-  ASSERT_EQ(2U, GetStoredVerdictCount());
+  ASSERT_EQ(2U, GetStoredVerdictCount(
+                    LoginReputationClientRequest::PASSWORD_REUSE_EVENT));
+
+  CacheVerdict(GURL("http://foo.com/abc/index.jsp"),
+               LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE,
+               LoginReputationClientResponse::LOW_REPUTATION, 10 * 60,
+               "foo.com/abc", now);
+  CacheVerdict(GURL("http://bar.com/index.jsp"),
+               LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE,
+               LoginReputationClientResponse::PHISHING, 10 * 60, "bar.com",
+               now);
+  ASSERT_EQ(2U, GetStoredVerdictCount(
+                    LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE));
 
   // Delete a bar.com URL. Corresponding content setting keyed on
   // origin "http://bar.com" should be removed,
@@ -390,17 +484,31 @@
 
   password_protection_service_->RemoveContentSettingsOnURLsDeleted(
       false /* all_history */, deleted_urls);
-  EXPECT_EQ(1U, GetStoredVerdictCount());
+  EXPECT_EQ(1U, GetStoredVerdictCount(
+                    LoginReputationClientRequest::PASSWORD_REUSE_EVENT));
+  EXPECT_EQ(1U, GetStoredVerdictCount(
+                    LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE));
+
   LoginReputationClientResponse actual_verdict;
+  EXPECT_EQ(
+      LoginReputationClientResponse::VERDICT_TYPE_UNSPECIFIED,
+      password_protection_service_->GetCachedVerdict(
+          GURL("http://bar.com"),
+          LoginReputationClientRequest::PASSWORD_REUSE_EVENT, &actual_verdict));
   EXPECT_EQ(LoginReputationClientResponse::VERDICT_TYPE_UNSPECIFIED,
             password_protection_service_->GetCachedVerdict(
-                GURL("http://bar.com"), &actual_verdict));
+                GURL("http://bar.com"),
+                LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE,
+                &actual_verdict));
 
   // If delete all history. All password protection content settings should be
   // gone.
   password_protection_service_->RemoveContentSettingsOnURLsDeleted(
       true /* all_history */, history::URLRows());
-  EXPECT_EQ(0U, GetStoredVerdictCount());
+  EXPECT_EQ(0U, GetStoredVerdictCount(
+                    LoginReputationClientRequest::PASSWORD_REUSE_EVENT));
+  EXPECT_EQ(0U, GetStoredVerdictCount(
+                    LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE));
 }
 
 TEST_F(PasswordProtectionServiceTest, VerifyCanGetReputationOfURL) {
@@ -454,8 +562,10 @@
 
 TEST_F(PasswordProtectionServiceTest, TestNoRequestSentIfVerdictAlreadyCached) {
   histograms_.ExpectTotalCount(kPasswordOnFocusRequestOutcomeHistogramName, 0);
-  CacheVerdict(GURL(kTargetUrl), LoginReputationClientResponse::LOW_REPUTATION,
-               600, GURL(kTargetUrl).host(), base::Time::Now());
+  CacheVerdict(GURL(kTargetUrl),
+               LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE,
+               LoginReputationClientResponse::LOW_REPUTATION, 600,
+               GURL(kTargetUrl).host(), base::Time::Now());
   InitializeAndStartPasswordOnFocusRequest(false /* match whitelist */,
                                            10000 /* timeout in ms*/);
   base::RunLoop().RunUntilIdle();
@@ -562,42 +672,87 @@
 }
 
 TEST_F(PasswordProtectionServiceTest, TestCleanUpExpiredVerdict) {
-  ASSERT_EQ(0U, GetStoredVerdictCount());
-  // Prepare 4 verdicts:
+  // Prepare 4 verdicts for PASSWORD_REUSE_EVENT:
   // (1) "foo.com/abc" valid
   // (2) "foo.com/def" expired
   // (3) "bar.com/abc" expired
   // (4) "bar.com/def" expired
   base::Time now = base::Time::Now();
   CacheVerdict(GURL("https://foo.com/abc/index.jsp"),
+               LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
                LoginReputationClientResponse::LOW_REPUTATION, 10 * 60,
                "foo.com/abc", now);
   CacheVerdict(GURL("https://foo.com/def/index.jsp"),
+               LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
                LoginReputationClientResponse::LOW_REPUTATION, 0, "foo.com/def",
                now);
   CacheVerdict(GURL("https://bar.com/abc/index.jsp"),
+               LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
                LoginReputationClientResponse::PHISHING, 0, "bar.com/abc", now);
   CacheVerdict(GURL("https://bar.com/def/index.jsp"),
+               LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
                LoginReputationClientResponse::PHISHING, 0, "bar.com/def", now);
-  ASSERT_EQ(4U, GetStoredVerdictCount());
+  ASSERT_EQ(4U, GetStoredVerdictCount(
+                    LoginReputationClientRequest::PASSWORD_REUSE_EVENT));
+
+  // Prepare 2 verdicts for UNFAMILIAR_LOGIN_PAGE:
+  // (1) "bar.com/def" valid
+  // (2) "bar.com/xyz" expired
+  CacheVerdict(GURL("https://bar.com/def/index.jsp"),
+               LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE,
+               LoginReputationClientResponse::SAFE, 10 * 60, "bar.com/def",
+               now);
+  CacheVerdict(GURL("https://bar.com/xyz/index.jsp"),
+               LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE,
+               LoginReputationClientResponse::PHISHING, 0, "bar.com/xyz", now);
+  ASSERT_EQ(2U, GetStoredVerdictCount(
+                    LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE));
 
   password_protection_service_->CleanUpExpiredVerdicts();
 
-  ASSERT_EQ(1U, GetStoredVerdictCount());
+  ASSERT_EQ(1U, GetStoredVerdictCount(
+                    LoginReputationClientRequest::PASSWORD_REUSE_EVENT));
+  ASSERT_EQ(1U, GetStoredVerdictCount(
+                    LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE));
   LoginReputationClientResponse actual_verdict;
-  // Has cached verdict for foo.com/abc.
-  EXPECT_EQ(LoginReputationClientResponse::LOW_REPUTATION,
+  // Has cached PASSWORD_REUSE_EVENT verdict for foo.com/abc.
+  EXPECT_EQ(
+      LoginReputationClientResponse::LOW_REPUTATION,
+      password_protection_service_->GetCachedVerdict(
+          GURL("https://foo.com/abc/test.jsp"),
+          LoginReputationClientRequest::PASSWORD_REUSE_EVENT, &actual_verdict));
+  // No cached PASSWORD_REUSE_EVENT verdict for foo.com/def.
+  EXPECT_EQ(
+      LoginReputationClientResponse::VERDICT_TYPE_UNSPECIFIED,
+      password_protection_service_->GetCachedVerdict(
+          GURL("https://foo.com/def/index.jsp"),
+          LoginReputationClientRequest::PASSWORD_REUSE_EVENT, &actual_verdict));
+  // No cached PASSWORD_REUSE_EVENT verdict for bar.com/abc.
+  EXPECT_EQ(
+      LoginReputationClientResponse::VERDICT_TYPE_UNSPECIFIED,
+      password_protection_service_->GetCachedVerdict(
+          GURL("https://bar.com/abc/index.jsp"),
+          LoginReputationClientRequest::PASSWORD_REUSE_EVENT, &actual_verdict));
+  // No cached PASSWORD_REUSE_EVENT verdict for bar.com/def.
+  EXPECT_EQ(
+      LoginReputationClientResponse::VERDICT_TYPE_UNSPECIFIED,
+      password_protection_service_->GetCachedVerdict(
+          GURL("https://bar.com/def/index.jsp"),
+          LoginReputationClientRequest::PASSWORD_REUSE_EVENT, &actual_verdict));
+
+  // Has cached UNFAMILIAR_LOGIN_PAGE verdict for bar.com/def.
+  EXPECT_EQ(LoginReputationClientResponse::SAFE,
             password_protection_service_->GetCachedVerdict(
-                GURL("https://foo.com/abc/test.jsp"), &actual_verdict));
-  // No cached verdict for foo.com/def.
+                GURL("https://bar.com/def/index.jsp"),
+                LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE,
+                &actual_verdict));
+
+  // No cached UNFAMILIAR_LOGIN_PAGE verdict for bar.com/xyz.
   EXPECT_EQ(LoginReputationClientResponse::VERDICT_TYPE_UNSPECIFIED,
             password_protection_service_->GetCachedVerdict(
-                GURL("https://foo.com/def/index.jsp"), &actual_verdict));
-  // Nothing in content setting for bar.com.
-  EXPECT_EQ(nullptr, content_setting_map_->GetWebsiteSetting(
-                         GURL("https://bar.com"), GURL(),
-                         CONTENT_SETTINGS_TYPE_PASSWORD_PROTECTION,
-                         std::string(), nullptr));
+                GURL("https://bar.com/xyz/index.jsp"),
+                LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE,
+                &actual_verdict));
 }
 
 TEST_F(PasswordProtectionServiceTest, VerifyPasswordOnFocusRequestProto) {
diff --git a/content/browser/accessibility/browser_accessibility.cc b/content/browser/accessibility/browser_accessibility.cc
index 3fb2659..3e24280 100644
--- a/content/browser/accessibility/browser_accessibility.cc
+++ b/content/browser/accessibility/browser_accessibility.cc
@@ -355,15 +355,11 @@
 }
 
 gfx::Rect BrowserAccessibility::GetFrameBoundsRect() const {
-  gfx::RectF bounds = GetLocation();
-  FixEmptyBounds(&bounds);
-  return RelativeToAbsoluteBounds(bounds, true);
+  return RelativeToAbsoluteBounds(gfx::RectF(), true);
 }
 
 gfx::Rect BrowserAccessibility::GetPageBoundsRect() const {
-  gfx::RectF bounds = GetLocation();
-  FixEmptyBounds(&bounds);
-  return RelativeToAbsoluteBounds(bounds, false);
+  return RelativeToAbsoluteBounds(gfx::RectF(), false);
 }
 
 gfx::Rect BrowserAccessibility::GetPageBoundsForRange(int start, int len)
@@ -1044,66 +1040,27 @@
   return text;
 }
 
-void BrowserAccessibility::FixEmptyBounds(gfx::RectF* bounds) const {
-  if (bounds->width() > 0 && bounds->height() > 0)
-    return;
-
-  for (size_t i = 0; i < InternalChildCount(); ++i) {
-    // Compute the bounds of each child - this calls FixEmptyBounds
-    // recursively if necessary.
-    BrowserAccessibility* child = InternalGetChild(i);
-    gfx::Rect child_bounds = child->GetPageBoundsRect();
-
-    // Ignore children that don't have valid bounds themselves.
-    if (child_bounds.width() == 0 || child_bounds.height() == 0)
-      continue;
-
-    // For the first valid child, just set the bounds to that child's bounds.
-    if (bounds->width() == 0 || bounds->height() == 0) {
-      *bounds = gfx::RectF(child_bounds);
-      continue;
-    }
-
-    // Union each additional child's bounds.
-    bounds->Union(gfx::RectF(child_bounds));
-  }
-}
-
 gfx::Rect BrowserAccessibility::RelativeToAbsoluteBounds(
     gfx::RectF bounds,
     bool frame_only) const {
   const BrowserAccessibility* node = this;
   while (node) {
-    if (node->GetData().transform)
-      node->GetData().transform->TransformRect(&bounds);
+    bounds =
+        node->manager()->ax_tree()->RelativeToTreeBounds(node->node(), bounds);
 
-    const BrowserAccessibility* container =
-        node->manager()->GetFromID(node->GetData().offset_container_id);
-    if (!container) {
-      if (node == node->manager()->GetRoot() && !frame_only) {
-        container = node->PlatformGetParent();
-      } else {
-        container = node->manager()->GetRoot();
-      }
-    }
-
-    if (!container || container == node)
-      break;
-
-    gfx::RectF container_bounds = container->GetLocation();
-    bounds.Offset(container_bounds.x(), container_bounds.y());
-
-    if (container->manager()->UseRootScrollOffsetsWhenComputingBounds() ||
-        container->PlatformGetParent()) {
+    // On some platforms we need to unapply root scroll offsets.
+    const BrowserAccessibility* root = node->manager()->GetRoot();
+    if (!node->manager()->UseRootScrollOffsetsWhenComputingBounds() &&
+        !root->PlatformGetParent()) {
       int sx = 0;
       int sy = 0;
-      if (container->GetIntAttribute(ui::AX_ATTR_SCROLL_X, &sx) &&
-          container->GetIntAttribute(ui::AX_ATTR_SCROLL_Y, &sy)) {
-        bounds.Offset(-sx, -sy);
+      if (root->GetIntAttribute(ui::AX_ATTR_SCROLL_X, &sx) &&
+          root->GetIntAttribute(ui::AX_ATTR_SCROLL_Y, &sy)) {
+        bounds.Offset(sx, sy);
       }
     }
 
-    node = container;
+    node = root->PlatformGetParent();
   }
 
   return gfx::ToEnclosingRect(bounds);
diff --git a/content/browser/accessibility/browser_accessibility.h b/content/browser/accessibility/browser_accessibility.h
index b4a6f2c..5412aba 100644
--- a/content/browser/accessibility/browser_accessibility.h
+++ b/content/browser/accessibility/browser_accessibility.h
@@ -422,12 +422,6 @@
   // text, depending on the platform.
   base::string16 GetInnerText() const;
 
-  // If a bounding rectangle is empty, compute it based on the union of its
-  // children, since most accessibility APIs don't like elements with no
-  // bounds, but "virtual" elements in the accessibility tree that don't
-  // correspond to a layed-out element sometimes don't have bounds.
-  void FixEmptyBounds(gfx::RectF* bounds) const;
-
   DISALLOW_COPY_AND_ASSIGN(BrowserAccessibility);
 };
 
diff --git a/content/browser/accessibility/browser_accessibility_manager.h b/content/browser/accessibility/browser_accessibility_manager.h
index cc4910e1..3f1d7b5 100644
--- a/content/browser/accessibility/browser_accessibility_manager.h
+++ b/content/browser/accessibility/browser_accessibility_manager.h
@@ -338,6 +338,7 @@
   // Accessors.
   ui::AXTreeIDRegistry::AXTreeID ax_tree_id() const { return ax_tree_id_; }
   float device_scale_factor() const { return device_scale_factor_; }
+  const ui::AXTree* ax_tree() const { return tree_.get(); }
 
   // AXTreeDelegate implementation.
   void OnNodeDataWillChange(ui::AXTree* tree,
diff --git a/content/browser/appcache/appcache_job.cc b/content/browser/appcache/appcache_job.cc
index 271553f..5bd29cc 100644
--- a/content/browser/appcache/appcache_job.cc
+++ b/content/browser/appcache/appcache_job.cc
@@ -23,7 +23,8 @@
   std::unique_ptr<AppCacheJob> job;
   if (base::CommandLine::ForCurrentProcess()->HasSwitch(
           switches::kEnableNetworkService)) {
-    job.reset(new AppCacheURLLoaderJob);
+    job.reset(
+        new AppCacheURLLoaderJob(*(request->GetResourceRequest()), storage));
   } else {
     job.reset(new AppCacheURLRequestJob(request->GetURLRequest(),
                                         network_delegate, storage, host,
@@ -36,6 +37,26 @@
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 }
 
+bool AppCacheJob::IsWaiting() const {
+  return delivery_type_ == AWAITING_DELIVERY_ORDERS;
+}
+
+bool AppCacheJob::IsDeliveringAppCacheResponse() const {
+  return delivery_type_ == APPCACHED_DELIVERY;
+}
+
+bool AppCacheJob::IsDeliveringNetworkResponse() const {
+  return delivery_type_ == NETWORK_DELIVERY;
+}
+
+bool AppCacheJob::IsDeliveringErrorResponse() const {
+  return delivery_type_ == ERROR_DELIVERY;
+}
+
+bool AppCacheJob::IsCacheEntryNotFound() const {
+  return cache_entry_not_found_;
+}
+
 base::WeakPtr<AppCacheJob> AppCacheJob::GetWeakPtr() {
   return weak_factory_.GetWeakPtr();
 }
@@ -48,6 +69,9 @@
   return nullptr;
 }
 
-AppCacheJob::AppCacheJob() : weak_factory_(this) {}
+AppCacheJob::AppCacheJob()
+    : cache_entry_not_found_(false),
+      delivery_type_(AWAITING_DELIVERY_ORDERS),
+      weak_factory_(this) {}
 
 }  // namespace content
diff --git a/content/browser/appcache/appcache_job.h b/content/browser/appcache/appcache_job.h
index e43bb121..c18c772 100644
--- a/content/browser/appcache/appcache_job.h
+++ b/content/browser/appcache/appcache_job.h
@@ -35,6 +35,13 @@
 // of the AppCache code.
 class CONTENT_EXPORT AppCacheJob : public base::SupportsWeakPtr<AppCacheJob> {
  public:
+  enum DeliveryType {
+    AWAITING_DELIVERY_ORDERS,
+    APPCACHED_DELIVERY,
+    NETWORK_DELIVERY,
+    ERROR_DELIVERY
+  };
+
   // Callback that will be invoked before the request is restarted. The caller
   // can use this opportunity to grab state from the job to determine how it
   // should behave when the request is restarted.
@@ -63,19 +70,19 @@
   virtual bool IsStarted() const = 0;
 
   // Returns true if the job is waiting for instructions.
-  virtual bool IsWaiting() const = 0;
+  virtual bool IsWaiting() const;
 
   // Returns true if the job is delivering a response from the cache.
-  virtual bool IsDeliveringAppCacheResponse() const = 0;
+  virtual bool IsDeliveringAppCacheResponse() const;
 
   // Returns true if the job is delivering a response from the network.
-  virtual bool IsDeliveringNetworkResponse() const = 0;
+  virtual bool IsDeliveringNetworkResponse() const;
 
   // Returns true if the job is delivering an error response.
-  virtual bool IsDeliveringErrorResponse() const = 0;
+  virtual bool IsDeliveringErrorResponse() const;
 
   // Returns true if the cache entry was not found in the cache.
-  virtual bool IsCacheEntryNotFound() const = 0;
+  virtual bool IsCacheEntryNotFound() const;
 
   // Informs the job of what response it should deliver. Only one of these
   // methods should be called, and only once per job. A job will sit idle and
@@ -111,6 +118,12 @@
 
   SEQUENCE_CHECKER(sequence_checker_);
 
+  // Set to true if the AppCache entry is not found.
+  bool cache_entry_not_found_;
+
+  // The jobs delivery status.
+  DeliveryType delivery_type_;
+
   base::WeakPtrFactory<AppCacheJob> weak_factory_;
 
   DISALLOW_COPY_AND_ASSIGN(AppCacheJob);
diff --git a/content/browser/appcache/appcache_request_handler.cc b/content/browser/appcache/appcache_request_handler.cc
index 870fa7d..1e2fe1b 100644
--- a/content/browser/appcache/appcache_request_handler.cc
+++ b/content/browser/appcache/appcache_request_handler.cc
@@ -9,8 +9,13 @@
 #include "base/bind.h"
 #include "content/browser/appcache/appcache.h"
 #include "content/browser/appcache/appcache_backend_impl.h"
+#include "content/browser/appcache/appcache_host.h"
+#include "content/browser/appcache/appcache_navigation_handle_core.h"
 #include "content/browser/appcache/appcache_policy.h"
 #include "content/browser/appcache/appcache_request.h"
+#include "content/browser/appcache/appcache_url_loader_factory.h"
+#include "content/browser/appcache/appcache_url_loader_job.h"
+#include "content/browser/appcache/appcache_url_loader_request.h"
 #include "content/browser/appcache/appcache_url_request_job.h"
 #include "content/browser/service_worker/service_worker_request_handler.h"
 #include "net/url_request/url_request.h"
@@ -223,6 +228,18 @@
   CompleteCrossSiteTransfer(old_process_id_, old_host_id_);
 }
 
+// static
+std::unique_ptr<AppCacheRequestHandler>
+AppCacheRequestHandler::InitializeForNavigationNetworkService(
+    const ResourceRequest& request,
+    AppCacheNavigationHandleCore* appcache_handle_core) {
+  std::unique_ptr<AppCacheRequestHandler> handler =
+      appcache_handle_core->host()->CreateRequestHandler(
+          AppCacheURLLoaderRequest::Create(request), request.resource_type,
+          request.should_reset_appcache);
+  return handler;
+}
+
 void AppCacheRequestHandler::OnDestructionImminent(AppCacheHost* host) {
   storage()->CancelDelegateCallbacks(this);
   host_ = NULL;  // no need to RemoveObserver, the host is being deleted
@@ -316,12 +333,22 @@
   // If a page falls into the scope of a ServiceWorker, any matching AppCaches
   // should be ignored. This depends on the ServiceWorker handler being invoked
   // prior to the AppCache handler.
-  if (ServiceWorkerRequestHandler::IsControlledByServiceWorker(
+  // TODO(ananta)
+  // We need to handle this for AppCache requests initiated for the network
+  // service
+  if (request_->GetURLRequest() &&
+      ServiceWorkerRequestHandler::IsControlledByServiceWorker(
           request_->GetURLRequest())) {
     host_->enable_cache_selection(false);
     return nullptr;
   }
 
+  if (storage()->IsInitialized() &&
+      service_->storage()->usage_map()->find(request_->GetURL().GetOrigin()) ==
+          service_->storage()->usage_map()->end()) {
+    return nullptr;
+  }
+
   host_->enable_cache_selection(true);
 
   const AppCacheHost* spawning_host =
@@ -503,4 +530,19 @@
   ContinueMaybeLoadSubResource();
 }
 
+void AppCacheRequestHandler::MaybeCreateLoader(
+    const ResourceRequest& resource_request,
+    ResourceContext* resource_context,
+    LoaderCallback callback) {
+  // MaybeLoadMainResource will invoke navigation_request_job's methods
+  // asynchronously via AppCacheStorage::Delegate.
+  navigation_request_job_ = MaybeLoadMainResource(nullptr);
+  if (!navigation_request_job_.get()) {
+    std::move(callback).Run(StartLoaderCallback());
+    return;
+  }
+  navigation_request_job_->AsURLLoaderJob()->set_loader_callback(
+      std::move(callback));
+}
+
 }  // namespace content
diff --git a/content/browser/appcache/appcache_request_handler.h b/content/browser/appcache/appcache_request_handler.h
index 3b7e167..b0ed05f 100644
--- a/content/browser/appcache/appcache_request_handler.h
+++ b/content/browser/appcache/appcache_request_handler.h
@@ -16,6 +16,7 @@
 #include "content/browser/appcache/appcache_entry.h"
 #include "content/browser/appcache/appcache_host.h"
 #include "content/browser/appcache/appcache_service_impl.h"
+#include "content/browser/loader/url_loader_request_handler.h"
 #include "content/common/content_export.h"
 #include "content/public/common/resource_type.h"
 
@@ -26,9 +27,11 @@
 
 namespace content {
 class AppCacheJob;
+class AppCacheNavigationHandleCore;
 class AppCacheRequest;
 class AppCacheRequestHandlerTest;
 class AppCacheURLRequestJob;
+struct ResourceRequest;
 
 // An instance is created for each net::URLRequest. The instance survives all
 // http transactions involved in the processing of its net::URLRequest, and is
@@ -39,7 +42,8 @@
     : public base::SupportsUserData::Data,
       public AppCacheHost::Observer,
       public AppCacheServiceImpl::Observer,
-      public AppCacheStorage::Delegate {
+      public AppCacheStorage::Delegate,
+      public URLLoaderRequestHandler {
  public:
   ~AppCacheRequestHandler() override;
 
@@ -69,6 +73,11 @@
            type == RESOURCE_TYPE_SHARED_WORKER;
   }
 
+  static std::unique_ptr<AppCacheRequestHandler>
+  InitializeForNavigationNetworkService(
+      const ResourceRequest& request,
+      AppCacheNavigationHandleCore* appcache_handle_core);
+
  private:
   friend class AppCacheHost;
 
@@ -135,6 +144,11 @@
   // AppCacheHost::Observer override
   void OnCacheSelectionComplete(AppCacheHost* host) override;
 
+  // URLLoaderRequestHandler override
+  void MaybeCreateLoader(const ResourceRequest& resource_request,
+                         ResourceContext* resource_context,
+                         LoaderCallback callback) override;
+
   // Data members -----------------------------------------------
 
   // What host we're servicing a request for.
@@ -195,6 +209,13 @@
 
   std::unique_ptr<AppCacheRequest> request_;
 
+  // In the network service world we are queried via the URLLoaderRequestHandler
+  // interface to see if the navigation request can be handled via the
+  // AppCache. We hold onto the AppCache job created here until the client
+  // binds to it (Serviced via AppCache). If the request cannot be handled via
+  // the AppCache, we delete the job.
+  std::unique_ptr<AppCacheJob> navigation_request_job_;
+
   friend class content::AppCacheRequestHandlerTest;
   DISALLOW_COPY_AND_ASSIGN(AppCacheRequestHandler);
 };
diff --git a/content/browser/appcache/appcache_storage.cc b/content/browser/appcache/appcache_storage.cc
index 0299ddc1..666acd56 100644
--- a/content/browser/appcache/appcache_storage.cc
+++ b/content/browser/appcache/appcache_storage.cc
@@ -18,9 +18,11 @@
 const int64_t AppCacheStorage::kUnitializedId = -1;
 
 AppCacheStorage::AppCacheStorage(AppCacheServiceImpl* service)
-    : last_cache_id_(kUnitializedId), last_group_id_(kUnitializedId),
-      last_response_id_(kUnitializedId), service_(service)  {
-}
+    : last_cache_id_(kUnitializedId),
+      last_group_id_(kUnitializedId),
+      last_response_id_(kUnitializedId),
+      service_(service),
+      weak_factory_(this) {}
 
 AppCacheStorage::~AppCacheStorage() {
   DCHECK(delegate_references_.empty());
@@ -94,6 +96,10 @@
   info_load->StartIfNeeded();
 }
 
+base::WeakPtr<AppCacheStorage> AppCacheStorage::GetWeakPtr() {
+  return weak_factory_.GetWeakPtr();
+}
+
 void AppCacheStorage::UpdateUsageMapAndNotify(const GURL& origin,
                                               int64_t new_usage) {
   DCHECK_GE(new_usage, 0);
diff --git a/content/browser/appcache/appcache_storage.h b/content/browser/appcache/appcache_storage.h
index d01de851..10a3d4c 100644
--- a/content/browser/appcache/appcache_storage.h
+++ b/content/browser/appcache/appcache_storage.h
@@ -15,6 +15,7 @@
 #include "base/gtest_prod_util.h"
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
+#include "base/memory/weak_ptr.h"
 #include "content/browser/appcache/appcache_working_set.h"
 #include "content/common/content_export.h"
 #include "net/base/completion_callback.h"
@@ -194,6 +195,9 @@
   virtual void DeleteResponses(const GURL& manifest_url,
                                const std::vector<int64_t>& response_ids) = 0;
 
+  // Returns true if the AppCacheStorage instance is initialized.
+  virtual bool IsInitialized() = 0;
+
   // Generates unique storage ids for different object types.
   int64_t NewCacheId() { return ++last_cache_id_; }
   int64_t NewGroupId() { return ++last_group_id_; }
@@ -207,6 +211,9 @@
   // Simple ptr back to the service object that owns us.
   AppCacheServiceImpl* service() { return service_; }
 
+  // Returns a weak pointer reference to the AppCacheStorage instance.
+  base::WeakPtr<AppCacheStorage> GetWeakPtr();
+
  protected:
   friend class content::AppCacheQuotaClientTest;
   friend class content::AppCacheResponseTest;
@@ -326,6 +333,10 @@
   FRIEND_TEST_ALL_PREFIXES(content::AppCacheStorageTest, DelegateReferences);
   FRIEND_TEST_ALL_PREFIXES(content::AppCacheStorageTest, UsageMap);
 
+  // The WeakPtrFactory below must occur last in the class definition so they
+  // get destroyed last.
+  base::WeakPtrFactory<AppCacheStorage> weak_factory_;
+
   DISALLOW_COPY_AND_ASSIGN(AppCacheStorage);
 };
 
diff --git a/content/browser/appcache/appcache_storage_impl.cc b/content/browser/appcache/appcache_storage_impl.cc
index 2159d159..0307853 100644
--- a/content/browser/appcache/appcache_storage_impl.cc
+++ b/content/browser/appcache/appcache_storage_impl.cc
@@ -1779,6 +1779,10 @@
   StartDeletingResponses(response_ids);
 }
 
+bool AppCacheStorageImpl::IsInitialized() {
+  return IsInitTaskComplete();
+}
+
 void AppCacheStorageImpl::DelayedStartDeletingUnusedResponses() {
   // Only if we haven't already begun.
   if (!did_start_deleting_responses_) {
diff --git a/content/browser/appcache/appcache_storage_impl.h b/content/browser/appcache/appcache_storage_impl.h
index 82a6f5a2..067b3e6 100644
--- a/content/browser/appcache/appcache_storage_impl.h
+++ b/content/browser/appcache/appcache_storage_impl.h
@@ -73,6 +73,7 @@
                      const std::vector<int64_t>& response_ids) override;
   void DeleteResponses(const GURL& manifest_url,
                        const std::vector<int64_t>& response_ids) override;
+  bool IsInitialized() override;
 
  private:
   // The AppCacheStorageImpl class methods and datamembers may only be
diff --git a/content/browser/appcache/appcache_url_loader_factory.cc b/content/browser/appcache/appcache_url_loader_factory.cc
index 37c105a..aa2bdbeb 100644
--- a/content/browser/appcache/appcache_url_loader_factory.cc
+++ b/content/browser/appcache/appcache_url_loader_factory.cc
@@ -6,157 +6,40 @@
 
 #include "base/bind.h"
 #include "base/logging.h"
-#include "content/browser/appcache/appcache_entry.h"
-#include "content/browser/appcache/appcache_policy.h"
-#include "content/browser/appcache/appcache_request.h"
-#include "content/browser/appcache/appcache_storage.h"
-#include "content/browser/appcache/chrome_appcache_service.h"
+#include "content/browser/appcache/appcache_request_handler.h"
+#include "content/browser/appcache/appcache_url_loader_job.h"
+#include "content/browser/appcache/appcache_url_loader_request.h"
 #include "content/browser/url_loader_factory_getter.h"
-#include "content/common/network_service.mojom.h"
 #include "content/common/resource_request.h"
-#include "content/common/url_loader.mojom.h"
 #include "content/common/url_loader_factory.mojom.h"
 #include "content/public/browser/browser_thread.h"
-#include "mojo/public/cpp/bindings/associated_binding.h"
-#include "mojo/public/cpp/bindings/associated_interface_ptr.h"
+#include "mojo/public/cpp/bindings/binding.h"
 #include "mojo/public/cpp/bindings/binding_set.h"
+#include "mojo/public/cpp/bindings/interface_ptr.h"
 
 namespace content {
 
-namespace {
-
-// Handles AppCache URL loads for the network service.
-class AppCacheURLLoader : public AppCacheStorage::Delegate,
-                          public mojom::URLLoader {
- public:
-  AppCacheURLLoader(const ResourceRequest& request,
-                    mojom::URLLoaderAssociatedRequest url_loader_request,
-                    int32_t routing_id,
-                    int32_t request_id,
-                    mojom::URLLoaderClientPtr client_info,
-                    ChromeAppCacheService* appcache_service,
-                    URLLoaderFactoryGetter* factory_getter)
-      : request_(request),
-        routing_id_(routing_id),
-        request_id_(request_id),
-        client_info_(std::move(client_info)),
-        appcache_service_(appcache_service),
-        factory_getter_(factory_getter),
-        binding_(this, std::move(url_loader_request)) {
-    binding_.set_connection_error_handler(base::Bind(
-        &AppCacheURLLoader::OnConnectionError, base::Unretained(this)));
-  }
-
-  ~AppCacheURLLoader() override {}
-
-  void Start() {
-    // If the origin does not exist in the AppCache usage map, then we can
-    // safely call the network service here.
-    if (appcache_service_->storage()->usage_map()->find(
-            request_.url.GetOrigin()) ==
-        appcache_service_->storage()->usage_map()->end()) {
-      factory_getter_->GetNetworkFactory()->get()->CreateLoaderAndStart(
-          mojo::MakeRequest(&network_loader_request_), routing_id_, request_id_,
-          mojom::kURLLoadOptionSendSSLInfo, request_, std::move(client_info_));
-      return;
-    }
-
-    appcache_service_->storage()->FindResponseForMainRequest(request_.url,
-                                                             GURL(), this);
-  }
-
-  // mojom::URLLoader implementation:
-  void FollowRedirect() override { network_loader_request_->FollowRedirect(); }
-
-  void SetPriority(net::RequestPriority priority,
-                   int32_t intra_priority_value) override {
-    DCHECK(false);
-  }
-
- private:
-  // AppCacheStorage::Delegate methods.
-  void OnMainResponseFound(const GURL& url,
-                           const AppCacheEntry& entry,
-                           const GURL& fallback_url,
-                           const AppCacheEntry& fallback_entry,
-                           int64_t cache_id,
-                           int64_t group_id,
-                           const GURL& manifest_url) override {
-    AppCachePolicy* policy = appcache_service_->appcache_policy();
-    bool was_blocked_by_policy =
-        !manifest_url.is_empty() && policy &&
-        !policy->CanLoadAppCache(manifest_url,
-                                 request_.first_party_for_cookies);
-
-    if (was_blocked_by_policy || !entry.has_response_id() ||
-        cache_id == kAppCacheNoCacheId) {
-      factory_getter_->GetNetworkFactory()->get()->CreateLoaderAndStart(
-          mojo::MakeRequest(&network_loader_request_), routing_id_, request_id_,
-          mojom::kURLLoadOptionSendSSLInfo, request_, std::move(client_info_));
-    } else {
-      DLOG(WARNING) << "AppCache found for url " << url
-                    << " Returning AppCache factory\n";
-      // TODO(ananta)
-      // Provide the plumbing to initiate AppCache requests here.
-      factory_getter_->GetNetworkFactory()->get()->CreateLoaderAndStart(
-          mojo::MakeRequest(&network_loader_request_), routing_id_, request_id_,
-          mojom::kURLLoadOptionSendSSLInfo, request_, std::move(client_info_));
-    }
-  }
-
-  void OnConnectionError() { delete this; }
-
-  // The current request.
-  ResourceRequest request_;
-
-  // URLLoader proxy for the network service.
-  mojom::URLLoaderAssociatedPtr network_loader_request_;
-
-  // Routing id of the request. This is 0 for navigation requests. For
-  // subresource requests it is non zero.
-  int routing_id_;
-
-  // Request id.
-  int request_id_;
-
-  // The URLLoaderClient pointer. We call this interface with notifications
-  // about the URL load
-  mojom::URLLoaderClientPtr client_info_;
-
-  // Used to query AppCacheStorage to see if a request can be served out of the
-  /// AppCache.
-  scoped_refptr<ChromeAppCacheService> appcache_service_;
-
-  // Used to retrieve the network service factory to pass requests to the
-  // network service.
-  scoped_refptr<URLLoaderFactoryGetter> factory_getter_;
-
-  // Binds the URLLoaderClient with us.
-  mojo::AssociatedBinding<mojom::URLLoader> binding_;
-
-  DISALLOW_COPY_AND_ASSIGN(AppCacheURLLoader);
-};
-
-}  // namespace
-
 // Implements the URLLoaderFactory mojom for AppCache requests.
 AppCacheURLLoaderFactory::AppCacheURLLoaderFactory(
-    ChromeAppCacheService* appcache_service,
+    mojom::URLLoaderFactoryRequest request,
     URLLoaderFactoryGetter* factory_getter)
-    : appcache_service_(appcache_service), factory_getter_(factory_getter) {}
+    : binding_(this, std::move(request)), factory_getter_(factory_getter) {
+  binding_.set_connection_error_handler(base::Bind(
+      &AppCacheURLLoaderFactory::OnConnectionError, base::Unretained(this)));
+}
 
 AppCacheURLLoaderFactory::~AppCacheURLLoaderFactory() {}
 
 // static
-void AppCacheURLLoaderFactory::CreateURLLoaderFactory(
-    mojom::URLLoaderFactoryRequest request,
-    ChromeAppCacheService* appcache_service,
+mojom::URLLoaderFactoryPtr AppCacheURLLoaderFactory::CreateURLLoaderFactory(
     URLLoaderFactoryGetter* factory_getter) {
-  std::unique_ptr<AppCacheURLLoaderFactory> factory_instance(
-      new AppCacheURLLoaderFactory(appcache_service, factory_getter));
-  AppCacheURLLoaderFactory* raw_factory = factory_instance.get();
-  raw_factory->loader_factory_bindings_.AddBinding(std::move(factory_instance),
-                                                   std::move(request));
+  mojom::URLLoaderFactoryPtr loader_factory;
+  mojom::URLLoaderFactoryRequest request = mojo::MakeRequest(&loader_factory);
+
+  // This instance will get deleted when the client drops the connection.
+  // Please see OnConnectionError() for details.
+  new AppCacheURLLoaderFactory(std::move(request), factory_getter);
+  return loader_factory;
 }
 
 void AppCacheURLLoaderFactory::CreateLoaderAndStart(
@@ -167,12 +50,7 @@
     const ResourceRequest& request,
     mojom::URLLoaderClientPtr client) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
-
-  // This will get deleted when the connection is dropped by the client.
-  AppCacheURLLoader* loader = new AppCacheURLLoader(
-      request, std::move(url_loader_request), routing_id, request_id,
-      std::move(client), appcache_service_.get(), factory_getter_.get());
-  loader->Start();
+  NOTREACHED() << "Currently not implemented";
 }
 
 void AppCacheURLLoaderFactory::SyncLoad(int32_t routing_id,
@@ -182,4 +60,8 @@
   NOTREACHED();
 }
 
+void AppCacheURLLoaderFactory::OnConnectionError() {
+  base::ThreadTaskRunnerHandle::Get()->DeleteSoon(FROM_HERE, this);
+}
+
 }  // namespace content
\ No newline at end of file
diff --git a/content/browser/appcache/appcache_url_loader_factory.h b/content/browser/appcache/appcache_url_loader_factory.h
index f615395..2e39001 100644
--- a/content/browser/appcache/appcache_url_loader_factory.h
+++ b/content/browser/appcache/appcache_url_loader_factory.h
@@ -7,11 +7,13 @@
 
 #include "base/memory/ref_counted.h"
 #include "content/common/url_loader_factory.mojom.h"
-#include "mojo/public/cpp/bindings/strong_binding_set.h"
+#include "mojo/public/cpp/bindings/binding.h"
 #include "url/gurl.h"
 
 namespace content {
-class ChromeAppCacheService;
+
+class AppCacheJob;
+class AppCacheServiceImpl;
 class URLLoaderFactoryGetter;
 
 // Implements the URLLoaderFactory mojom for AppCache requests.
@@ -20,13 +22,12 @@
   ~AppCacheURLLoaderFactory() override;
 
   // Factory function to create an instance of the factory.
-  // The |appcache_service| parameter is used to query the underlying
-  // AppCacheStorage instance to check if we can service requests from the
-  // AppCache. We pass unhandled requests to the network service retrieved from
-  // the |factory_getter|.
-  static void CreateURLLoaderFactory(mojom::URLLoaderFactoryRequest request,
-                                     ChromeAppCacheService* appcache_service,
-                                     URLLoaderFactoryGetter* factory_getter);
+  // 1. The |factory_getter| parameter is used to query the network service
+  //    to pass network requests to.
+  // Returns a URLLoaderFactoryPtr instance which controls the lifetime of the
+  // factory.
+  static mojom::URLLoaderFactoryPtr CreateURLLoaderFactory(
+      URLLoaderFactoryGetter* factory_getter);
 
   // mojom::URLLoaderFactory implementation.
   void CreateLoaderAndStart(
@@ -42,19 +43,18 @@
                 SyncLoadCallback callback) override;
 
  private:
-  AppCacheURLLoaderFactory(ChromeAppCacheService* appcache_service,
+  AppCacheURLLoaderFactory(mojom::URLLoaderFactoryRequest request,
                            URLLoaderFactoryGetter* factory_getter);
 
-  // Used to query AppCacheStorage to see if a request can be served out of the
-  /// AppCache.
-  scoped_refptr<ChromeAppCacheService> appcache_service_;
+  void OnConnectionError();
+
+  // Mojo binding.
+  mojo::Binding<mojom::URLLoaderFactory> binding_;
 
   // Used to retrieve the network service factory to pass unhandled requests to
   // the network service.
   scoped_refptr<URLLoaderFactoryGetter> factory_getter_;
 
-  mojo::StrongBindingSet<mojom::URLLoaderFactory> loader_factory_bindings_;
-
   DISALLOW_COPY_AND_ASSIGN(AppCacheURLLoaderFactory);
 };
 
diff --git a/content/browser/appcache/appcache_url_loader_job.cc b/content/browser/appcache/appcache_url_loader_job.cc
index 09f1545..86d90fe 100644
--- a/content/browser/appcache/appcache_url_loader_job.cc
+++ b/content/browser/appcache/appcache_url_loader_job.cc
@@ -3,51 +3,288 @@
 // found in the LICENSE file.
 
 #include "content/browser/appcache/appcache_url_loader_job.h"
-#include "content/browser/appcache/appcache_entry.h"
+
+#include "base/strings/string_number_conversions.h"
+#include "content/browser/appcache/appcache_histograms.h"
+#include "content/common/net_adapters.h"
+#include "content/public/common/resource_type.h"
+#include "net/http/http_status_code.h"
 
 namespace content {
 
-AppCacheURLLoaderJob::~AppCacheURLLoaderJob() {}
+AppCacheURLLoaderJob::~AppCacheURLLoaderJob() {
+  if (storage_.get())
+    storage_->CancelDelegateCallbacks(this);
+}
 
 void AppCacheURLLoaderJob::Kill() {}
 
 bool AppCacheURLLoaderJob::IsStarted() const {
-  return false;
-}
-
-bool AppCacheURLLoaderJob::IsWaiting() const {
-  return false;
-}
-
-bool AppCacheURLLoaderJob::IsDeliveringAppCacheResponse() const {
-  return false;
-}
-
-bool AppCacheURLLoaderJob::IsDeliveringNetworkResponse() const {
-  return false;
-}
-
-bool AppCacheURLLoaderJob::IsDeliveringErrorResponse() const {
-  return false;
-}
-
-bool AppCacheURLLoaderJob::IsCacheEntryNotFound() const {
-  return false;
+  return delivery_type_ != AWAITING_DELIVERY_ORDERS;
 }
 
 void AppCacheURLLoaderJob::DeliverAppCachedResponse(const GURL& manifest_url,
                                                     int64_t cache_id,
                                                     const AppCacheEntry& entry,
-                                                    bool is_fallback) {}
+                                                    bool is_fallback) {
+  if (!storage_.get()) {
+    DeliverErrorResponse();
+    return;
+  }
 
-void AppCacheURLLoaderJob::DeliverNetworkResponse() {}
+  delivery_type_ = APPCACHED_DELIVERY;
 
-void AppCacheURLLoaderJob::DeliverErrorResponse() {}
+  AppCacheHistograms::AddAppCacheJobStartDelaySample(base::TimeTicks::Now() -
+                                                     start_time_tick_);
 
-const GURL& AppCacheURLLoaderJob::GetURL() const {
-  return url_;
+  manifest_url_ = manifest_url;
+  cache_id_ = cache_id;
+  entry_ = entry;
+  is_fallback_ = is_fallback;
+
+  // TODO(ananta)
+  // Implement the AppCacheServiceImpl::Observer interface or add weak pointer
+  // support to it.
+  storage_->LoadResponseInfo(manifest_url_, entry_.response_id(), this);
 }
 
-AppCacheURLLoaderJob::AppCacheURLLoaderJob() {}
+void AppCacheURLLoaderJob::DeliverNetworkResponse() {
+  delivery_type_ = NETWORK_DELIVERY;
+
+  AppCacheHistograms::AddNetworkJobStartDelaySample(base::TimeTicks::Now() -
+                                                    start_time_tick_);
+
+  DCHECK(!loader_callback_.is_null());
+  // In network service land, if we are processing a navigation request, we
+  // need to inform the loader callback that we are not going to handle this
+  // request. The loader callback is valid only for navigation requests.
+  std::move(loader_callback_).Run(StartLoaderCallback());
+}
+
+void AppCacheURLLoaderJob::DeliverErrorResponse() {
+  delivery_type_ = ERROR_DELIVERY;
+
+  // We expect the URLLoaderClient pointer to be valid at this point.
+  DCHECK(client_info_);
+
+  // AppCacheURLRequestJob uses ERR_FAILED as the error code here. That seems
+  // to map to HTTP_INTERNAL_SERVER_ERROR.
+  std::string status("HTTP/1.1 ");
+  status.append(base::IntToString(net::HTTP_INTERNAL_SERVER_ERROR));
+  status.append(" ");
+  status.append(net::GetHttpReasonPhrase(net::HTTP_INTERNAL_SERVER_ERROR));
+  status.append("\0\0", 2);
+
+  ResourceResponseHead response;
+  response.headers = new net::HttpResponseHeaders(status);
+  client_info_->OnReceiveResponse(response, base::nullopt, nullptr);
+
+  NotifyCompleted(net::ERR_FAILED);
+
+  AppCacheHistograms::AddErrorJobStartDelaySample(base::TimeTicks::Now() -
+                                                  start_time_tick_);
+}
+
+const GURL& AppCacheURLLoaderJob::GetURL() const {
+  return request_.url;
+}
+
+AppCacheURLLoaderJob* AppCacheURLLoaderJob::AsURLLoaderJob() {
+  return this;
+}
+
+void AppCacheURLLoaderJob::FollowRedirect() {
+  DCHECK(false);
+}
+
+void AppCacheURLLoaderJob::SetPriority(net::RequestPriority priority,
+                                       int32_t intra_priority_value) {
+  NOTREACHED() << "We don't support SetPriority()";
+}
+
+void AppCacheURLLoaderJob::Start(mojom::URLLoaderRequest request,
+                                 mojom::URLLoaderClientPtr client) {
+  DCHECK(!binding_.is_bound());
+  binding_.Bind(std::move(request));
+
+  binding_.set_connection_error_handler(base::Bind(
+      &AppCacheURLLoaderJob::OnConnectionError, StaticAsWeakPtr(this)));
+
+  client_info_ = std::move(client);
+
+  // Send the cached AppCacheResponse if any.
+  if (info_.get())
+    SendResponseInfo();
+}
+
+AppCacheURLLoaderJob::AppCacheURLLoaderJob(const ResourceRequest& request,
+                                           AppCacheStorage* storage)
+    : request_(request),
+      storage_(storage->GetWeakPtr()),
+      start_time_tick_(base::TimeTicks::Now()),
+      cache_id_(kAppCacheNoCacheId),
+      is_fallback_(false),
+      binding_(this),
+      writable_handle_watcher_(FROM_HERE,
+                               mojo::SimpleWatcher::ArmingPolicy::MANUAL) {}
+
+void AppCacheURLLoaderJob::OnResponseInfoLoaded(
+    AppCacheResponseInfo* response_info,
+    int64_t response_id) {
+  DCHECK(IsDeliveringAppCacheResponse());
+
+  if (!storage_.get()) {
+    DeliverErrorResponse();
+    return;
+  }
+
+  if (response_info) {
+    info_ = response_info;
+    reader_.reset(
+        storage_->CreateResponseReader(manifest_url_, entry_.response_id()));
+
+    DCHECK(!loader_callback_.is_null());
+    std::move(loader_callback_)
+        .Run(base::Bind(&AppCacheURLLoaderJob::Start, StaticAsWeakPtr(this)));
+
+    // TODO(ananta)
+    // Handle range requests.
+
+    response_body_stream_ = std::move(data_pipe_.producer_handle);
+
+    // TODO(ananta)
+    // Move the asynchronous reading and mojo pipe handling code to a helper
+    // class. That would also need a change to BlobURLLoader.
+
+    // Wait for the data pipe to be ready to accept data.
+    writable_handle_watcher_.Watch(
+        response_body_stream_.get(), MOJO_HANDLE_SIGNAL_WRITABLE,
+        base::Bind(&AppCacheURLLoaderJob::OnResponseBodyStreamReady,
+                   StaticAsWeakPtr(this)));
+
+    if (client_info_)
+      SendResponseInfo();
+
+    ReadMore();
+  } else {
+    // Error case here. We fallback to the network.
+    DeliverNetworkResponse();
+    AppCacheHistograms::CountResponseRetrieval(
+        false, IsResourceTypeFrame(request_.resource_type),
+        manifest_url_.GetOrigin());
+
+    cache_entry_not_found_ = true;
+  }
+}
+
+void AppCacheURLLoaderJob::OnReadComplete(int result) {
+  DLOG(WARNING) << "AppCache read completed with result: " << result;
+
+  bool is_main_resource = IsResourceTypeFrame(request_.resource_type);
+
+  if (result == 0) {
+    NotifyCompleted(result);
+    AppCacheHistograms::CountResponseRetrieval(true, is_main_resource,
+                                               manifest_url_.GetOrigin());
+    return;
+  } else if (result < 0) {
+    // TODO(ananta)
+    // Populate the relevant fields of the ResourceRequestCompletionStatus
+    // structure.
+    NotifyCompleted(result);
+    AppCacheHistograms::CountResponseRetrieval(false, is_main_resource,
+                                               manifest_url_.GetOrigin());
+    return;
+  }
+
+  uint32_t bytes_written = static_cast<uint32_t>(result);
+  response_body_stream_ = pending_write_->Complete(bytes_written);
+  pending_write_ = nullptr;
+  ReadMore();
+}
+
+void AppCacheURLLoaderJob::OnConnectionError() {
+  if (storage_.get())
+    storage_->CancelDelegateCallbacks(this);
+  base::ThreadTaskRunnerHandle::Get()->DeleteSoon(FROM_HERE, this);
+}
+
+void AppCacheURLLoaderJob::SendResponseInfo() {
+  DCHECK(client_info_);
+
+  // If this is null it means the response information was sent to the client.
+  if (!data_pipe_.consumer_handle.is_valid())
+    return;
+
+  const net::HttpResponseInfo* http_info = info_->http_response_info();
+
+  ResourceResponseHead response_head;
+  response_head.headers = http_info->headers;
+
+  // TODO(ananta)
+  // Copy more fields.
+  http_info->headers->GetMimeType(&response_head.mime_type);
+  http_info->headers->GetCharset(&response_head.charset);
+
+  response_head.request_time = http_info->request_time;
+  response_head.response_time = http_info->response_time;
+  response_head.content_length = info_->response_data_size();
+
+  client_info_->OnReceiveResponse(response_head, http_info->ssl_info,
+                                  mojom::DownloadedTempFilePtr());
+
+  client_info_->OnStartLoadingResponseBody(
+      std::move(data_pipe_.consumer_handle));
+}
+
+void AppCacheURLLoaderJob::ReadMore() {
+  DCHECK(!pending_write_.get());
+
+  uint32_t num_bytes;
+  // TODO: we should use the abstractions in MojoAsyncResourceHandler.
+  MojoResult result = NetToMojoPendingBuffer::BeginWrite(
+      &response_body_stream_, &pending_write_, &num_bytes);
+  if (result == MOJO_RESULT_SHOULD_WAIT) {
+    // The pipe is full. We need to wait for it to have more space.
+    writable_handle_watcher_.ArmOrNotify();
+    return;
+  } else if (result != MOJO_RESULT_OK) {
+    // The response body stream is in a bad state. Bail.
+    // TODO(ananta)
+    // Add proper error handling here.
+    NotifyCompleted(net::ERR_FAILED);
+    writable_handle_watcher_.Cancel();
+    response_body_stream_.reset();
+    return;
+  }
+
+  CHECK_GT(static_cast<uint32_t>(std::numeric_limits<int>::max()), num_bytes);
+  scoped_refptr<NetToMojoIOBuffer> buffer =
+      new NetToMojoIOBuffer(pending_write_.get());
+
+  reader_->ReadData(
+      buffer.get(), info_->response_data_size(),
+      base::Bind(&AppCacheURLLoaderJob::OnReadComplete, StaticAsWeakPtr(this)));
+}
+
+void AppCacheURLLoaderJob::OnResponseBodyStreamReady(MojoResult result) {
+  // TODO(ananta)
+  // Add proper error handling here.
+  if (result != MOJO_RESULT_OK) {
+    DCHECK(false);
+    NotifyCompleted(net::ERR_FAILED);
+  }
+  ReadMore();
+}
+
+void AppCacheURLLoaderJob::NotifyCompleted(int error_code) {
+  if (storage_.get())
+    storage_->CancelDelegateCallbacks(this);
+  // TODO(ananta)
+  // Fill other details in the ResourceRequestCompletionStatus structure.
+  ResourceRequestCompletionStatus request_complete_data;
+  request_complete_data.error_code = error_code;
+  client_info_->OnComplete(request_complete_data);
+}
 
 }  // namespace content
diff --git a/content/browser/appcache/appcache_url_loader_job.h b/content/browser/appcache/appcache_url_loader_job.h
index 3d526c9..3a1e125 100644
--- a/content/browser/appcache/appcache_url_loader_job.h
+++ b/content/browser/appcache/appcache_url_loader_job.h
@@ -6,30 +6,40 @@
 #define CONTENT_BROWSER_APPCACHE_APPCACHE_URL_LOADER_JOB_H_
 
 #include "base/logging.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/weak_ptr.h"
 #include "base/strings/string16.h"
+#include "base/time/time.h"
+#include "content/browser/appcache/appcache_entry.h"
 #include "content/browser/appcache/appcache_job.h"
+#include "content/browser/appcache/appcache_response.h"
+#include "content/browser/appcache/appcache_storage.h"
+#include "content/browser/loader/url_loader_request_handler.h"
 #include "content/common/content_export.h"
+#include "content/common/resource_request.h"
+#include "content/common/url_loader.mojom.h"
+#include "mojo/public/cpp/bindings/binding.h"
+#include "mojo/public/cpp/system/data_pipe.h"
 
 namespace content {
 
-class AppCacheHost;
 class AppCacheRequest;
-class AppCacheStorage;
+class NetToMojoPendingBuffer;
 
 // AppCacheJob wrapper for a mojom::URLLoader implementation which returns
 // responses stored in the AppCache.
-class CONTENT_EXPORT AppCacheURLLoaderJob : public AppCacheJob {
+class CONTENT_EXPORT AppCacheURLLoaderJob : public AppCacheJob,
+                                            public AppCacheStorage::Delegate,
+                                            public mojom::URLLoader {
  public:
   ~AppCacheURLLoaderJob() override;
 
+  // Sets up the bindings.
+  void Start(mojom::URLLoaderRequest request, mojom::URLLoaderClientPtr client);
+
   // AppCacheJob overrides.
   void Kill() override;
   bool IsStarted() const override;
-  bool IsWaiting() const override;
-  bool IsDeliveringAppCacheResponse() const override;
-  bool IsDeliveringNetworkResponse() const override;
-  bool IsDeliveringErrorResponse() const override;
-  bool IsCacheEntryNotFound() const override;
   void DeliverAppCachedResponse(const GURL& manifest_url,
                                 int64_t cache_id,
                                 const AppCacheEntry& entry,
@@ -37,14 +47,90 @@
   void DeliverNetworkResponse() override;
   void DeliverErrorResponse() override;
   const GURL& GetURL() const override;
+  AppCacheURLLoaderJob* AsURLLoaderJob() override;
+
+  // mojom::URLLoader implementation:
+  void FollowRedirect() override;
+  void SetPriority(net::RequestPriority priority,
+                   int32_t intra_priority_value) override;
+
+  void set_loader_callback(LoaderCallback callback) {
+    loader_callback_ = std::move(callback);
+  }
 
  protected:
   // AppCacheJob::Create() creates this instance.
   friend class AppCacheJob;
 
-  AppCacheURLLoaderJob();
+  AppCacheURLLoaderJob(const ResourceRequest& request,
+                       AppCacheStorage* storage);
 
-  GURL url_;
+  // AppCacheStorage::Delegate methods
+  void OnResponseInfoLoaded(AppCacheResponseInfo* response_info,
+                            int64_t response_id) override;
+
+  // AppCacheResponseReader completion callback
+  void OnReadComplete(int result);
+
+  void OnConnectionError();
+
+  // Helper to send the AppCacheResponseInfo to the URLLoaderClient.
+  void SendResponseInfo();
+
+  // Helper function to read the data from the AppCache.
+  void ReadMore();
+
+  // Callback invoked when the data pipe can be written to.
+  void OnResponseBodyStreamReady(MojoResult result);
+
+  // Notifies the client about request completion.
+  void NotifyCompleted(int error_code);
+
+  // The current request.
+  ResourceRequest request_;
+
+  base::WeakPtr<AppCacheStorage> storage_;
+
+  // The response details.
+  scoped_refptr<AppCacheResponseInfo> info_;
+
+  // Used to read the cache.
+  std::unique_ptr<AppCacheResponseReader> reader_;
+
+  // Time when the request started.
+  base::TimeTicks start_time_tick_;
+
+  // The AppCache manifest URL.
+  GURL manifest_url_;
+
+  // The AppCache id.
+  int64_t cache_id_;
+
+  AppCacheEntry entry_;
+
+  // Set to true if we are loading fallback content.
+  bool is_fallback_;
+
+  // The data pipe used to transfer AppCache data to the client.
+  mojo::DataPipe data_pipe_;
+
+  // Binds the URLLoaderClient with us.
+  mojo::Binding<mojom::URLLoader> binding_;
+
+  // The URLLoaderClient pointer. We call this interface with notifications
+  // about the URL load
+  mojom::URLLoaderClientPtr client_info_;
+
+  // mojo data pipe entities.
+  mojo::ScopedDataPipeProducerHandle response_body_stream_;
+
+  scoped_refptr<NetToMojoPendingBuffer> pending_write_;
+
+  mojo::SimpleWatcher writable_handle_watcher_;
+
+  // The Callback to be invoked in the network service land to indicate if
+  // the request can be serviced via the AppCache.
+  LoaderCallback loader_callback_;
 
   DISALLOW_COPY_AND_ASSIGN(AppCacheURLLoaderJob);
 };
diff --git a/content/browser/appcache/appcache_url_loader_request.cc b/content/browser/appcache/appcache_url_loader_request.cc
index c85de029..11c85ba 100644
--- a/content/browser/appcache/appcache_url_loader_request.cc
+++ b/content/browser/appcache/appcache_url_loader_request.cc
@@ -9,27 +9,27 @@
 
 // static
 std::unique_ptr<AppCacheURLLoaderRequest> AppCacheURLLoaderRequest::Create(
-    std::unique_ptr<ResourceRequest> request) {
+    const ResourceRequest& request) {
   return std::unique_ptr<AppCacheURLLoaderRequest>(
-      new AppCacheURLLoaderRequest(std::move(request)));
+      new AppCacheURLLoaderRequest(request));
 }
 
 AppCacheURLLoaderRequest::~AppCacheURLLoaderRequest() {}
 
 const GURL& AppCacheURLLoaderRequest::GetURL() const {
-  return request_->url;
+  return request_.url;
 }
 
 const std::string& AppCacheURLLoaderRequest::GetMethod() const {
-  return request_->method;
+  return request_.method;
 }
 
 const GURL& AppCacheURLLoaderRequest::GetFirstPartyForCookies() const {
-  return request_->first_party_for_cookies;
+  return request_.first_party_for_cookies;
 }
 
 const GURL AppCacheURLLoaderRequest::GetReferrer() const {
-  return request_->referrer;
+  return request_.referrer;
 }
 
 bool AppCacheURLLoaderRequest::IsSuccess() const {
@@ -54,11 +54,11 @@
 }
 
 ResourceRequest* AppCacheURLLoaderRequest::GetResourceRequest() {
-  return request_.get();
+  return &request_;
 }
 
 AppCacheURLLoaderRequest::AppCacheURLLoaderRequest(
-    std::unique_ptr<ResourceRequest> request)
-    : request_(std::move(request)) {}
+    const ResourceRequest& request)
+    : request_(request) {}
 
 }  // namespace content
diff --git a/content/browser/appcache/appcache_url_loader_request.h b/content/browser/appcache/appcache_url_loader_request.h
index 13581f1..97e3f77 100644
--- a/content/browser/appcache/appcache_url_loader_request.h
+++ b/content/browser/appcache/appcache_url_loader_request.h
@@ -17,7 +17,7 @@
   // Factory function to create an instance of the AppCacheResourceRequest
   // class.
   static std::unique_ptr<AppCacheURLLoaderRequest> Create(
-      std::unique_ptr<ResourceRequest> request);
+      const ResourceRequest& request);
 
   ~AppCacheURLLoaderRequest() override;
 
@@ -38,14 +38,13 @@
   bool IsError() const override;
   int GetResponseCode() const override;
   std::string GetResponseHeaderByName(const std::string& name) const override;
-
   ResourceRequest* GetResourceRequest() override;
 
  protected:
-  explicit AppCacheURLLoaderRequest(std::unique_ptr<ResourceRequest> request);
+  explicit AppCacheURLLoaderRequest(const ResourceRequest& request);
 
  private:
-  std::unique_ptr<ResourceRequest> request_;
+  ResourceRequest request_;
 
   DISALLOW_COPY_AND_ASSIGN(AppCacheURLLoaderRequest);
 };
diff --git a/content/browser/appcache/appcache_url_request_job.cc b/content/browser/appcache/appcache_url_request_job.cc
index 8a89def..b17d3d77 100644
--- a/content/browser/appcache/appcache_url_request_job.cc
+++ b/content/browser/appcache/appcache_url_request_job.cc
@@ -60,26 +60,6 @@
   return has_been_started_;
 }
 
-bool AppCacheURLRequestJob::IsWaiting() const {
-  return delivery_type_ == AWAITING_DELIVERY_ORDERS;
-}
-
-bool AppCacheURLRequestJob::IsDeliveringAppCacheResponse() const {
-  return delivery_type_ == APPCACHED_DELIVERY;
-}
-
-bool AppCacheURLRequestJob::IsDeliveringNetworkResponse() const {
-  return delivery_type_ == NETWORK_DELIVERY;
-}
-
-bool AppCacheURLRequestJob::IsDeliveringErrorResponse() const {
-  return delivery_type_ == ERROR_DELIVERY;
-}
-
-bool AppCacheURLRequestJob::IsCacheEntryNotFound() const {
-  return cache_entry_not_found_;
-}
-
 void AppCacheURLRequestJob::DeliverAppCachedResponse(const GURL& manifest_url,
                                                      int64_t cache_id,
                                                      const AppCacheEntry& entry,
@@ -128,11 +108,9 @@
       storage_(storage),
       has_been_started_(false),
       has_been_killed_(false),
-      delivery_type_(AWAITING_DELIVERY_ORDERS),
       cache_id_(kAppCacheNoCacheId),
       is_fallback_(false),
       is_main_resource_(is_main_resource),
-      cache_entry_not_found_(false),
       on_prepare_to_restart_callback_(restart_callback) {
   DCHECK(storage_);
 }
diff --git a/content/browser/appcache/appcache_url_request_job.h b/content/browser/appcache/appcache_url_request_job.h
index 56b3cfa..f4f23a7 100644
--- a/content/browser/appcache/appcache_url_request_job.h
+++ b/content/browser/appcache/appcache_url_request_job.h
@@ -44,11 +44,6 @@
   // AppCacheJob overrides.
   void Kill() override;
   bool IsStarted() const override;
-  bool IsWaiting() const override;
-  bool IsDeliveringAppCacheResponse() const override;
-  bool IsDeliveringNetworkResponse() const override;
-  bool IsDeliveringErrorResponse() const override;
-  bool IsCacheEntryNotFound() const override;
   void DeliverAppCachedResponse(const GURL& manifest_url,
                                 int64_t cache_id,
                                 const AppCacheEntry& entry,
@@ -83,13 +78,6 @@
                         bool is_main_resource,
                         const OnPrepareToRestartCallback& restart_callback_);
 
-  enum DeliveryType {
-    AWAITING_DELIVERY_ORDERS,
-    APPCACHED_DELIVERY,
-    NETWORK_DELIVERY,
-    ERROR_DELIVERY
-  };
-
   // Returns true if one of the Deliver methods has been called.
   bool has_delivery_orders() const { return !IsWaiting(); }
 
@@ -140,13 +128,11 @@
   base::TimeTicks start_time_tick_;
   bool has_been_started_;
   bool has_been_killed_;
-  DeliveryType delivery_type_;
   GURL manifest_url_;
   int64_t cache_id_;
   AppCacheEntry entry_;
   bool is_fallback_;
   bool is_main_resource_;  // Used for histogram logging.
-  bool cache_entry_not_found_;
   scoped_refptr<AppCacheResponseInfo> info_;
   scoped_refptr<net::GrowableIOBuffer> handler_source_buffer_;
   std::unique_ptr<AppCacheResponseReader> handler_source_reader_;
diff --git a/content/browser/appcache/mock_appcache_storage.cc b/content/browser/appcache/mock_appcache_storage.cc
index a5c5382..1bd3bed5 100644
--- a/content/browser/appcache/mock_appcache_storage.cc
+++ b/content/browser/appcache/mock_appcache_storage.cc
@@ -201,6 +201,10 @@
   }
 }
 
+bool MockAppCacheStorage::IsInitialized() {
+  return false;
+}
+
 void MockAppCacheStorage::ProcessGetAllInfo(
     scoped_refptr<DelegateReference> delegate_ref) {
   if (delegate_ref->delegate)
diff --git a/content/browser/appcache/mock_appcache_storage.h b/content/browser/appcache/mock_appcache_storage.h
index b19404b..d8a6ee3 100644
--- a/content/browser/appcache/mock_appcache_storage.h
+++ b/content/browser/appcache/mock_appcache_storage.h
@@ -81,6 +81,7 @@
                      const std::vector<int64_t>& response_ids) override;
   void DeleteResponses(const GURL& manifest_url,
                        const std::vector<int64_t>& response_ids) override;
+  bool IsInitialized() override;
 
  private:
   friend class AppCacheRequestHandlerTest;
diff --git a/content/browser/devtools/protocol/devtools_protocol_browsertest.cc b/content/browser/devtools/protocol/devtools_protocol_browsertest.cc
index 2845733e..addd4e1 100644
--- a/content/browser/devtools/protocol/devtools_protocol_browsertest.cc
+++ b/content/browser/devtools/protocol/devtools_protocol_browsertest.cc
@@ -1669,14 +1669,6 @@
   command_params.reset(new base::DictionaryValue());
   command_params->SetBoolean("discover", false);
   SendCommand("Target.setDiscoverTargets", std::move(command_params), true);
-  params = WaitForNotification("Target.targetDestroyed", true);
-  EXPECT_TRUE(params->GetString("targetId", &temp));
-  EXPECT_TRUE(ids.find(temp) != ids.end());
-  ids.erase(temp);
-  params = WaitForNotification("Target.targetDestroyed", true);
-  EXPECT_TRUE(params->GetString("targetId", &temp));
-  EXPECT_TRUE(ids.find(temp) != ids.end());
-  ids.erase(temp);
   EXPECT_TRUE(notifications_.empty());
 
   command_params.reset(new base::DictionaryValue());
diff --git a/content/browser/devtools/protocol/target_handler.cc b/content/browser/devtools/protocol/target_handler.cc
index 7a51b79..0c32e8f3 100644
--- a/content/browser/devtools/protocol/target_handler.cc
+++ b/content/browser/devtools/protocol/target_handler.cc
@@ -218,7 +218,8 @@
   auto it = reported_hosts_.find(host->GetId());
   if (it == reported_hosts_.end())
     return;
-  frontend_->TargetDestroyed(host->GetId());
+  if (discover_)
+    frontend_->TargetDestroyed(host->GetId());
   reported_hosts_.erase(it);
 }
 
diff --git a/content/browser/frame_host/form_submission_throttle.cc b/content/browser/frame_host/form_submission_throttle.cc
index 5784bbf..5a235d86 100644
--- a/content/browser/frame_host/form_submission_throttle.cc
+++ b/content/browser/frame_host/form_submission_throttle.cc
@@ -60,8 +60,12 @@
   RenderFrameHostImpl* render_frame =
       handle->frame_tree_node()->current_frame_host();
 
+  // TODO(estark): Move this check into NavigationRequest and split it into (1)
+  // check report-only CSP, (2) upgrade request if needed, (3) check enforced
+  // CSP to match how frame-src works. https://crbug.com/713388
   if (render_frame->IsAllowedByCsp(CSPDirective::FormAction, url, is_redirect,
-                                   handle->source_location())) {
+                                   handle->source_location(),
+                                   CSPContext::CHECK_ALL_CSP)) {
     return NavigationThrottle::PROCEED;
   }
 
diff --git a/content/browser/frame_host/mixed_content_navigation_throttle.h b/content/browser/frame_host/mixed_content_navigation_throttle.h
index 2313a7d..c7ca00f7 100644
--- a/content/browser/frame_host/mixed_content_navigation_throttle.h
+++ b/content/browser/frame_host/mixed_content_navigation_throttle.h
@@ -30,10 +30,6 @@
 //
 // Current mixed content W3C draft that drives this implementation:
 // https://w3c.github.io/webappsec-mixed-content/
-//
-// TODO(carlosk): For HSTS to work properly in PlzNavigate when these browser
-// side mixed content checks happen it needs to be ported to the browser as a
-// navigation throttle implementation. See https://crbug.com/692157.
 class MixedContentNavigationThrottle : public NavigationThrottle {
  public:
   static std::unique_ptr<NavigationThrottle> CreateThrottleForNavigation(
diff --git a/content/browser/frame_host/navigation_request.cc b/content/browser/frame_host/navigation_request.cc
index 521b288..0d1b534 100644
--- a/content/browser/frame_host/navigation_request.cc
+++ b/content/browser/frame_host/navigation_request.cc
@@ -950,9 +950,43 @@
   RenderFrameHostImpl* parent = parent_ftn->current_frame_host();
   DCHECK(parent);
 
+  // CSP checking happens in three phases, per steps 3-5 of
+  // https://fetch.spec.whatwg.org/#main-fetch:
+  //
+  // (1) Check report-only policies and trigger reports for any violations.
+  // (2) Upgrade the request to HTTPS if necessary.
+  // (3) Check enforced policies (triggering reports for any violations of those
+  //     policies) and block the request if necessary.
+  //
+  // This sequence of events allows site owners to learn about (via step 1) any
+  // requests that are upgraded in step 2.
+
+  bool allowed = parent->IsAllowedByCsp(
+      CSPDirective::FrameSrc, common_params_.url, is_redirect,
+      common_params_.source_location.value_or(SourceLocation()),
+      CSPContext::CHECK_REPORT_ONLY_CSP);
+
+  // Checking report-only CSP should never return false because no requests are
+  // blocked by report-only policies.
+  DCHECK(allowed);
+
+  // TODO(mkwst,estark): upgrade-insecure-requests does not work when following
+  // redirects. Trying to uprade the new URL on redirect here is fruitless: the
+  // redirect URL cannot be changed at this point. upgrade-insecure-requests
+  // needs to move to the net stack to resolve this. https://crbug.com/615885
+  if (!is_redirect) {
+    GURL new_url;
+    if (parent->ShouldModifyRequestUrlForCsp(
+            common_params_.url, true /* is subresource */, &new_url)) {
+      common_params_.url = new_url;
+      request_params_.original_url = new_url;
+    }
+  }
+
   if (parent->IsAllowedByCsp(
           CSPDirective::FrameSrc, common_params_.url, is_redirect,
-          common_params_.source_location.value_or(SourceLocation()))) {
+          common_params_.source_location.value_or(SourceLocation()),
+          CSPContext::CHECK_ENFORCED_CSP)) {
     return CONTENT_SECURITY_POLICY_CHECK_PASSED;
   }
 
diff --git a/content/browser/frame_host/render_widget_host_view_child_frame_browsertest.cc b/content/browser/frame_host/render_widget_host_view_child_frame_browsertest.cc
index d03ebb4b..b572962 100644
--- a/content/browser/frame_host/render_widget_host_view_child_frame_browsertest.cc
+++ b/content/browser/frame_host/render_widget_host_view_child_frame_browsertest.cc
@@ -3,8 +3,11 @@
 // found in the LICENSE file.
 
 #include "base/macros.h"
+#include "cc/surfaces/surface_id.h"
+#include "cc/surfaces/surface_sequence.h"
 #include "content/browser/frame_host/frame_tree_node.h"
 #include "content/browser/web_contents/web_contents_impl.h"
+#include "content/common/frame_messages.h"
 #include "content/common/view_messages.h"
 #include "content/public/browser/render_frame_host.h"
 #include "content/public/browser/web_contents.h"
@@ -114,4 +117,92 @@
   EXPECT_EQ(gfx::Size(75, 75), rwhv->GetVisibleViewportSize());
 }
 
+// A class to filter RequireSequence and SatisfySequence messages sent from
+// an embedding renderer for its child's Surfaces.
+class SurfaceRefMessageFilter : public BrowserMessageFilter {
+ public:
+  SurfaceRefMessageFilter()
+      : BrowserMessageFilter(FrameMsgStart),
+        require_message_loop_runner_(new content::MessageLoopRunner),
+        satisfy_message_loop_runner_(new content::MessageLoopRunner),
+        satisfy_received_(false),
+        require_received_first_(false) {}
+
+  void WaitForRequire() { require_message_loop_runner_->Run(); }
+
+  void WaitForSatisfy() { satisfy_message_loop_runner_->Run(); }
+
+  bool require_received_first() { return require_received_first_; }
+
+ protected:
+  ~SurfaceRefMessageFilter() override {}
+
+ private:
+  // BrowserMessageFilter:
+  bool OnMessageReceived(const IPC::Message& message) override {
+    IPC_BEGIN_MESSAGE_MAP(SurfaceRefMessageFilter, message)
+      IPC_MESSAGE_HANDLER(FrameHostMsg_RequireSequence, OnRequire)
+      IPC_MESSAGE_HANDLER(FrameHostMsg_SatisfySequence, OnSatisfy)
+    IPC_END_MESSAGE_MAP()
+    return false;
+  }
+
+  void OnRequire(const cc::SurfaceId& id, const cc::SurfaceSequence sequence) {
+    content::BrowserThread::PostTask(
+        content::BrowserThread::UI, FROM_HERE,
+        base::Bind(&SurfaceRefMessageFilter::OnRequireOnUI, this));
+  }
+
+  void OnRequireOnUI() {
+    if (!satisfy_received_)
+      require_received_first_ = true;
+    require_message_loop_runner_->Quit();
+  }
+
+  void OnSatisfy(const cc::SurfaceSequence sequence) {
+    content::BrowserThread::PostTask(
+        content::BrowserThread::UI, FROM_HERE,
+        base::Bind(&SurfaceRefMessageFilter::OnSatisfyOnUI, this));
+  }
+
+  void OnSatisfyOnUI() {
+    satisfy_received_ = true;
+    satisfy_message_loop_runner_->Quit();
+  }
+
+  scoped_refptr<content::MessageLoopRunner> require_message_loop_runner_;
+  scoped_refptr<content::MessageLoopRunner> satisfy_message_loop_runner_;
+  bool satisfy_received_;
+  bool require_received_first_;
+
+  DISALLOW_COPY_AND_ASSIGN(SurfaceRefMessageFilter);
+};
+
+// Test that when a child frame submits its first compositor frame, the
+// embedding renderer process properly acquires and releases references to the
+// new Surface. See https://crbug.com/701175.
+IN_PROC_BROWSER_TEST_F(RenderWidgetHostViewChildFrameTest,
+                       ChildFrameSurfaceReference) {
+  EXPECT_TRUE(NavigateToURL(
+      shell(), embedded_test_server()->GetURL(
+                   "a.com", "/cross_site_iframe_factory.html?a(a)")));
+
+  FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
+                            ->GetFrameTree()
+                            ->root();
+  ASSERT_EQ(1U, root->child_count());
+
+  scoped_refptr<SurfaceRefMessageFilter> filter = new SurfaceRefMessageFilter();
+  root->current_frame_host()->GetProcess()->AddFilter(filter.get());
+
+  GURL foo_url = embedded_test_server()->GetURL("foo.com", "/title1.html");
+  NavigateFrameToURL(root->child_at(0), foo_url);
+
+  // If one of these messages isn't received, this test times out.
+  filter->WaitForRequire();
+  filter->WaitForSatisfy();
+
+  EXPECT_TRUE(filter->require_received_first());
+}
+
 }  // namespace content
diff --git a/content/browser/indexed_db/indexed_db_transaction.cc b/content/browser/indexed_db/indexed_db_transaction.cc
index 8bc011e5..63a1f13 100644
--- a/content/browser/indexed_db/indexed_db_transaction.cc
+++ b/content/browser/indexed_db/indexed_db_transaction.cc
@@ -377,26 +377,28 @@
     committed = true;
   } else {
     base::TimeDelta active_time = base::Time::Now() - diagnostics_.start_time;
-    uint64_t size = transaction_->GetTransactionSize() / 1024;
+    uint64_t size_kb = transaction_->GetTransactionSize() / 1024;
+    // All histograms record 1KB to 1GB.
     switch (mode_) {
       case blink::kWebIDBTransactionModeReadOnly:
         UMA_HISTOGRAM_MEDIUM_TIMES(
             "WebCore.IndexedDB.Transaction.ReadOnly.TimeActive", active_time);
-        UMA_HISTOGRAM_MEMORY_KB(
-            "WebCore.IndexedDB.Transaction.ReadOnly.SizeOnCommit", size);
+        UMA_HISTOGRAM_COUNTS_1M(
+            "WebCore.IndexedDB.Transaction.ReadOnly.SizeOnCommit2", size_kb);
         break;
       case blink::kWebIDBTransactionModeReadWrite:
         UMA_HISTOGRAM_MEDIUM_TIMES(
             "WebCore.IndexedDB.Transaction.ReadWrite.TimeActive", active_time);
-        UMA_HISTOGRAM_MEMORY_KB(
-            "WebCore.IndexedDB.Transaction.ReadWrite.SizeOnCommit", size);
+        UMA_HISTOGRAM_COUNTS_1M(
+            "WebCore.IndexedDB.Transaction.ReadWrite.SizeOnCommit2", size_kb);
         break;
       case blink::kWebIDBTransactionModeVersionChange:
         UMA_HISTOGRAM_MEDIUM_TIMES(
             "WebCore.IndexedDB.Transaction.VersionChange.TimeActive",
             active_time);
-        UMA_HISTOGRAM_MEMORY_KB(
-            "WebCore.IndexedDB.Transaction.VersionChange.SizeOnCommit", size);
+        UMA_HISTOGRAM_COUNTS_1M(
+            "WebCore.IndexedDB.Transaction.VersionChange.SizeOnCommit2",
+            size_kb);
         break;
       default:
         NOTREACHED();
diff --git a/content/browser/loader/navigation_url_loader_network_service.cc b/content/browser/loader/navigation_url_loader_network_service.cc
index e9e9ac9..e358cb19 100644
--- a/content/browser/loader/navigation_url_loader_network_service.cc
+++ b/content/browser/loader/navigation_url_loader_network_service.cc
@@ -9,6 +9,7 @@
 #include "base/memory/ptr_util.h"
 #include "base/trace_event/trace_event.h"
 #include "content/browser/appcache/appcache_navigation_handle.h"
+#include "content/browser/appcache/appcache_request_handler.h"
 #include "content/browser/blob_storage/chrome_blob_storage_context.h"
 #include "content/browser/frame_host/frame_tree_node.h"
 #include "content/browser/frame_host/navigation_request_info.h"
@@ -166,7 +167,11 @@
     }
 
     if (appcache_handle_core) {
-      // TODO: add appcache code here.
+      std::unique_ptr<URLLoaderRequestHandler> appcache_handler =
+          AppCacheRequestHandler::InitializeForNavigationNetworkService(
+              *resource_request_, appcache_handle_core);
+      if (appcache_handler)
+        handlers_.push_back(std::move(appcache_handler));
     }
 
     Restart(std::move(url_loader_request), std::move(url_loader_client_ptr_));
diff --git a/content/browser/presentation/presentation_service_impl.cc b/content/browser/presentation/presentation_service_impl.cc
index ea75760..4f3e8b2 100644
--- a/content/browser/presentation/presentation_service_impl.cc
+++ b/content/browser/presentation/presentation_service_impl.cc
@@ -123,7 +123,8 @@
 void PresentationServiceImpl::ListenForScreenAvailability(const GURL& url) {
   DVLOG(2) << "ListenForScreenAvailability " << url.spec();
   if (!controller_delegate_) {
-    client_->OnScreenAvailabilityUpdated(url, false);
+    client_->OnScreenAvailabilityUpdated(
+        url, blink::mojom::ScreenAvailability::UNAVAILABLE);
     return;
   }
 
@@ -491,7 +492,10 @@
 
 void PresentationServiceImpl::ScreenAvailabilityListenerImpl
 ::OnScreenAvailabilityChanged(bool available) {
-  service_->client_->OnScreenAvailabilityUpdated(availability_url_, available);
+  service_->client_->OnScreenAvailabilityUpdated(
+      availability_url_, available
+                             ? blink::mojom::ScreenAvailability::AVAILABLE
+                             : blink::mojom::ScreenAvailability::UNAVAILABLE);
 }
 
 void PresentationServiceImpl::ScreenAvailabilityListenerImpl
diff --git a/content/browser/presentation/presentation_service_impl_unittest.cc b/content/browser/presentation/presentation_service_impl_unittest.cc
index c7fe639c..2535bce 100644
--- a/content/browser/presentation/presentation_service_impl_unittest.cc
+++ b/content/browser/presentation/presentation_service_impl_unittest.cc
@@ -196,7 +196,8 @@
     : public blink::mojom::PresentationServiceClient {
  public:
   MOCK_METHOD2(OnScreenAvailabilityUpdated,
-               void(const GURL& url, bool available));
+               void(const GURL& url,
+                    blink::mojom::ScreenAvailability availability));
   MOCK_METHOD2(OnConnectionStateChanged,
                void(const PresentationInfo& connection,
                     PresentationConnectionState new_state));
@@ -317,7 +318,11 @@
     ASSERT_TRUE(listener_it->second);
 
     base::RunLoop run_loop;
-    EXPECT_CALL(mock_client_, OnScreenAvailabilityUpdated(url, available))
+    blink::mojom::ScreenAvailability expected_availability =
+        available ? blink::mojom::ScreenAvailability::AVAILABLE
+                  : blink::mojom::ScreenAvailability::UNAVAILABLE;
+    EXPECT_CALL(mock_client_,
+                OnScreenAvailabilityUpdated(url, expected_availability))
         .WillOnce(InvokeWithoutArgs(&run_loop, &base::RunLoop::Quit));
     listener_it->second->OnScreenAvailabilityChanged(available);
     run_loop.Run();
diff --git a/content/browser/renderer_host/render_process_host_impl.cc b/content/browser/renderer_host/render_process_host_impl.cc
index fc876c6..b5bb568 100644
--- a/content/browser/renderer_host/render_process_host_impl.cc
+++ b/content/browser/renderer_host/render_process_host_impl.cc
@@ -1778,11 +1778,14 @@
 resource_coordinator::ResourceCoordinatorInterface*
 RenderProcessHostImpl::GetProcessResourceCoordinator() {
   if (!process_resource_coordinator_) {
+    base::ProcessHandle process_handle = GetHandle();
+    DCHECK(process_handle);
+
     process_resource_coordinator_ =
         base::MakeUnique<resource_coordinator::ResourceCoordinatorInterface>(
             ServiceManagerConnection::GetForProcess()->GetConnector(),
             resource_coordinator::CoordinationUnitType::kProcess,
-            base::Process(GetHandle()).Pid());
+            base::Process(process_handle).Pid());
   }
   return process_resource_coordinator_.get();
 }
@@ -3201,6 +3204,7 @@
   if (route_provider_binding_.is_bound())
     route_provider_binding_.Close();
   associated_interfaces_.reset();
+  process_resource_coordinator_.reset();
   ResetChannelProxy();
 
   UpdateProcessPriority();
diff --git a/content/browser/url_loader_factory_getter.cc b/content/browser/url_loader_factory_getter.cc
index 4cf6d39..d5013516 100644
--- a/content/browser/url_loader_factory_getter.cc
+++ b/content/browser/url_loader_factory_getter.cc
@@ -5,7 +5,6 @@
 #include "content/browser/url_loader_factory_getter.h"
 
 #include "base/bind.h"
-#include "content/browser/appcache/appcache_url_loader_factory.h"
 #include "content/browser/storage_partition_impl.h"
 #include "content/common/network_service.mojom.h"
 
@@ -26,9 +25,7 @@
       BrowserThread::IO, FROM_HERE,
       base::BindOnce(&URLLoaderFactoryGetter::InitializeOnIOThread, this,
                      network_factory.PassInterface(),
-                     blob_factory.PassInterface(),
-                     scoped_refptr<ChromeAppCacheService>(
-                         partition->GetAppCacheService())));
+                     blob_factory.PassInterface()));
 }
 
 mojom::URLLoaderFactoryPtr* URLLoaderFactoryGetter::GetNetworkFactory() {
@@ -52,22 +49,13 @@
                      this, test_factory.PassInterface()));
 }
 
-mojom::URLLoaderFactoryPtr* URLLoaderFactoryGetter::GetAppCacheFactory() {
-  DCHECK_CURRENTLY_ON(BrowserThread::IO);
-  return &appcache_factory_;
-}
-
 URLLoaderFactoryGetter::~URLLoaderFactoryGetter() {}
 
 void URLLoaderFactoryGetter::InitializeOnIOThread(
     mojom::URLLoaderFactoryPtrInfo network_factory,
-    mojom::URLLoaderFactoryPtrInfo blob_factory,
-    scoped_refptr<ChromeAppCacheService> appcache_service) {
+    mojom::URLLoaderFactoryPtrInfo blob_factory) {
   network_factory_.Bind(std::move(network_factory));
   blob_factory_.Bind(std::move(blob_factory));
-
-  AppCacheURLLoaderFactory::CreateURLLoaderFactory(
-      mojo::MakeRequest(&appcache_factory_), appcache_service.get(), this);
 }
 
 void URLLoaderFactoryGetter::SetTestNetworkFactoryOnIOThread(
diff --git a/content/browser/url_loader_factory_getter.h b/content/browser/url_loader_factory_getter.h
index 0af16fd..1ea7281 100644
--- a/content/browser/url_loader_factory_getter.h
+++ b/content/browser/url_loader_factory_getter.h
@@ -13,7 +13,6 @@
 
 namespace content {
 
-class ChromeAppCacheService;
 class StoragePartitionImpl;
 
 // Holds on to URLLoaderFactory for a given StoragePartition and allows code
@@ -43,25 +42,18 @@
   CONTENT_EXPORT void SetNetworkFactoryForTesting(
       mojom::URLLoaderFactoryPtr test_factory);
 
-  // Called on the IO thread to get the URLLoaderFactory for AppCache. The
-  // pointer should not be cached.
-  mojom::URLLoaderFactoryPtr* GetAppCacheFactory();
-
  private:
   friend class base::DeleteHelper<URLLoaderFactoryGetter>;
   friend struct BrowserThread::DeleteOnThread<BrowserThread::IO>;
 
   CONTENT_EXPORT ~URLLoaderFactoryGetter();
-  void InitializeOnIOThread(
-      mojom::URLLoaderFactoryPtrInfo network_factory,
-      mojom::URLLoaderFactoryPtrInfo blob_factory,
-      scoped_refptr<ChromeAppCacheService> appcache_service);
+  void InitializeOnIOThread(mojom::URLLoaderFactoryPtrInfo network_factory,
+                            mojom::URLLoaderFactoryPtrInfo blob_factory);
   void SetTestNetworkFactoryOnIOThread(
       mojom::URLLoaderFactoryPtrInfo test_factory);
 
   // Only accessed on IO thread.
   mojom::URLLoaderFactoryPtr network_factory_;
-  mojom::URLLoaderFactoryPtr appcache_factory_;
   mojom::URLLoaderFactoryPtr blob_factory_;
   mojom::URLLoaderFactoryPtr test_factory_;
 
diff --git a/content/browser/webrtc/webrtc_image_capture_browsertest.cc b/content/browser/webrtc/webrtc_image_capture_browsertest.cc
index 687c325..eb541ba 100644
--- a/content/browser/webrtc/webrtc_image_capture_browsertest.cc
+++ b/content/browser/webrtc/webrtc_image_capture_browsertest.cc
@@ -28,6 +28,14 @@
 #define MAYBE_GetTrackCapabilities DISABLED_GetTrackCapabilities
 #define MAYBE_GetTrackSettings DISABLED_GetTrackSettings
 #define MAYBE_ManipulateZoom DISABLED_ManipulateZoom
+#elif defined(OS_ANDROID)
+#define MAYBE_GetPhotoCapabilities GetPhotoCapabilities
+#define MAYBE_TakePhoto TakePhoto
+#define MAYBE_GrabFrame GrabFrame
+// TODO(mcasas): these fail on Android devices: https://crbug.com/731904.
+#define MAYBE_GetTrackCapabilities DISABLED_GetTrackCapabilities
+#define MAYBE_GetTrackSettings DISABLED_GetTrackSettings
+#define MAYBE_ManipulateZoom DISABLED_ManipulateZoom
 #else
 #define MAYBE_GetPhotoCapabilities GetPhotoCapabilities
 #define MAYBE_TakePhoto TakePhoto
diff --git a/content/common/content_security_policy/content_security_policy.cc b/content/common/content_security_policy/content_security_policy.cc
index 299a4c2..f0e93a50 100644
--- a/content/common/content_security_policy/content_security_policy.cc
+++ b/content/common/content_security_policy/content_security_policy.cc
@@ -15,6 +15,7 @@
   switch (directive) {
     case CSPDirective::DefaultSrc:
     case CSPDirective::FormAction:
+    case CSPDirective::UpgradeInsecureRequests:
       return CSPDirective::Unknown;
 
     case CSPDirective::FrameSrc:
@@ -186,4 +187,14 @@
   return text.str();
 }
 
+// static
+bool ContentSecurityPolicy::ShouldUpgradeInsecureRequest(
+    const ContentSecurityPolicy& policy) {
+  for (const CSPDirective& directive : policy.directives) {
+    if (directive.name == CSPDirective::UpgradeInsecureRequests)
+      return true;
+  }
+  return false;
+}
+
 }  // namespace content
diff --git a/content/common/content_security_policy/content_security_policy.h b/content/common/content_security_policy/content_security_policy.h
index d4de5237..d531b4c 100644
--- a/content/common/content_security_policy/content_security_policy.h
+++ b/content/common/content_security_policy/content_security_policy.h
@@ -45,6 +45,10 @@
                     bool is_redirect,
                     CSPContext* context,
                     const SourceLocation& source_location);
+
+  // Returns true if |policy| specifies that an insecure HTTP request should be
+  // upgraded to HTTPS.
+  static bool ShouldUpgradeInsecureRequest(const ContentSecurityPolicy& policy);
 };
 
 }  // namespace content
diff --git a/content/common/content_security_policy/content_security_policy_unittest.cc b/content/common/content_security_policy/content_security_policy_unittest.cc
index ee9ae79..e14a041 100644
--- a/content/common/content_security_policy/content_security_policy_unittest.cc
+++ b/content/common/content_security_policy/content_security_policy_unittest.cc
@@ -238,4 +238,20 @@
       false, &context, SourceLocation()));
 }
 
+TEST(ContentSecurityPolicy, ShouldUpgradeInsecureRequest) {
+  std::vector<std::string> report_end_points;  // empty
+  CSPSource source("https", "example.com", false, url::PORT_UNSPECIFIED, false,
+                   "");
+  CSPSourceList source_list(false, false, {source});
+  ContentSecurityPolicy policy(
+      EmptyCspHeader(), {CSPDirective(CSPDirective::DefaultSrc, source_list)},
+      report_end_points);
+
+  EXPECT_FALSE(ContentSecurityPolicy::ShouldUpgradeInsecureRequest(policy));
+
+  policy.directives.push_back(
+      CSPDirective(CSPDirective::UpgradeInsecureRequests, CSPSourceList()));
+  EXPECT_TRUE(ContentSecurityPolicy::ShouldUpgradeInsecureRequest(policy));
+}
+
 }  // namespace content
diff --git a/content/common/content_security_policy/csp_context.cc b/content/common/content_security_policy/csp_context.cc
index 194a8a1..7d3aebef 100644
--- a/content/common/content_security_policy/csp_context.cc
+++ b/content/common/content_security_policy/csp_context.cc
@@ -6,6 +6,26 @@
 
 namespace content {
 
+namespace {
+
+// Helper function that returns true if |policy| should be checked under
+// |check_csp_disposition|.
+bool ShouldCheckPolicy(const ContentSecurityPolicy& policy,
+                       CSPContext::CheckCSPDisposition check_csp_disposition) {
+  switch (check_csp_disposition) {
+    case CSPContext::CHECK_REPORT_ONLY_CSP:
+      return policy.header.type == blink::kWebContentSecurityPolicyTypeReport;
+    case CSPContext::CHECK_ENFORCED_CSP:
+      return policy.header.type == blink::kWebContentSecurityPolicyTypeEnforce;
+    case CSPContext::CHECK_ALL_CSP:
+      return true;
+  }
+  NOTREACHED();
+  return true;
+}
+
+}  // namespace
+
 CSPContext::CSPContext() : has_self_(false) {}
 
 CSPContext::~CSPContext() {}
@@ -13,18 +33,41 @@
 bool CSPContext::IsAllowedByCsp(CSPDirective::Name directive_name,
                                 const GURL& url,
                                 bool is_redirect,
-                                const SourceLocation& source_location) {
+                                const SourceLocation& source_location,
+                                CheckCSPDisposition check_csp_disposition) {
   if (SchemeShouldBypassCSP(url.scheme_piece()))
     return true;
 
   bool allow = true;
   for (const auto& policy : policies_) {
-    allow &= ContentSecurityPolicy::Allow(policy, directive_name, url,
-                                          is_redirect, this, source_location);
+    if (ShouldCheckPolicy(policy, check_csp_disposition)) {
+      allow &= ContentSecurityPolicy::Allow(policy, directive_name, url,
+                                            is_redirect, this, source_location);
+    }
   }
   return allow;
 }
 
+bool CSPContext::ShouldModifyRequestUrlForCsp(
+    const GURL& url,
+    bool is_subresource_or_form_submission,
+    GURL* new_url) {
+  for (const auto& policy : policies_) {
+    if (url.scheme() == "http" &&
+        ContentSecurityPolicy::ShouldUpgradeInsecureRequest(policy) &&
+        is_subresource_or_form_submission) {
+      *new_url = url;
+      GURL::Replacements replacements;
+      replacements.SetSchemeStr("https");
+      if (url.port() == "80")
+        replacements.SetPortStr("443");
+      *new_url = new_url->ReplaceComponents(replacements);
+      return true;
+    }
+  }
+  return false;
+}
+
 void CSPContext::SetSelf(const url::Origin origin) {
   if (origin.unique()) {
     // TODO(arthursonzogni): Decide what to do with unique origins.
diff --git a/content/common/content_security_policy/csp_context.h b/content/common/content_security_policy/csp_context.h
index 4cf89b9..51471f43 100644
--- a/content/common/content_security_policy/csp_context.h
+++ b/content/common/content_security_policy/csp_context.h
@@ -24,6 +24,18 @@
 // is in content/browser/frame_host/render_frame_host_impl.h
 class CONTENT_EXPORT CSPContext {
  public:
+  // This enum represents what set of policies should be checked by
+  // IsAllowedByCsp().
+  enum CheckCSPDisposition {
+    // Only check report-only policies.
+    CHECK_REPORT_ONLY_CSP,
+    // Only check enforced policies. (Note that enforced policies can still
+    // trigger reports.)
+    CHECK_ENFORCED_CSP,
+    // Check all policies.
+    CHECK_ALL_CSP,
+  };
+
   CSPContext();
   virtual ~CSPContext();
 
@@ -36,7 +48,15 @@
   bool IsAllowedByCsp(CSPDirective::Name directive_name,
                       const GURL& url,
                       bool is_redirect,
-                      const SourceLocation& source_location);
+                      const SourceLocation& source_location,
+                      CheckCSPDisposition check_csp_disposition);
+
+  // Returns true if the request URL needs to be modified (e.g. upgraded to
+  // HTTPS) according to the CSP. If true, |new_url| will contain the new URL
+  // that should be used instead of |url|.
+  bool ShouldModifyRequestUrlForCsp(const GURL& url,
+                                    bool is_suresource_or_form_submssion,
+                                    GURL* new_url);
 
   void SetSelf(const url::Origin origin);
   bool AllowSelf(const GURL& url);
diff --git a/content/common/content_security_policy/csp_context_unittest.cc b/content/common/content_security_policy/csp_context_unittest.cc
index ce32854..d3a7c16 100644
--- a/content/common/content_security_policy/csp_context_unittest.cc
+++ b/content/common/content_security_policy/csp_context_unittest.cc
@@ -25,6 +25,8 @@
     return scheme_to_bypass_.count(scheme.as_string());
   }
 
+  void ClearViolations() { violations_.clear(); }
+
   void set_sanitize_data_for_use_in_csp_violation(bool value) {
     sanitize_data_for_use_in_csp_violation_ = value;
   }
@@ -70,15 +72,15 @@
   context.AddContentSecurityPolicy(
       BuildPolicy(CSPDirective::DefaultSrc, {source}));
 
-  EXPECT_FALSE(context.IsAllowedByCsp(CSPDirective::FrameSrc,
-                                      GURL("data:text/html,<html></html>"),
-                                      false, SourceLocation()));
+  EXPECT_FALSE(context.IsAllowedByCsp(
+      CSPDirective::FrameSrc, GURL("data:text/html,<html></html>"), false,
+      SourceLocation(), CSPContext::CHECK_ALL_CSP));
 
   context.AddSchemeToBypassCSP("data");
 
-  EXPECT_TRUE(context.IsAllowedByCsp(CSPDirective::FrameSrc,
-                                     GURL("data:text/html,<html></html>"),
-                                     false, SourceLocation()));
+  EXPECT_TRUE(context.IsAllowedByCsp(
+      CSPDirective::FrameSrc, GURL("data:text/html,<html></html>"), false,
+      SourceLocation(), CSPContext::CHECK_ALL_CSP));
 }
 
 TEST(CSPContextTest, MultiplePolicies) {
@@ -95,13 +97,17 @@
       BuildPolicy(CSPDirective::FrameSrc, {source_a, source_c}));
 
   EXPECT_TRUE(context.IsAllowedByCsp(
-      CSPDirective::FrameSrc, GURL("http://a.com"), false, SourceLocation()));
+      CSPDirective::FrameSrc, GURL("http://a.com"), false, SourceLocation(),
+      CSPContext::CHECK_ALL_CSP));
   EXPECT_FALSE(context.IsAllowedByCsp(
-      CSPDirective::FrameSrc, GURL("http://b.com"), false, SourceLocation()));
+      CSPDirective::FrameSrc, GURL("http://b.com"), false, SourceLocation(),
+      CSPContext::CHECK_ALL_CSP));
   EXPECT_FALSE(context.IsAllowedByCsp(
-      CSPDirective::FrameSrc, GURL("http://c.com"), false, SourceLocation()));
+      CSPDirective::FrameSrc, GURL("http://c.com"), false, SourceLocation(),
+      CSPContext::CHECK_ALL_CSP));
   EXPECT_FALSE(context.IsAllowedByCsp(
-      CSPDirective::FrameSrc, GURL("http://d.com"), false, SourceLocation()));
+      CSPDirective::FrameSrc, GURL("http://d.com"), false, SourceLocation(),
+      CSPContext::CHECK_ALL_CSP));
 }
 
 TEST(CSPContextTest, SanitizeDataForUseInCspViolation) {
@@ -120,7 +126,8 @@
   // When the |blocked_url| and |source_location| aren't sensitive information.
   {
     EXPECT_FALSE(context.IsAllowedByCsp(CSPDirective::FrameSrc, blocked_url,
-                                        false, source_location));
+                                        false, source_location,
+                                        CSPContext::CHECK_ALL_CSP));
     ASSERT_EQ(1u, context.violations().size());
     EXPECT_EQ(context.violations()[0].blocked_url, blocked_url);
     EXPECT_EQ(context.violations()[0].source_location.url,
@@ -138,7 +145,8 @@
   // When the |blocked_url| and |source_location| are sensitive information.
   {
     EXPECT_FALSE(context.IsAllowedByCsp(CSPDirective::FrameSrc, blocked_url,
-                                        false, source_location));
+                                        false, source_location,
+                                        CSPContext::CHECK_ALL_CSP));
     ASSERT_EQ(2u, context.violations().size());
     EXPECT_EQ(context.violations()[1].blocked_url, blocked_url.GetOrigin());
     EXPECT_EQ(context.violations()[1].source_location.url, "http://a.com/");
@@ -168,7 +176,8 @@
       BuildPolicy(CSPDirective::FrameSrc, {source_c}));
 
   EXPECT_FALSE(context.IsAllowedByCsp(
-      CSPDirective::FrameSrc, GURL("http://c.com"), false, SourceLocation()));
+      CSPDirective::FrameSrc, GURL("http://c.com"), false, SourceLocation(),
+      CSPContext::CHECK_ALL_CSP));
   ASSERT_EQ(2u, context.violations().size());
   const char console_message_a[] =
       "Refused to frame 'http://c.com/' because it violates the following "
@@ -180,4 +189,88 @@
   EXPECT_EQ(console_message_b, context.violations()[1].console_message);
 }
 
+// Tests that the CheckCSPDisposition parameter is obeyed.
+TEST(CSPContextTest, CheckCSPDisposition) {
+  CSPSource source("", "example.com", false, url::PORT_UNSPECIFIED, false, "");
+  CSPContextTest context;
+
+  // Add an enforced policy.
+  context.AddContentSecurityPolicy(
+      BuildPolicy(CSPDirective::FrameSrc, {source}));
+
+  // Add a report-only policy.
+  ContentSecurityPolicy report_only =
+      BuildPolicy(CSPDirective::DefaultSrc, {source});
+  report_only.header.type = blink::kWebContentSecurityPolicyTypeReport;
+  context.AddContentSecurityPolicy(report_only);
+
+  // With CHECK_ALL_CSP, both policies should be checked and violations should
+  // be reported.
+  EXPECT_FALSE(context.IsAllowedByCsp(
+      CSPDirective::FrameSrc, GURL("https://not-example.com"), false,
+      SourceLocation(), CSPContext::CHECK_ALL_CSP));
+  ASSERT_EQ(2u, context.violations().size());
+  const char console_message_a[] =
+      "Refused to frame 'https://not-example.com/' because it violates the "
+      "following "
+      "Content Security Policy directive: \"frame-src example.com\".\n";
+  const char console_message_b[] =
+      "[Report Only] Refused to frame 'https://not-example.com/' because it "
+      "violates the following "
+      "Content Security Policy directive: \"default-src example.com\". Note "
+      "that 'frame-src' was not explicitly set, so 'default-src' is used as a "
+      "fallback.\n";
+  // Both console messages must appear in the reported violations.
+  EXPECT_TRUE((console_message_a == context.violations()[0].console_message &&
+               console_message_b == context.violations()[1].console_message) ||
+              (console_message_a == context.violations()[1].console_message &&
+               console_message_b == context.violations()[0].console_message));
+
+  // With CHECK_REPORT_ONLY_CSP, the request should be allowed but reported.
+  context.ClearViolations();
+  EXPECT_TRUE(context.IsAllowedByCsp(
+      CSPDirective::FrameSrc, GURL("https://not-example.com"), false,
+      SourceLocation(), CSPContext::CHECK_REPORT_ONLY_CSP));
+  ASSERT_EQ(1u, context.violations().size());
+  EXPECT_EQ(console_message_b, context.violations()[0].console_message);
+
+  // With CHECK_ENFORCED_CSP, the request should be blocked and only the
+  // enforced policy violation should be reported.
+  context.ClearViolations();
+  EXPECT_FALSE(context.IsAllowedByCsp(
+      CSPDirective::FrameSrc, GURL("https://not-example.com"), false,
+      SourceLocation(), CSPContext::CHECK_ENFORCED_CSP));
+  ASSERT_EQ(1u, context.violations().size());
+  EXPECT_EQ(console_message_a, context.violations()[0].console_message);
+}
+
+// Tests HTTP subresources and form submissions have their URLs upgraded when
+// upgrade-insecure-requests is present.
+TEST(CSPContextTest, ShouldModifyRequestUrlForCsp) {
+  CSPContextTest context;
+  context.AddContentSecurityPolicy(BuildPolicy(
+      CSPDirective::UpgradeInsecureRequests, std::vector<CSPSource>()));
+  GURL new_url;
+  // An HTTP subresource or form submission should be upgraded.
+  EXPECT_TRUE(context.ShouldModifyRequestUrlForCsp(GURL("http://example.com"),
+                                                   true, &new_url));
+  EXPECT_EQ(GURL("https://example.com"), new_url);
+  EXPECT_TRUE(context.ShouldModifyRequestUrlForCsp(
+      GURL("http://example.com:80"), true, &new_url));
+  EXPECT_EQ(GURL("https://example.com:443"), new_url);
+  // Non-standard ports should not be modified.
+  EXPECT_TRUE(context.ShouldModifyRequestUrlForCsp(
+      GURL("http://example-weird-port.com:8088"), true, &new_url));
+  EXPECT_EQ(GURL("https://example-weird-port.com:8088"), new_url);
+
+  // Non-HTTP URLs don't need to be modified.
+  EXPECT_FALSE(context.ShouldModifyRequestUrlForCsp(GURL("https://example.com"),
+                                                    true, &new_url));
+  EXPECT_FALSE(context.ShouldModifyRequestUrlForCsp(
+      GURL("data:text/html,<html></html>"), true, &new_url));
+  // Nor do main-frame navigation requests.
+  EXPECT_FALSE(context.ShouldModifyRequestUrlForCsp(GURL("http://example.com"),
+                                                    false, &new_url));
+}
+
 }  // namespace content
diff --git a/content/common/content_security_policy/csp_directive.cc b/content/common/content_security_policy/csp_directive.cc
index 8727e82..d518a4b5 100644
--- a/content/common/content_security_policy/csp_directive.cc
+++ b/content/common/content_security_policy/csp_directive.cc
@@ -28,6 +28,8 @@
       return "frame-src";
     case FormAction:
       return "form-action";
+    case UpgradeInsecureRequests:
+      return "upgrade-insecure-requests";
     case Unknown:
       return "";
   }
@@ -45,6 +47,8 @@
     return CSPDirective::FrameSrc;
   if (name == "form-action")
     return CSPDirective::FormAction;
+  if (name == "upgrade-insecure-requests")
+    return CSPDirective::UpgradeInsecureRequests;
   return CSPDirective::Unknown;
 }
 
diff --git a/content/common/content_security_policy/csp_directive.h b/content/common/content_security_policy/csp_directive.h
index 15c0069..914ab18 100644
--- a/content/common/content_security_policy/csp_directive.h
+++ b/content/common/content_security_policy/csp_directive.h
@@ -26,6 +26,7 @@
     ChildSrc,
     FrameSrc,
     FormAction,
+    UpgradeInsecureRequests,
 
     Unknown,
     NameLast = Unknown,
diff --git a/content/common/throttling_url_loader.cc b/content/common/throttling_url_loader.cc
index aee6e0d..beec7cc 100644
--- a/content/common/throttling_url_loader.cc
+++ b/content/common/throttling_url_loader.cc
@@ -124,7 +124,7 @@
   }
 
   mojom::URLLoaderClientPtr client;
-  client_binding_.Bind(mojo::MakeRequest(&client, std::move(task_runner)));
+  client_binding_.Bind(mojo::MakeRequest(&client), std::move(task_runner));
   factory->CreateLoaderAndStart(mojo::MakeRequest(&url_loader_), routing_id,
                                 request_id, options, *url_request,
                                 std::move(client));
@@ -257,7 +257,7 @@
     case DEFERRED_START: {
       mojom::URLLoaderClientPtr client;
       client_binding_.Bind(
-          mojo::MakeRequest(&client, std::move(start_info_->task_runner)));
+          mojo::MakeRequest(&client), std::move(start_info_->task_runner));
       start_info_->url_loader_factory->CreateLoaderAndStart(
           mojo::MakeRequest(&url_loader_), start_info_->routing_id,
           start_info_->request_id, start_info_->options,
diff --git a/content/content_resources.grd b/content/content_resources.grd
index 7eb6571..e28a8db7 100644
--- a/content/content_resources.grd
+++ b/content/content_resources.grd
@@ -47,6 +47,7 @@
         <include name="IDR_COMMON_SANDBOX_PROFILE" file="common/common.sb" type="BINDATA" />
         <include name="IDR_PPAPI_SANDBOX_PROFILE" file="ppapi_plugin/ppapi.sb" type="BINDATA" />
         <include name="IDR_RENDERER_SANDBOX_PROFILE" file="renderer/renderer.sb" type="BINDATA" />
+        <include name="IDR_RENDERER_SANDBOX_V2_PROFILE" file="renderer/renderer_v2.sb" type="BINDATA" />
         <include name="IDR_UTILITY_SANDBOX_PROFILE" file="utility/utility.sb" type="BINDATA" />
       </if>
       <if expr="not is_ios">
diff --git a/content/renderer/child_frame_compositing_helper.cc b/content/renderer/child_frame_compositing_helper.cc
index 022d3009..8d715a2 100644
--- a/content/renderer/child_frame_compositing_helper.cc
+++ b/content/renderer/child_frame_compositing_helper.cc
@@ -49,14 +49,32 @@
                                 int routing_id)
       : sender_(std::move(sender)), routing_id_(routing_id) {}
 
+  void AddPendingSequence(const cc::SurfaceSequence& sequence) {
+    ReleasePendingSequenceIfNecessary();
+    pending_sequence_ = sequence;
+  }
+
  private:
-  ~IframeSurfaceReferenceFactory() override = default;
+  ~IframeSurfaceReferenceFactory() override {
+    ReleasePendingSequenceIfNecessary();
+  }
+
+  void ReleasePendingSequenceIfNecessary() const {
+    if (pending_sequence_.is_valid()) {
+      sender_->Send(
+          new FrameHostMsg_SatisfySequence(routing_id_, pending_sequence_));
+      pending_sequence_ = cc::SurfaceSequence();
+    }
+  }
 
   // cc::SequenceSurfaceReferenceFactory implementation:
   void RequireSequence(const cc::SurfaceId& surface_id,
                        const cc::SurfaceSequence& sequence) const override {
     sender_->Send(
         new FrameHostMsg_RequireSequence(routing_id_, surface_id, sequence));
+    // If there is a temporary reference that was waiting on a new one to be
+    // created, it is now safe to release it.
+    ReleasePendingSequenceIfNecessary();
   }
 
   void SatisfySequence(const cc::SurfaceSequence& sequence) const override {
@@ -64,6 +82,7 @@
   }
 
   const scoped_refptr<ThreadSafeSender> sender_;
+  mutable cc::SurfaceSequence pending_sequence_;
   const int routing_id_;
 
   DISALLOW_COPY_AND_ASSIGN(IframeSurfaceReferenceFactory);
@@ -79,8 +98,23 @@
         routing_id_(routing_id),
         browser_plugin_instance_id_(browser_plugin_instance_id) {}
 
+  void AddPendingSequence(const cc::SurfaceSequence& sequence) {
+    ReleasePendingSequenceIfNecessary();
+    pending_sequence_ = sequence;
+  }
+
  private:
-  ~BrowserPluginSurfaceReferenceFactory() override = default;
+  ~BrowserPluginSurfaceReferenceFactory() override {
+    ReleasePendingSequenceIfNecessary();
+  }
+
+  void ReleasePendingSequenceIfNecessary() const {
+    if (pending_sequence_.is_valid()) {
+      sender_->Send(new BrowserPluginHostMsg_SatisfySequence(
+          routing_id_, browser_plugin_instance_id_, pending_sequence_));
+      pending_sequence_ = cc::SurfaceSequence();
+    }
+  }
 
   // cc::SequenceSurfaceRefrenceFactory implementation:
   void SatisfySequence(const cc::SurfaceSequence& seq) const override {
@@ -92,9 +126,13 @@
                        const cc::SurfaceSequence& sequence) const override {
     sender_->Send(new BrowserPluginHostMsg_RequireSequence(
         routing_id_, browser_plugin_instance_id_, surface_id, sequence));
+    // If there is a temporary reference that was waiting on a new one to be
+    // created, it is now safe to release it.
+    ReleasePendingSequenceIfNecessary();
   }
 
   const scoped_refptr<ThreadSafeSender> sender_;
+  mutable cc::SurfaceSequence pending_sequence_;
   const int routing_id_;
   const int browser_plugin_instance_id_;
 
@@ -223,6 +261,19 @@
   if (IsUseZoomForDSFEnabled())
     scale_factor = 1.0f;
 
+  // The RWHV creates a destruction dependency on the surface that needs to be
+  // satisfied. The reference factory will satisfy it when a new reference has
+  // been created.
+  if (render_frame_proxy_) {
+    static_cast<IframeSurfaceReferenceFactory*>(
+        surface_reference_factory_.get())
+        ->AddPendingSequence(sequence);
+  } else {
+    static_cast<BrowserPluginSurfaceReferenceFactory*>(
+        surface_reference_factory_.get())
+        ->AddPendingSequence(sequence);
+  }
+
   cc::SurfaceInfo modified_surface_info(surface_info.id(), scale_factor,
                                         surface_info.size_in_pixels());
   surface_layer->SetPrimarySurfaceInfo(modified_surface_info);
@@ -238,17 +289,6 @@
 
   UpdateVisibility(true);
 
-  // The RWHV creates a destruction dependency on the surface that needs to be
-  // satisfied. Note: render_frame_proxy_ is null in the case our client is a
-  // BrowserPlugin; in this case the BrowserPlugin sends its own SatisfySequence
-  // message.
-  if (render_frame_proxy_) {
-    render_frame_proxy_->Send(
-        new FrameHostMsg_SatisfySequence(host_routing_id_, sequence));
-  } else if (browser_plugin_.get()) {
-    browser_plugin_->SendSatisfySequence(sequence);
-  }
-
   CheckSizeAndAdjustLayerProperties(
       surface_info.size_in_pixels(), surface_info.device_scale_factor(),
       static_cast<cc_blink::WebLayerImpl*>(web_layer_.get())->layer());
diff --git a/content/renderer/dom_automation_controller.cc b/content/renderer/dom_automation_controller.cc
index ab675a8..7973ba4e 100644
--- a/content/renderer/dom_automation_controller.cc
+++ b/content/renderer/dom_automation_controller.cc
@@ -12,8 +12,8 @@
 #include "content/renderer/render_view_impl.h"
 #include "gin/handle.h"
 #include "gin/object_template_builder.h"
-#include "third_party/WebKit/public/web/WebFrame.h"
 #include "third_party/WebKit/public/web/WebKit.h"
+#include "third_party/WebKit/public/web/WebLocalFrame.h"
 
 namespace content {
 
@@ -22,7 +22,7 @@
 
 // static
 void DomAutomationController::Install(RenderFrame* render_frame,
-                                      blink::WebFrame* frame) {
+                                      blink::WebLocalFrame* frame) {
   v8::Isolate* isolate = blink::MainThreadIsolate();
   v8::HandleScope handle_scope(isolate);
   v8::Local<v8::Context> context = frame->MainWorldScriptContext();
diff --git a/content/renderer/dom_automation_controller.h b/content/renderer/dom_automation_controller.h
index d684f96..8742ec40 100644
--- a/content/renderer/dom_automation_controller.h
+++ b/content/renderer/dom_automation_controller.h
@@ -74,7 +74,7 @@
 */
 
 namespace blink {
-class WebFrame;
+class WebLocalFrame;
 }
 
 namespace gin {
@@ -90,7 +90,7 @@
  public:
   static gin::WrapperInfo kWrapperInfo;
 
-  static void Install(RenderFrame* render_frame, blink::WebFrame* frame);
+  static void Install(RenderFrame* render_frame, blink::WebLocalFrame* frame);
 
   // Makes the renderer send a javascript value to the app.
   // The value to be sent can be either of type String,
diff --git a/content/renderer/gin_browsertest.cc b/content/renderer/gin_browsertest.cc
index a8639a1..097dfe2 100644
--- a/content/renderer/gin_browsertest.cc
+++ b/content/renderer/gin_browsertest.cc
@@ -11,8 +11,8 @@
 #include "gin/per_isolate_data.h"
 #include "gin/wrappable.h"
 #include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/WebKit/public/web/WebFrame.h"
 #include "third_party/WebKit/public/web/WebKit.h"
+#include "third_party/WebKit/public/web/WebLocalFrame.h"
 #include "third_party/WebKit/public/web/WebView.h"
 
 namespace content {
@@ -65,8 +65,7 @@
   {
     v8::Isolate* isolate = blink::MainThreadIsolate();
     v8::HandleScope handle_scope(isolate);
-    v8::Context::Scope context_scope(
-        view_->GetWebView()->MainFrame()->MainWorldScriptContext());
+    v8::Context::Scope context_scope(GetMainFrame()->MainWorldScriptContext());
 
     // We create the object inside a scope so it's not kept alive by a handle
     // on the stack.
diff --git a/content/renderer/gpu/gpu_benchmarking_extension.cc b/content/renderer/gpu/gpu_benchmarking_extension.cc
index e05aab51..e37cba66 100644
--- a/content/renderer/gpu/gpu_benchmarking_extension.cc
+++ b/content/renderer/gpu/gpu_benchmarking_extension.cc
@@ -544,7 +544,7 @@
 gin::WrapperInfo GpuBenchmarking::kWrapperInfo = {gin::kEmbedderNativeGin};
 
 // static
-void GpuBenchmarking::Install(blink::WebFrame* frame) {
+void GpuBenchmarking::Install(blink::WebLocalFrame* frame) {
   v8::Isolate* isolate = blink::MainThreadIsolate();
   v8::HandleScope handle_scope(isolate);
   v8::Local<v8::Context> context = frame->MainWorldScriptContext();
diff --git a/content/renderer/gpu/gpu_benchmarking_extension.h b/content/renderer/gpu/gpu_benchmarking_extension.h
index 2b69d2b..fbb0022c 100644
--- a/content/renderer/gpu/gpu_benchmarking_extension.h
+++ b/content/renderer/gpu/gpu_benchmarking_extension.h
@@ -9,7 +9,7 @@
 #include "gin/wrappable.h"
 
 namespace blink {
-class WebFrame;
+class WebLocalFrame;
 }
 
 namespace gin {
@@ -27,7 +27,7 @@
 class GpuBenchmarking : public gin::Wrappable<GpuBenchmarking> {
  public:
   static gin::WrapperInfo kWrapperInfo;
-  static void Install(blink::WebFrame* frame);
+  static void Install(blink::WebLocalFrame* frame);
 
  private:
   GpuBenchmarking();
diff --git a/content/renderer/java/gin_java_bridge_object.cc b/content/renderer/java/gin_java_bridge_object.cc
index 95f36382..d747e3c 100644
--- a/content/renderer/java/gin_java_bridge_object.cc
+++ b/content/renderer/java/gin_java_bridge_object.cc
@@ -8,14 +8,14 @@
 #include "content/public/renderer/render_thread.h"
 #include "content/renderer/java/gin_java_function_invocation_helper.h"
 #include "gin/function_template.h"
-#include "third_party/WebKit/public/web/WebFrame.h"
 #include "third_party/WebKit/public/web/WebKit.h"
+#include "third_party/WebKit/public/web/WebLocalFrame.h"
 
 namespace content {
 
 // static
 GinJavaBridgeObject* GinJavaBridgeObject::InjectNamed(
-    blink::WebFrame* frame,
+    blink::WebLocalFrame* frame,
     const base::WeakPtr<GinJavaBridgeDispatcher>& dispatcher,
     const std::string& object_name,
     GinJavaBridgeDispatcher::ObjectID object_id) {
diff --git a/content/renderer/java/gin_java_bridge_object.h b/content/renderer/java/gin_java_bridge_object.h
index 25d1840..23c7efa 100644
--- a/content/renderer/java/gin_java_bridge_object.h
+++ b/content/renderer/java/gin_java_bridge_object.h
@@ -17,7 +17,7 @@
 #include "v8/include/v8-util.h"
 
 namespace blink {
-class WebFrame;
+class WebLocalFrame;
 }
 
 namespace content {
@@ -40,7 +40,7 @@
       v8::Isolate* isolate) override;
 
   static GinJavaBridgeObject* InjectNamed(
-      blink::WebFrame* frame,
+      blink::WebLocalFrame* frame,
       const base::WeakPtr<GinJavaBridgeDispatcher>& dispatcher,
       const std::string& object_name,
       GinJavaBridgeDispatcher::ObjectID object_id);
diff --git a/content/renderer/presentation/presentation_dispatcher.cc b/content/renderer/presentation/presentation_dispatcher.cc
index bdd44dd..2fe4361 100644
--- a/content/renderer/presentation/presentation_dispatcher.cc
+++ b/content/renderer/presentation/presentation_dispatcher.cc
@@ -302,7 +302,7 @@
 
   auto screen_availability = GetScreenAvailability(urls);
   // Reject Promise if screen availability is unsupported for all URLs.
-  if (screen_availability == ScreenAvailability::UNSUPPORTED) {
+  if (screen_availability == blink::mojom::ScreenAvailability::DISABLED) {
     base::ThreadTaskRunnerHandle::Get()->PostTask(
         FROM_HERE,
         base::Bind(
@@ -321,12 +321,13 @@
     availability_set_.insert(base::WrapUnique(listener));
   }
 
-  if (screen_availability != ScreenAvailability::UNKNOWN) {
+  if (screen_availability != blink::mojom::ScreenAvailability::UNKNOWN) {
     base::ThreadTaskRunnerHandle::Get()->PostTask(
         FROM_HERE,
         base::Bind(&blink::WebPresentationAvailabilityCallbacks::OnSuccess,
                    base::Passed(&callback),
-                   screen_availability == ScreenAvailability::AVAILABLE));
+                   screen_availability ==
+                       blink::mojom::ScreenAvailability::AVAILABLE));
   } else {
     listener->availability_callbacks.Add(std::move(callback));
   }
@@ -423,8 +424,9 @@
       blink::WebPresentationConnectionState::kTerminated);
 }
 
-void PresentationDispatcher::OnScreenAvailabilityUpdated(const GURL& url,
-                                                         bool available) {
+void PresentationDispatcher::OnScreenAvailabilityUpdated(
+    const GURL& url,
+    blink::mojom::ScreenAvailability availability) {
   auto* listening_status = GetListeningStatus(url);
   if (!listening_status)
     return;
@@ -432,12 +434,10 @@
   if (listening_status->listening_state == ListeningState::WAITING)
     listening_status->listening_state = ListeningState::ACTIVE;
 
-  auto new_screen_availability = available ? ScreenAvailability::AVAILABLE
-                                           : ScreenAvailability::UNAVAILABLE;
-  if (listening_status->last_known_availability == new_screen_availability)
+  if (listening_status->last_known_availability == availability)
     return;
 
-  listening_status->last_known_availability = new_screen_availability;
+  listening_status->last_known_availability = availability;
 
   std::set<AvailabilityListener*> modified_listeners;
   for (auto& listener : availability_set_) {
@@ -445,17 +445,16 @@
       continue;
 
     auto screen_availability = GetScreenAvailability(listener->urls);
-    DCHECK(screen_availability == ScreenAvailability::AVAILABLE ||
-           screen_availability == ScreenAvailability::UNAVAILABLE);
-    bool is_available = (screen_availability == ScreenAvailability::AVAILABLE);
-
+    DCHECK(screen_availability != blink::mojom::ScreenAvailability::UNKNOWN &&
+           screen_availability != blink::mojom::ScreenAvailability::DISABLED);
     for (auto* observer : listener->availability_observers)
-      observer->AvailabilityChanged(is_available);
+      observer->AvailabilityChanged(screen_availability);
 
     for (AvailabilityCallbacksMap::iterator iter(
              &listener->availability_callbacks);
          !iter.IsAtEnd(); iter.Advance()) {
-      iter.GetCurrentValue()->OnSuccess(is_available);
+      iter.GetCurrentValue()->OnSuccess(
+          screen_availability == blink::mojom::ScreenAvailability::AVAILABLE);
     }
     listener->availability_callbacks.Clear();
 
@@ -478,11 +477,12 @@
     listening_status->listening_state = ListeningState::ACTIVE;
 
   if (listening_status->last_known_availability ==
-      ScreenAvailability::UNSUPPORTED) {
+      blink::mojom::ScreenAvailability::DISABLED) {
     return;
   }
 
-  listening_status->last_known_availability = ScreenAvailability::UNSUPPORTED;
+  listening_status->last_known_availability =
+      blink::mojom::ScreenAvailability::DISABLED;
 
   const blink::WebString& not_supported_error = blink::WebString::FromUTF8(
       "getAvailability() isn't supported at the moment. It can be due to "
@@ -496,10 +496,15 @@
 
     // ScreenAvailabilityNotSupported should be a browser side setting, which
     // means all urls in PresentationAvailability should report NotSupported.
-    // It is not possible to change listening status from Available or
-    // Unavailable to NotSupported. No need to update observer.
+    // It is not possible to change listening status from AVAILABLE or
+    // UNAVAILABLE to DISABLED.
     auto screen_availability = GetScreenAvailability(listener->urls);
-    DCHECK_EQ(screen_availability, ScreenAvailability::UNSUPPORTED);
+    DCHECK_EQ(screen_availability, blink::mojom::ScreenAvailability::DISABLED);
+
+    // RemotePlayback is using a listener but doesn't use callbacks.
+    // So update observers even though it's not necessary for Presentation API.
+    for (auto* observer : listener->availability_observers)
+      observer->AvailabilityChanged(screen_availability);
 
     for (AvailabilityCallbacksMap::iterator iter(
              &listener->availability_callbacks);
@@ -730,23 +735,38 @@
   }
 }
 
-// Given a screen availability vector and integer value for each availability:
-// UNKNOWN = 0, UNAVAILABLE = 1, UNSUPPORTED = 2, and AVAILABLE = 3, the max
-// value of the vector is the overall availability.
-PresentationDispatcher::ScreenAvailability
-PresentationDispatcher::GetScreenAvailability(
+blink::mojom::ScreenAvailability PresentationDispatcher::GetScreenAvailability(
     const std::vector<GURL>& urls) const {
-  int current_availability = 0;  // UNKNOWN;
+  bool has_disabled = false;
+  bool has_source_not_supported = false;
+  bool has_unavailable = false;
 
   for (const auto& url : urls) {
     auto* status = GetListeningStatus(url);
-    auto screen_availability =
-        status ? status->last_known_availability : ScreenAvailability::UNKNOWN;
-    current_availability =
-        std::max(current_availability, static_cast<int>(screen_availability));
+    auto screen_availability = status
+                                   ? status->last_known_availability
+                                   : blink::mojom::ScreenAvailability::UNKNOWN;
+    if (screen_availability == blink::mojom::ScreenAvailability::AVAILABLE) {
+      return blink::mojom::ScreenAvailability::AVAILABLE;
+    } else if (screen_availability ==
+               blink::mojom::ScreenAvailability::DISABLED) {
+      has_disabled = true;
+    } else if (screen_availability ==
+               blink::mojom::ScreenAvailability::SOURCE_NOT_SUPPORTED) {
+      has_source_not_supported = true;
+    } else if (screen_availability ==
+               blink::mojom::ScreenAvailability::UNAVAILABLE) {
+      has_unavailable = true;
+    }
   }
 
-  return static_cast<ScreenAvailability>(current_availability);
+  if (has_disabled)
+    return blink::mojom::ScreenAvailability::DISABLED;
+  if (has_source_not_supported)
+    return blink::mojom::ScreenAvailability::SOURCE_NOT_SUPPORTED;
+  if (has_unavailable)
+    return blink::mojom::ScreenAvailability::UNAVAILABLE;
+  return blink::mojom::ScreenAvailability::UNKNOWN;
 }
 
 PresentationDispatcher::SendMessageRequest::SendMessageRequest(
@@ -799,7 +819,7 @@
 PresentationDispatcher::ListeningStatus::ListeningStatus(
     const GURL& availability_url)
     : url(availability_url),
-      last_known_availability(ScreenAvailability::UNKNOWN),
+      last_known_availability(blink::mojom::ScreenAvailability::UNKNOWN),
       listening_state(ListeningState::INACTIVE) {}
 
 PresentationDispatcher::ListeningStatus::~ListeningStatus() {}
diff --git a/content/renderer/presentation/presentation_dispatcher.h b/content/renderer/presentation/presentation_dispatcher.h
index e717cb1..57721b0 100644
--- a/content/renderer/presentation/presentation_dispatcher.h
+++ b/content/renderer/presentation/presentation_dispatcher.h
@@ -159,7 +159,9 @@
 
   // blink::mojom::PresentationServiceClient
   void OnScreenAvailabilityNotSupported(const GURL& url) override;
-  void OnScreenAvailabilityUpdated(const GURL& url, bool available) override;
+  void OnScreenAvailabilityUpdated(
+      const GURL& url,
+      blink::mojom::ScreenAvailability availability) override;
   void OnConnectionStateChanged(const PresentationInfo& presentation_info,
                                 PresentationConnectionState state) override;
   void OnConnectionClosed(const PresentationInfo& presentation_info,
@@ -219,15 +221,6 @@
     ACTIVE,
   };
 
-  // Do not change order or add new enum values. |GetScreenAvailability| impl
-  // depends on the order of the enum values.
-  enum class ScreenAvailability {
-    UNKNOWN = 0,
-    UNAVAILABLE,
-    UNSUPPORTED,
-    AVAILABLE
-  };
-
   using AvailabilityCallbacksMap =
       IDMap<std::unique_ptr<blink::WebPresentationAvailabilityCallbacks>>;
   using AvailabilityObserversSet =
@@ -250,7 +243,7 @@
     ~ListeningStatus();
 
     const GURL url;
-    ScreenAvailability last_known_availability;
+    blink::mojom::ScreenAvailability last_known_availability;
     ListeningState listening_state;
   };
 
@@ -279,13 +272,15 @@
   void TryRemoveAvailabilityListener(AvailabilityListener* listener);
 
   // Returns AVAILABLE if any url in |urls| has screen availability AVAILABLE;
-  // Returns UNSUPPORTED if any url in |urls| have screen availability
-  // UNSUPPORTED, and no url has screen availability AVAILABLE;
-  // Returns UNAVAILABLE if at least one url in |urls| has screen availability
-  // UNAVAILABLE, and no url has screen availability AVAILABLE or UNSUPPORTED;
-  // Returns UNKNOWN if all urls in |urls| have screen availability
-  // UNKNOWN.
-  ScreenAvailability GetScreenAvailability(const std::vector<GURL>& urls) const;
+  // otherwise returns DISABLED if at least one url in |urls| has screen
+  // availability DISABLED;
+  // otherwise, returns SOURCE_NOT_SUPPORTED if any url in |urls| has screen
+  // availability SOURCE_NOT_SUPPORTED;
+  // otherwise, returns UNAVAILABLE if any url in |urls| has screen
+  // availability UNAVAILABLE;
+  // otherwise returns UNKNOWN.
+  blink::mojom::ScreenAvailability GetScreenAvailability(
+      const std::vector<GURL>& urls) const;
 
   DISALLOW_COPY_AND_ASSIGN(PresentationDispatcher);
 };
diff --git a/content/renderer/presentation/presentation_dispatcher_unittest.cc b/content/renderer/presentation/presentation_dispatcher_unittest.cc
index 0c4651f5..aadc61e 100644
--- a/content/renderer/presentation/presentation_dispatcher_unittest.cc
+++ b/content/renderer/presentation/presentation_dispatcher_unittest.cc
@@ -35,6 +35,7 @@
 using blink::mojom::PresentationConnection;
 using blink::mojom::PresentationService;
 using blink::mojom::PresentationServiceClientPtr;
+using blink::mojom::ScreenAvailability;
 
 // TODO(crbug.com/576808): Add test cases for the following:
 // - State changes
@@ -52,7 +53,7 @@
       : urls_(urls) {}
   ~MockPresentationAvailabilityObserver() override {}
 
-  MOCK_METHOD1(AvailabilityChanged, void(bool is_available));
+  MOCK_METHOD1(AvailabilityChanged, void(ScreenAvailability availability));
   const WebVector<WebURL>& Urls() const override { return urls_; }
 
  private:
@@ -211,8 +212,6 @@
  public:
   using OnMessageCallback = PresentationConnectionProxy::OnMessageCallback;
 
-  enum class URLState { Available, Unavailable, Unsupported, Unknown };
-
   PresentationDispatcherTest()
       : gurl1_(GURL("https://www.example.com/1.html")),
         gurl2_(GURL("https://www.example.com/2.html")),
@@ -244,18 +243,24 @@
     return static_cast<uint8_t*>(array_buffer_.Data());
   }
 
-  void ChangeURLState(const GURL& url, URLState state) {
+  void ChangeURLState(const GURL& url, ScreenAvailability state) {
     switch (state) {
-      case URLState::Available:
-        dispatcher_.OnScreenAvailabilityUpdated(url, true);
+      case ScreenAvailability::AVAILABLE:
+        dispatcher_.OnScreenAvailabilityUpdated(url,
+                                                ScreenAvailability::AVAILABLE);
         break;
-      case URLState::Unavailable:
-        dispatcher_.OnScreenAvailabilityUpdated(url, false);
+      case ScreenAvailability::SOURCE_NOT_SUPPORTED:
+        dispatcher_.OnScreenAvailabilityUpdated(
+            url, ScreenAvailability::SOURCE_NOT_SUPPORTED);
         break;
-      case URLState::Unsupported:
+      case ScreenAvailability::UNAVAILABLE:
+        dispatcher_.OnScreenAvailabilityUpdated(
+            url, ScreenAvailability::UNAVAILABLE);
+        break;
+      case ScreenAvailability::DISABLED:
         dispatcher_.OnScreenAvailabilityNotSupported(url);
         break;
-      case URLState::Unknown:
+      case ScreenAvailability::UNKNOWN:
         break;
     }
   }
@@ -265,7 +270,7 @@
   // |mock_callback|.
   void TestGetAvailability(
       const std::vector<GURL>& urls,
-      const std::vector<URLState>& states,
+      const std::vector<ScreenAvailability>& states,
       MockPresentationAvailabilityCallbacks* mock_callback) {
     DCHECK_EQ(urls.size(), states.size());
 
@@ -534,7 +539,7 @@
 
   dispatcher_.GetAvailability(
       urls_, base::MakeUnique<WebPresentationAvailabilityCallbacks>());
-  dispatcher_.OnScreenAvailabilityUpdated(url1_, true);
+  dispatcher_.OnScreenAvailabilityUpdated(url1_, ScreenAvailability::AVAILABLE);
   run_loop1.RunUntilIdle();
 
   base::RunLoop run_loop2;
@@ -545,10 +550,15 @@
   run_loop2.RunUntilIdle();
 
   base::RunLoop run_loop3;
-  EXPECT_CALL(observer_, AvailabilityChanged(false));
-  dispatcher_.OnScreenAvailabilityUpdated(url1_, false);
-  EXPECT_CALL(observer_, AvailabilityChanged(true));
-  dispatcher_.OnScreenAvailabilityUpdated(url1_, true);
+  EXPECT_CALL(observer_, AvailabilityChanged(ScreenAvailability::UNAVAILABLE));
+  dispatcher_.OnScreenAvailabilityUpdated(url1_,
+                                          ScreenAvailability::UNAVAILABLE);
+  EXPECT_CALL(observer_,
+              AvailabilityChanged(ScreenAvailability::SOURCE_NOT_SUPPORTED));
+  dispatcher_.OnScreenAvailabilityUpdated(
+      url1_, ScreenAvailability::SOURCE_NOT_SUPPORTED);
+  EXPECT_CALL(observer_, AvailabilityChanged(ScreenAvailability::AVAILABLE));
+  dispatcher_.OnScreenAvailabilityUpdated(url1_, ScreenAvailability::AVAILABLE);
   for (const auto& gurl : gurls_) {
     EXPECT_CALL(presentation_service_,
                 StopListeningForScreenAvailability(gurl));
@@ -558,8 +568,10 @@
 
   // After stopListening(), |observer_| should no longer be notified.
   base::RunLoop run_loop4;
-  EXPECT_CALL(observer_, AvailabilityChanged(false)).Times(0);
-  dispatcher_.OnScreenAvailabilityUpdated(url1_, false);
+  EXPECT_CALL(observer_, AvailabilityChanged(ScreenAvailability::UNAVAILABLE))
+      .Times(0);
+  dispatcher_.OnScreenAvailabilityUpdated(url1_,
+                                          ScreenAvailability::UNAVAILABLE);
   run_loop4.RunUntilIdle();
 }
 
@@ -587,21 +599,30 @@
   auto* mock_callback = new MockPresentationAvailabilityCallbacks();
   EXPECT_CALL(*mock_callback, OnSuccess(true));
 
-  TestGetAvailability({url1_}, {URLState::Available}, mock_callback);
+  TestGetAvailability({url1_}, {ScreenAvailability::AVAILABLE}, mock_callback);
+}
+
+TEST_F(PresentationDispatcherTest, GetAvailabilityOneUrlBecomesNotCompatible) {
+  auto* mock_callback = new MockPresentationAvailabilityCallbacks();
+  EXPECT_CALL(*mock_callback, OnSuccess(false));
+
+  TestGetAvailability({url1_}, {ScreenAvailability::SOURCE_NOT_SUPPORTED},
+                      mock_callback);
 }
 
 TEST_F(PresentationDispatcherTest, GetAvailabilityOneUrlBecomesUnavailable) {
   auto* mock_callback = new MockPresentationAvailabilityCallbacks();
   EXPECT_CALL(*mock_callback, OnSuccess(false));
 
-  TestGetAvailability({url1_}, {URLState::Unavailable}, mock_callback);
+  TestGetAvailability({url1_}, {ScreenAvailability::UNAVAILABLE},
+                      mock_callback);
 }
 
-TEST_F(PresentationDispatcherTest, GetAvailabilityOneUrlBecomesNotSupported) {
+TEST_F(PresentationDispatcherTest, GetAvailabilityOneUrlBecomesUnsupported) {
   auto* mock_callback = new MockPresentationAvailabilityCallbacks();
   EXPECT_CALL(*mock_callback, OnError(_));
 
-  TestGetAvailability({url1_}, {URLState::Unsupported}, mock_callback);
+  TestGetAvailability({url1_}, {ScreenAvailability::DISABLED}, mock_callback);
 }
 
 TEST_F(PresentationDispatcherTest,
@@ -609,9 +630,10 @@
   auto* mock_callback = new MockPresentationAvailabilityCallbacks();
   EXPECT_CALL(*mock_callback, OnSuccess(true)).Times(1);
 
-  TestGetAvailability({url1_, url2_},
-                      {URLState::Available, URLState::Available},
-                      mock_callback);
+  TestGetAvailability(
+      {url1_, url2_},
+      {ScreenAvailability::AVAILABLE, ScreenAvailability::AVAILABLE},
+      mock_callback);
 }
 
 TEST_F(PresentationDispatcherTest,
@@ -619,8 +641,20 @@
   auto* mock_callback = new MockPresentationAvailabilityCallbacks();
   EXPECT_CALL(*mock_callback, OnSuccess(false)).Times(1);
 
+  TestGetAvailability(
+      {url1_, url2_},
+      {ScreenAvailability::UNAVAILABLE, ScreenAvailability::UNAVAILABLE},
+      mock_callback);
+}
+
+TEST_F(PresentationDispatcherTest,
+       GetAvailabilityMultipleUrlsAllBecomesNotCompatible) {
+  auto* mock_callback = new MockPresentationAvailabilityCallbacks();
+  EXPECT_CALL(*mock_callback, OnSuccess(false)).Times(1);
+
   TestGetAvailability({url1_, url2_},
-                      {URLState::Unavailable, URLState::Unavailable},
+                      {ScreenAvailability::SOURCE_NOT_SUPPORTED,
+                       ScreenAvailability::SOURCE_NOT_SUPPORTED},
                       mock_callback);
 }
 
@@ -629,9 +663,10 @@
   auto* mock_callback = new MockPresentationAvailabilityCallbacks();
   EXPECT_CALL(*mock_callback, OnError(_)).Times(1);
 
-  TestGetAvailability({url1_, url2_},
-                      {URLState::Unsupported, URLState::Unsupported},
-                      mock_callback);
+  TestGetAvailability(
+      {url1_, url2_},
+      {ScreenAvailability::DISABLED, ScreenAvailability::DISABLED},
+      mock_callback);
 }
 
 TEST_F(PresentationDispatcherTest,
@@ -640,8 +675,9 @@
   auto* mock_callback_1 = new MockPresentationAvailabilityCallbacks();
   EXPECT_CALL(*mock_callback_1, OnSuccess(false)).Times(1);
 
-  std::vector<URLState> state_seq = {URLState::Unavailable, URLState::Available,
-                                     URLState::Unavailable};
+  std::vector<ScreenAvailability> state_seq = {ScreenAvailability::UNAVAILABLE,
+                                               ScreenAvailability::AVAILABLE,
+                                               ScreenAvailability::UNAVAILABLE};
   TestGetAvailability({url1_, url2_, url3_}, state_seq, mock_callback_1);
 
   // Second getAvailability() call.
@@ -682,9 +718,12 @@
         .Times(1);
   }
 
-  EXPECT_CALL(mock_observer1_, AvailabilityChanged(false));
-  EXPECT_CALL(mock_observer2_, AvailabilityChanged(false));
-  EXPECT_CALL(mock_observer3_, AvailabilityChanged(false));
+  EXPECT_CALL(mock_observer1_,
+              AvailabilityChanged(ScreenAvailability::UNAVAILABLE));
+  EXPECT_CALL(mock_observer2_,
+              AvailabilityChanged(ScreenAvailability::UNAVAILABLE));
+  EXPECT_CALL(mock_observer3_,
+              AvailabilityChanged(ScreenAvailability::UNAVAILABLE));
 
   // Set up |availability_set_| and |listening_status_|
   base::RunLoop run_loop;
@@ -697,7 +736,7 @@
   }
 
   // Clean up callbacks.
-  ChangeURLState(gurl2_, URLState::Unavailable);
+  ChangeURLState(gurl2_, ScreenAvailability::UNAVAILABLE);
 
   for (auto* mock_observer : mock_observers_)
     client()->StopListening(mock_observer);
@@ -729,12 +768,15 @@
   for (auto* mock_observer : mock_observers_)
     client()->StartListening(mock_observer);
 
-  EXPECT_CALL(mock_observer1_, AvailabilityChanged(false));
-  EXPECT_CALL(mock_observer2_, AvailabilityChanged(false));
-  EXPECT_CALL(mock_observer3_, AvailabilityChanged(false));
+  EXPECT_CALL(mock_observer1_,
+              AvailabilityChanged(ScreenAvailability::UNAVAILABLE));
+  EXPECT_CALL(mock_observer2_,
+              AvailabilityChanged(ScreenAvailability::UNAVAILABLE));
+  EXPECT_CALL(mock_observer3_,
+              AvailabilityChanged(ScreenAvailability::UNAVAILABLE));
 
   // Clean up callbacks.
-  ChangeURLState(gurl2_, URLState::Unavailable);
+  ChangeURLState(gurl2_, ScreenAvailability::UNAVAILABLE);
   client()->StopListening(&mock_observer1_);
   run_loop.RunUntilIdle();
 }
@@ -745,7 +787,8 @@
     EXPECT_CALL(presentation_service_, ListenForScreenAvailability(gurl))
         .Times(1);
   }
-  EXPECT_CALL(mock_observer1_, AvailabilityChanged(true));
+  EXPECT_CALL(mock_observer1_,
+              AvailabilityChanged(ScreenAvailability::AVAILABLE));
 
   base::RunLoop run_loop;
   for (auto* mock_observer : mock_observers_) {
@@ -755,14 +798,22 @@
     client()->StartListening(mock_observer);
   }
 
-  ChangeURLState(gurl1_, URLState::Available);
+  ChangeURLState(gurl1_, ScreenAvailability::AVAILABLE);
   run_loop.RunUntilIdle();
 
-  EXPECT_CALL(mock_observer1_, AvailabilityChanged(false));
+  EXPECT_CALL(mock_observer1_,
+              AvailabilityChanged(ScreenAvailability::UNAVAILABLE));
 
   base::RunLoop run_loop_2;
-  ChangeURLState(gurl1_, URLState::Unavailable);
+  ChangeURLState(gurl1_, ScreenAvailability::UNAVAILABLE);
   run_loop_2.RunUntilIdle();
+
+  EXPECT_CALL(mock_observer1_,
+              AvailabilityChanged(ScreenAvailability::SOURCE_NOT_SUPPORTED));
+
+  base::RunLoop run_loop_3;
+  ChangeURLState(gurl1_, ScreenAvailability::SOURCE_NOT_SUPPORTED);
+  run_loop_3.RunUntilIdle();
 }
 
 TEST_F(PresentationDispatcherTest,
@@ -771,8 +822,10 @@
     EXPECT_CALL(presentation_service_, ListenForScreenAvailability(gurl))
         .Times(1);
   }
-  for (auto* mock_observer : mock_observers_)
-    EXPECT_CALL(*mock_observer, AvailabilityChanged(true));
+  for (auto* mock_observer : mock_observers_) {
+    EXPECT_CALL(*mock_observer,
+                AvailabilityChanged(ScreenAvailability::AVAILABLE));
+  }
 
   base::RunLoop run_loop;
   for (auto* mock_observer : mock_observers_) {
@@ -782,15 +835,26 @@
     client()->StartListening(mock_observer);
   }
 
-  ChangeURLState(gurl2_, URLState::Available);
+  ChangeURLState(gurl2_, ScreenAvailability::AVAILABLE);
   run_loop.RunUntilIdle();
 
-  for (auto* mock_observer : mock_observers_)
-    EXPECT_CALL(*mock_observer, AvailabilityChanged(false));
+  for (auto* mock_observer : mock_observers_) {
+    EXPECT_CALL(*mock_observer,
+                AvailabilityChanged(ScreenAvailability::UNAVAILABLE));
+  }
 
   base::RunLoop run_loop_2;
-  ChangeURLState(gurl2_, URLState::Unavailable);
+  ChangeURLState(gurl2_, ScreenAvailability::UNAVAILABLE);
   run_loop_2.RunUntilIdle();
+
+  for (auto* mock_observer : mock_observers_) {
+    EXPECT_CALL(*mock_observer,
+                AvailabilityChanged(ScreenAvailability::SOURCE_NOT_SUPPORTED));
+  }
+
+  base::RunLoop run_loop_3;
+  ChangeURLState(gurl2_, ScreenAvailability::SOURCE_NOT_SUPPORTED);
+  run_loop_3.RunUntilIdle();
 }
 
 }  // namespace content
diff --git a/content/renderer/renderer_v2.sb b/content/renderer/renderer_v2.sb
new file mode 100644
index 0000000..cb871b0
--- /dev/null
+++ b/content/renderer/renderer_v2.sb
@@ -0,0 +1,144 @@
+; Copyright 2017 The Chromium Authors. All rights reserved.
+; Use of this source code is governed by a BSD-style license that can be
+; found in the LICENSE file.
+(version 1)
+
+; The top of this will be the V2 common profile.
+
+; Helper function to check if a param is set to true.
+(define (param-true? str) (string=? (param str) "TRUE"))
+
+; Helper function to determine if a parameter is defined or not.
+(define (param-defined? str) (string? (param str)))
+
+; Define constants for all of the parameter strings passed in.
+(define disable-sandbox-denial-logging "DISABLE_SANDBOX_DENIAL_LOGGING")
+(define enable-logging "ENABLE_LOGGING")
+(define homedir-as-literal "USER_HOMEDIR_AS_LITERAL")
+(define elcap-or-later "ELCAP_OR_LATER")
+(define bundle-path "BUNDLE_PATH")
+(define executable-path "EXECUTABLE_PATH")
+(define chromium-pid "CHROMIUM_PID")
+(define log-file-path "LOG_FILE_PATH")
+(define bundle-id "BUNDLE_ID")
+(define component-path "COMPONENT_PATH")
+
+; Backwards compatibility for 10.9
+(define (path x) (literal x))
+
+; --enable-sandbox-logging causes the sandbox to log failures to the syslog.
+(if (param-true? disable-sandbox-denial-logging)
+   (deny default (with no-log))
+   (deny default))
+
+(if (param-true? enable-logging) (debug deny))
+
+; Allow sending signals to self - https://crbug.com/20370
+(allow signal (target self))
+
+; Consumes a subpath and appends it to the user's homedir path.
+(define (user-homedir-path subpath) 
+  (string-append (param homedir-as-literal) subpath))
+
+; Allow logging for all processes.
+(allow file-write* (path (param log-file-path)))
+
+; Allow component builds to work.
+(if (param-defined? component-path)
+  (allow file-read* (subpath (param component-path))))
+
+(allow process-exec* (path (param executable-path)))
+(allow file-read* (path (param executable-path)))
+
+(allow mach-lookup (global-name (string-append (param bundle-id)
+                                               ".rohitfork."
+                                               (param chromium-pid))))
+; Allow realpath() to work.
+(allow file-read-metadata (subpath "/"))
+
+; Allow cf prefs to work.
+(allow user-preference-read)
+
+; All processes can read the bundle contents.
+(allow file-read* (subpath (param bundle-path)))
+
+; End of common.sb?
+(allow file-ioctl file-read-data file-write-data (path "/dev/dtracehelper"))
+
+; File reads.
+; Reads from the home directory.
+(allow file-read-data (path (user-homedir-path "/.CFUserTextEncoding")))
+
+; Reads of /dev devices.
+(allow file-read-data 
+  (path "/dev/autofs_nowait")
+  (path "/dev/fd")
+  (path "/dev/null")
+  (path "/dev/urandom"))
+
+(allow file-write-data (path "/dev/null"))
+
+; Reads from /usr.
+(allow file-read-data
+  (path "/usr/lib/libexpat.1.dylib")
+  (subpath "/usr/share/locale")
+  (subpath "/usr/share/zoneinfo"))
+
+(allow file-read* (path "/usr/share/icu/icudt57l.dat"))
+
+; Reads from /Library.
+(allow file-read-data (subpath "/Library/Fonts"))
+
+; Reads from /System.
+(allow file-read-data 
+  (path "/System/Library/CoreServices/CoreTypes.bundle/Contents/Library/AppExceptions.bundle/Exceptions.plist")
+  (path "/System/Library/CoreServices/CoreTypes.bundle/Contents/Resources/Exceptions.plist")
+  (path "/System/Library/CoreServices/SystemVersion.plist")
+  (path "/System/Library/Preferences/Logging/Subsystems/com.apple.SkyLight.plist")
+  (subpath "/System/Library/ColorSync/Profiles")
+  (subpath "/System/Library/CoreServices/SystemAppearance.bundle")
+  (subpath "/System/Library/CoreServices/SystemVersion.bundle")
+  (subpath "/System/Library/Fonts")
+  (subpath "/System/Library/Frameworks"))
+
+; IOKit
+(allow iokit-open
+  (iokit-registry-entry-class "IOSurfaceRootUserClient")
+  (iokit-registry-entry-class "RootDomainUserClient"))
+
+; POSIX IPC
+(allow ipc-posix-shm-read-data 
+  (ipc-posix-name "apple.cfprefs.317580v1")
+  (ipc-posix-name "apple.cfprefs.daemonv1")
+  (ipc-posix-name "apple.shm.notification_center"))
+
+; mach IPC
+(allow mach-lookup 
+  (global-name "com.apple.distributed_notifications@Uv3")
+  (global-name "com.apple.fonts")
+  (global-name "com.apple.logd")
+  (global-name "com.apple.system.logger")
+  (global-name "com.apple.system.notification_center")
+  (global-name "com.apple.system.opendirectoryd.libinfo")
+  (global-name "com.apple.windowserver.active"))
+
+; sysctl
+(allow sysctl-read
+  (sysctl-name "hw.activecpu")
+  (sysctl-name "hw.busfrequency_compat")
+  (sysctl-name "hw.byteorder")
+  (sysctl-name "hw.cachelinesize_compat")
+  (sysctl-name "hw.cpufrequency_compat")
+  (sysctl-name "hw.cputype")
+  (sysctl-name "hw.machine")
+  (sysctl-name "hw.ncpu")
+  (sysctl-name "hw.pagesize_compat")
+  (sysctl-name "hw.physicalcpu_max")
+  (sysctl-name "hw.tbfrequency_compat")
+  (sysctl-name "hw.vectorunit")
+  (sysctl-name "kern.hostname")
+  (sysctl-name "kern.maxfilesperproc")
+  (sysctl-name "kern.osrelease")
+  (sysctl-name "kern.ostype")
+  (sysctl-name "kern.osversion")
+  (sysctl-name "kern.version"))
diff --git a/content/renderer/skia_benchmarking_extension.cc b/content/renderer/skia_benchmarking_extension.cc
index dfbf9afe..b034452 100644
--- a/content/renderer/skia_benchmarking_extension.cc
+++ b/content/renderer/skia_benchmarking_extension.cc
@@ -21,8 +21,8 @@
 #include "skia/ext/benchmarking_canvas.h"
 #include "third_party/WebKit/public/web/WebArrayBuffer.h"
 #include "third_party/WebKit/public/web/WebArrayBufferConverter.h"
-#include "third_party/WebKit/public/web/WebFrame.h"
 #include "third_party/WebKit/public/web/WebKit.h"
+#include "third_party/WebKit/public/web/WebLocalFrame.h"
 #include "third_party/skia/include/core/SkCanvas.h"
 #include "third_party/skia/include/core/SkColorPriv.h"
 #include "third_party/skia/include/core/SkGraphics.h"
@@ -144,7 +144,7 @@
 gin::WrapperInfo SkiaBenchmarking::kWrapperInfo = {gin::kEmbedderNativeGin};
 
 // static
-void SkiaBenchmarking::Install(blink::WebFrame* frame) {
+void SkiaBenchmarking::Install(blink::WebLocalFrame* frame) {
   v8::Isolate* isolate = blink::MainThreadIsolate();
   v8::HandleScope handle_scope(isolate);
   v8::Local<v8::Context> context = frame->MainWorldScriptContext();
diff --git a/content/renderer/skia_benchmarking_extension.h b/content/renderer/skia_benchmarking_extension.h
index 8dd05dd5..43bfbfbb 100644
--- a/content/renderer/skia_benchmarking_extension.h
+++ b/content/renderer/skia_benchmarking_extension.h
@@ -9,7 +9,7 @@
 #include "gin/wrappable.h"
 
 namespace blink {
-class WebFrame;
+class WebLocalFrame;
 }
 
 namespace gin {
@@ -21,7 +21,7 @@
 class SkiaBenchmarking : public gin::Wrappable<SkiaBenchmarking> {
  public:
   static gin::WrapperInfo kWrapperInfo;
-  static void Install(blink::WebFrame* frame);
+  static void Install(blink::WebLocalFrame* frame);
 
   // Wrapper around SkGraphics::Init that can be invoked multiple times.
   static void Initialize();
diff --git a/content/renderer/stats_collection_controller.cc b/content/renderer/stats_collection_controller.cc
index 618c6fc..16eb0f83 100644
--- a/content/renderer/stats_collection_controller.cc
+++ b/content/renderer/stats_collection_controller.cc
@@ -78,7 +78,7 @@
 };
 
 // static
-void StatsCollectionController::Install(blink::WebFrame* frame) {
+void StatsCollectionController::Install(blink::WebLocalFrame* frame) {
   v8::Isolate* isolate = blink::MainThreadIsolate();
   v8::HandleScope handle_scope(isolate);
   v8::Local<v8::Context> context = frame->MainWorldScriptContext();
diff --git a/content/renderer/stats_collection_controller.h b/content/renderer/stats_collection_controller.h
index df297c1..848e102a7 100644
--- a/content/renderer/stats_collection_controller.h
+++ b/content/renderer/stats_collection_controller.h
@@ -9,7 +9,7 @@
 #include "gin/wrappable.h"
 
 namespace blink {
-class WebFrame;
+class WebLocalFrame;
 }
 
 namespace content {
@@ -23,7 +23,7 @@
  public:
   static gin::WrapperInfo kWrapperInfo;
 
-  static void Install(blink::WebFrame* frame);
+  static void Install(blink::WebLocalFrame* frame);
 
  private:
   StatsCollectionController();
diff --git a/content/renderer/web_ui_extension.cc b/content/renderer/web_ui_extension.cc
index 2a98fa0..19593e17 100644
--- a/content/renderer/web_ui_extension.cc
+++ b/content/renderer/web_ui_extension.cc
@@ -32,10 +32,9 @@
 
 namespace {
 
-bool ShouldRespondToRequest(
-    blink::WebFrame** frame_ptr,
-    RenderView** render_view_ptr) {
-  blink::WebFrame* frame = blink::WebLocalFrame::FrameForCurrentContext();
+bool ShouldRespondToRequest(blink::WebLocalFrame** frame_ptr,
+                            RenderView** render_view_ptr) {
+  blink::WebLocalFrame* frame = blink::WebLocalFrame::FrameForCurrentContext();
   if (!frame || !frame->View())
     return false;
 
@@ -70,7 +69,7 @@
 //      should be an array.
 //  - chrome.getVariableValue: Returns value for the input variable name if such
 //      a value was set by the browser. Else will return an empty string.
-void WebUIExtension::Install(blink::WebFrame* frame) {
+void WebUIExtension::Install(blink::WebLocalFrame* frame) {
   v8::Isolate* isolate = blink::MainThreadIsolate();
   v8::HandleScope handle_scope(isolate);
   v8::Local<v8::Context> context = frame->MainWorldScriptContext();
@@ -92,7 +91,7 @@
 
 // static
 void WebUIExtension::Send(gin::Arguments* args) {
-  blink::WebFrame* frame;
+  blink::WebLocalFrame* frame;
   RenderView* render_view;
   if (!ShouldRespondToRequest(&frame, &render_view))
     return;
@@ -136,7 +135,7 @@
 
 // static
 std::string WebUIExtension::GetVariableValue(const std::string& name) {
-  blink::WebFrame* frame;
+  blink::WebLocalFrame* frame;
   RenderView* render_view;
   if (!ShouldRespondToRequest(&frame, &render_view))
     return std::string();
diff --git a/content/renderer/web_ui_extension.h b/content/renderer/web_ui_extension.h
index 8e4bfbb..9ada0aa 100644
--- a/content/renderer/web_ui_extension.h
+++ b/content/renderer/web_ui_extension.h
@@ -10,7 +10,7 @@
 #include "base/macros.h"
 
 namespace blink {
-class WebFrame;
+class WebLocalFrame;
 }
 
 namespace gin {
@@ -21,7 +21,7 @@
 
 class WebUIExtension {
  public:
-  static void Install(blink::WebFrame* frame);
+  static void Install(blink::WebLocalFrame* frame);
 
  private:
   static void Send(gin::Arguments* args);
diff --git a/content/shell/test_runner/accessibility_controller.cc b/content/shell/test_runner/accessibility_controller.cc
index e2e26c8..233f8f0 100644
--- a/content/shell/test_runner/accessibility_controller.cc
+++ b/content/shell/test_runner/accessibility_controller.cc
@@ -165,8 +165,9 @@
   blink::WebFrame* frame = web_view()->MainFrame();
   if (!frame || frame->IsWebRemoteFrame())
     return;
+  blink::WebLocalFrame* local_frame = frame->ToWebLocalFrame();
 
-  v8::Local<v8::Context> context = frame->MainWorldScriptContext();
+  v8::Local<v8::Context> context = local_frame->MainWorldScriptContext();
   if (context.IsEmpty())
     return;
 
@@ -180,7 +181,7 @@
   WebAXObjectProxy* element;
   bool result = gin::ConvertFromV8(isolate, element_handle, &element);
   DCHECK(result);
-  element->NotificationReceived(frame, notification_name);
+  element->NotificationReceived(local_frame, notification_name);
 
   if (notification_callback_.IsEmpty())
     return;
@@ -191,7 +192,7 @@
                                               v8::String::kNormalString,
                                               notification_name.size()),
   };
-  frame->CallFunctionEvenIfScriptDisabled(
+  local_frame->CallFunctionEvenIfScriptDisabled(
       v8::Local<v8::Function>::New(isolate, notification_callback_),
       context->Global(), arraysize(argv), argv);
 }
diff --git a/content/shell/test_runner/gamepad_controller.cc b/content/shell/test_runner/gamepad_controller.cc
index f58bfa4..b92eae0 100644
--- a/content/shell/test_runner/gamepad_controller.cc
+++ b/content/shell/test_runner/gamepad_controller.cc
@@ -13,11 +13,10 @@
 #include "gin/object_template_builder.h"
 #include "gin/wrappable.h"
 #include "third_party/WebKit/public/platform/WebGamepadListener.h"
-#include "third_party/WebKit/public/web/WebFrame.h"
 #include "third_party/WebKit/public/web/WebKit.h"
+#include "third_party/WebKit/public/web/WebLocalFrame.h"
 #include "v8/include/v8.h"
 
-using blink::WebFrame;
 using device::Gamepad;
 using device::Gamepads;
 
@@ -29,7 +28,7 @@
   static gin::WrapperInfo kWrapperInfo;
 
   static void Install(base::WeakPtr<GamepadController> controller,
-                      blink::WebFrame* frame);
+                      blink::WebLocalFrame* frame);
 
  private:
   explicit GamepadControllerBindings(
@@ -60,7 +59,7 @@
 // static
 void GamepadControllerBindings::Install(
     base::WeakPtr<GamepadController> controller,
-    WebFrame* frame) {
+    blink::WebLocalFrame* frame) {
   v8::Isolate* isolate = blink::MainThreadIsolate();
   v8::HandleScope handle_scope(isolate);
   v8::Local<v8::Context> context = frame->MainWorldScriptContext();
@@ -161,7 +160,7 @@
   memset(&gamepads_, 0, sizeof(gamepads_));
 }
 
-void GamepadController::Install(WebFrame* frame) {
+void GamepadController::Install(blink::WebLocalFrame* frame) {
   GamepadControllerBindings::Install(weak_factory_.GetWeakPtr(), frame);
 }
 
diff --git a/content/shell/test_runner/gamepad_controller.h b/content/shell/test_runner/gamepad_controller.h
index 787eec5..c91d301 100644
--- a/content/shell/test_runner/gamepad_controller.h
+++ b/content/shell/test_runner/gamepad_controller.h
@@ -13,8 +13,8 @@
 #include "device/gamepad/public/cpp/gamepads.h"
 
 namespace blink {
-class WebFrame;
 class WebGamepadListener;
+class WebLocalFrame;
 }
 
 namespace test_runner {
@@ -28,7 +28,7 @@
   ~GamepadController();
 
   void Reset();
-  void Install(blink::WebFrame* frame);
+  void Install(blink::WebLocalFrame* frame);
 
   void SampleGamepads(device::Gamepads& gamepads);
   void SetListener(blink::WebGamepadListener* listener);
diff --git a/content/shell/test_runner/gc_controller.cc b/content/shell/test_runner/gc_controller.cc
index a84ff83..93f9be6 100644
--- a/content/shell/test_runner/gc_controller.cc
+++ b/content/shell/test_runner/gc_controller.cc
@@ -7,8 +7,8 @@
 #include "gin/arguments.h"
 #include "gin/handle.h"
 #include "gin/object_template_builder.h"
-#include "third_party/WebKit/public/web/WebFrame.h"
 #include "third_party/WebKit/public/web/WebKit.h"
+#include "third_party/WebKit/public/web/WebLocalFrame.h"
 #include "v8/include/v8.h"
 
 namespace test_runner {
@@ -16,7 +16,7 @@
 gin::WrapperInfo GCController::kWrapperInfo = {gin::kEmbedderNativeGin};
 
 // static
-void GCController::Install(blink::WebFrame* frame) {
+void GCController::Install(blink::WebLocalFrame* frame) {
   v8::Isolate* isolate = blink::MainThreadIsolate();
   v8::HandleScope handle_scope(isolate);
   v8::Local<v8::Context> context = frame->MainWorldScriptContext();
diff --git a/content/shell/test_runner/gc_controller.h b/content/shell/test_runner/gc_controller.h
index 49e4b06..9c34adcf 100644
--- a/content/shell/test_runner/gc_controller.h
+++ b/content/shell/test_runner/gc_controller.h
@@ -9,7 +9,7 @@
 #include "gin/wrappable.h"
 
 namespace blink {
-class WebFrame;
+class WebLocalFrame;
 }
 
 namespace gin {
@@ -21,7 +21,7 @@
 class GCController : public gin::Wrappable<GCController> {
  public:
   static gin::WrapperInfo kWrapperInfo;
-  static void Install(blink::WebFrame* frame);
+  static void Install(blink::WebLocalFrame* frame);
 
  private:
   GCController();
diff --git a/content/shell/test_runner/spell_check_client.cc b/content/shell/test_runner/spell_check_client.cc
index 19ed128..22ef57d 100644
--- a/content/shell/test_runner/spell_check_client.cc
+++ b/content/shell/test_runner/spell_check_client.cc
@@ -13,8 +13,8 @@
 #include "content/shell/test_runner/mock_grammar_check.h"
 #include "content/shell/test_runner/test_runner.h"
 #include "content/shell/test_runner/web_test_delegate.h"
-#include "third_party/WebKit/public/web/WebFrame.h"
 #include "third_party/WebKit/public/web/WebKit.h"
+#include "third_party/WebKit/public/web/WebLocalFrame.h"
 #include "third_party/WebKit/public/web/WebTextCheckingCompletion.h"
 #include "third_party/WebKit/public/web/WebTextCheckingResult.h"
 
@@ -148,14 +148,15 @@
   blink::WebFrame* frame = test_runner_->mainFrame();
   if (!frame || frame->IsWebRemoteFrame())
     return;
+  blink::WebLocalFrame* local_frame = frame->ToWebLocalFrame();
 
-  v8::Local<v8::Context> context = frame->MainWorldScriptContext();
+  v8::Local<v8::Context> context = local_frame->MainWorldScriptContext();
   if (context.IsEmpty())
     return;
 
   v8::Context::Scope context_scope(context);
 
-  frame->CallFunctionEvenIfScriptDisabled(
+  local_frame->CallFunctionEvenIfScriptDisabled(
       v8::Local<v8::Function>::New(isolate, resolved_callback_),
       context->Global(), 0, nullptr);
 }
diff --git a/content/shell/test_runner/test_interfaces.cc b/content/shell/test_runner/test_interfaces.cc
index dc6e245..0131fdb 100644
--- a/content/shell/test_runner/test_interfaces.cc
+++ b/content/shell/test_runner/test_interfaces.cc
@@ -59,7 +59,7 @@
   delegate_ = delegate;
 }
 
-void TestInterfaces::BindTo(blink::WebFrame* frame) {
+void TestInterfaces::BindTo(blink::WebLocalFrame* frame) {
   if (gamepad_controller_)
     gamepad_controller_->Install(frame);
   GCController::Install(frame);
diff --git a/content/shell/test_runner/test_interfaces.h b/content/shell/test_runner/test_interfaces.h
index 18bf973..97789e2 100644
--- a/content/shell/test_runner/test_interfaces.h
+++ b/content/shell/test_runner/test_interfaces.h
@@ -14,7 +14,7 @@
 #include "third_party/WebKit/public/platform/WebNonCopyable.h"
 
 namespace blink {
-class WebFrame;
+class WebLocalFrame;
 class WebThemeEngine;
 class WebURL;
 class WebView;
@@ -34,7 +34,7 @@
 
   void SetMainView(blink::WebView* web_view);
   void SetDelegate(WebTestDelegate* delegate);
-  void BindTo(blink::WebFrame* frame);
+  void BindTo(blink::WebLocalFrame* frame);
   void ResetTestHelperControllers();
   void ResetAll();
   bool TestIsRunning();
diff --git a/content/shell/test_runner/test_runner_for_specific_view.cc b/content/shell/test_runner/test_runner_for_specific_view.cc
index e90cbab..461646c1 100644
--- a/content/shell/test_runner/test_runner_for_specific_view.cc
+++ b/content/shell/test_runner/test_runner_for_specific_view.cc
@@ -185,7 +185,7 @@
   v8::Isolate* isolate = blink::MainThreadIsolate();
   v8::HandleScope handle_scope(isolate);
 
-  WebFrame* frame = web_view()->MainFrame();
+  blink::WebLocalFrame* frame = GetLocalMainFrame();
   v8::Local<v8::Context> context = frame->MainWorldScriptContext();
   if (context.IsEmpty())
     return;
@@ -249,7 +249,7 @@
   v8::HandleScope handle_scope(isolate);
 
   v8::Local<v8::Context> context =
-      web_view()->MainFrame()->MainWorldScriptContext();
+      GetLocalMainFrame()->MainWorldScriptContext();
   if (context.IsEmpty())
     return;
 
@@ -331,7 +331,7 @@
   v8::Isolate* isolate = blink::MainThreadIsolate();
   v8::HandleScope handle_scope(isolate);
   v8::Local<v8::Context> context =
-      web_view()->MainFrame()->MainWorldScriptContext();
+      GetLocalMainFrame()->MainWorldScriptContext();
   if (context.IsEmpty())
     return;
   v8::Context::Scope context_scope(context);
@@ -408,7 +408,7 @@
   v8::HandleScope handle_scope(isolate);
 
   v8::Local<v8::Context> context =
-      web_view()->MainFrame()->MainWorldScriptContext();
+      GetLocalMainFrame()->MainWorldScriptContext();
   if (context.IsEmpty())
     return;
 
@@ -572,13 +572,7 @@
 }
 
 bool TestRunnerForSpecificView::CallShouldCloseOnWebView() {
-  if (!web_view()->MainFrame()->ToWebLocalFrame()) {
-    CHECK(false) << "This function cannot be called if the main frame is not a "
-                    "local frame.";
-  }
-
-  return web_view()->MainFrame()->ToWebLocalFrame()->DispatchBeforeUnloadEvent(
-      false);
+  return GetLocalMainFrame()->DispatchBeforeUnloadEvent(false);
 }
 
 void TestRunnerForSpecificView::SetDomainRelaxationForbiddenForURLScheme(
@@ -662,7 +656,7 @@
       wrap_around = true;
   }
 
-  WebLocalFrame* frame = web_view()->MainFrame()->ToWebLocalFrame();
+  WebLocalFrame* frame = GetLocalMainFrame();
   const bool find_result = frame->Find(0, WebString::FromUTF8(search_text),
                                        find_options, wrap_around, 0);
   frame->StopFinding(WebLocalFrame::kStopFindActionKeepSelection);
@@ -670,25 +664,25 @@
 }
 
 std::string TestRunnerForSpecificView::SelectionAsMarkup() {
-  if (!web_view()->MainFrame()->ToWebLocalFrame()) {
-    CHECK(false) << "This function cannot be called if the main frame is not a "
-                    "local frame.";
-  }
-  return web_view()->MainFrame()->ToWebLocalFrame()->SelectionAsMarkup().Utf8();
+  return GetLocalMainFrame()->SelectionAsMarkup().Utf8();
 }
 
 void TestRunnerForSpecificView::SetViewSourceForFrame(const std::string& name,
                                                       bool enabled) {
+  WebFrame* target_frame =
+      GetLocalMainFrame()->FindFrameByName(WebString::FromUTF8(name));
+  if (target_frame)
+    target_frame->EnableViewSourceMode(enabled);
+}
+
+blink::WebLocalFrame* TestRunnerForSpecificView::GetLocalMainFrame() {
   if (!web_view()->MainFrame()->IsWebLocalFrame()) {
+    // Hitting the check below uncovers a new scenario that requires OOPIF
+    // support in the layout tests harness.
     CHECK(false) << "This function cannot be called if the main frame is not a "
                     "local frame.";
   }
-
-  WebFrame* target_frame =
-      web_view()->MainFrame()->ToWebLocalFrame()->FindFrameByName(
-          WebString::FromUTF8(name));
-  if (target_frame)
-    target_frame->EnableViewSourceMode(enabled);
+  return web_view()->MainFrame()->ToWebLocalFrame();
 }
 
 blink::WebView* TestRunnerForSpecificView::web_view() {
diff --git a/content/shell/test_runner/test_runner_for_specific_view.h b/content/shell/test_runner/test_runner_for_specific_view.h
index 8636461..6cb30a0 100644
--- a/content/shell/test_runner/test_runner_for_specific_view.h
+++ b/content/shell/test_runner/test_runner_for_specific_view.h
@@ -215,6 +215,11 @@
   std::string SelectionAsMarkup();
   void SetViewSourceForFrame(const std::string& name, bool enabled);
 
+  // Many parts of the layout test harness assume that the main frame is local.
+  // Having all of them go through the helper below makes it easier to catch
+  // scenarios that require breaking this assumption.
+  blink::WebLocalFrame* GetLocalMainFrame();
+
   // Helpers for accessing pointers exposed by |web_view_test_proxy_base_|.
   blink::WebView* web_view();
   WebTestDelegate* delegate();
diff --git a/content/shell/test_runner/web_ax_object_proxy.cc b/content/shell/test_runner/web_ax_object_proxy.cc
index 5ae5dd7..65f5241 100644
--- a/content/shell/test_runner/web_ax_object_proxy.cc
+++ b/content/shell/test_runner/web_ax_object_proxy.cc
@@ -13,8 +13,8 @@
 #include "third_party/WebKit/public/platform/WebPoint.h"
 #include "third_party/WebKit/public/platform/WebRect.h"
 #include "third_party/WebKit/public/platform/WebString.h"
-#include "third_party/WebKit/public/web/WebFrame.h"
 #include "third_party/WebKit/public/web/WebKit.h"
+#include "third_party/WebKit/public/web/WebLocalFrame.h"
 #include "third_party/skia/include/core/SkMatrix44.h"
 #include "ui/gfx/geometry/rect_f.h"
 #include "ui/gfx/transform.h"
@@ -742,7 +742,7 @@
 }
 
 void WebAXObjectProxy::NotificationReceived(
-    blink::WebFrame* frame,
+    blink::WebLocalFrame* frame,
     const std::string& notification_name) {
   if (notification_callback_.IsEmpty())
     return;
diff --git a/content/shell/test_runner/web_ax_object_proxy.h b/content/shell/test_runner/web_ax_object_proxy.h
index 6c169c7..6e852499 100644
--- a/content/shell/test_runner/web_ax_object_proxy.h
+++ b/content/shell/test_runner/web_ax_object_proxy.h
@@ -17,7 +17,7 @@
 #include "v8/include/v8.h"
 
 namespace blink {
-class WebFrame;
+class WebLocalFrame;
 }
 
 namespace test_runner {
@@ -44,7 +44,7 @@
   virtual bool IsRoot() const;
   bool IsEqualToObject(const blink::WebAXObject& object);
 
-  void NotificationReceived(blink::WebFrame* frame,
+  void NotificationReceived(blink::WebLocalFrame* frame,
                             const std::string& notification_name);
   void Reset();
 
diff --git a/content/test/data/accessibility/aria/aria-owns-list-expected-blink.txt b/content/test/data/accessibility/aria/aria-owns-list-expected-blink.txt
new file mode 100644
index 0000000..624735ea
--- /dev/null
+++ b/content/test/data/accessibility/aria/aria-owns-list-expected-blink.txt
@@ -0,0 +1,8 @@
+rootWebArea
+++list pageSize=(400, 400)
+++++listItem pageSize=(400, 200)
+++++++staticText name='One'
+++++++++inlineTextBox name='One'
+++++listItem pageSize=(400, 200)
+++++++staticText name='Two'
+++++++++inlineTextBox name='Two'
diff --git a/content/test/data/accessibility/aria/aria-owns-list.html b/content/test/data/accessibility/aria/aria-owns-list.html
index 8112c65..b8587971 100644
--- a/content/test/data/accessibility/aria/aria-owns-list.html
+++ b/content/test/data/accessibility/aria/aria-owns-list.html
@@ -1,5 +1,6 @@
 <!--
 @MAC-ALLOW:size=(400*
+@BLINK-ALLOW:pageSize=(400*
 -->
 <html>
   <body>
diff --git a/docs/code_reviews.md b/docs/code_reviews.md
index 6efe906..ae6bfd0f1 100644
--- a/docs/code_reviews.md
+++ b/docs/code_reviews.md
@@ -43,8 +43,13 @@
 
 Owners files are recursive, so each file also applies to its subdirectories.
 It's generally best to pick more specific owners. People listed in higher-level
-directories may have less experience with the code in question. More detail on
-the owners file format is provided in the "More information" section below.
+directories may have less experience with the code in question. For example,
+the reviewers in the `//chrome/browser/component_name/OWNERS` file will likely
+be more familiar with code in `//chrome/browser/component_name/sub_component`
+than reviewers in the higher-level `//chrome/OWNERS` file.
+
+More detail on the owners file format is provided in the "More information"
+section below.
 
 *Tip:* The `git cl owners` command can help find owners.
 
@@ -109,27 +114,40 @@
     like normal.
 
   * Add a line "TBR=<reviewer's email>" to the bottom of the change list
-    description.
+    description. e.g. `TBR=reviewer1@chromium.org,reviewer2@chromium.org`
+
+  * Type a message so that the owners in the TBR list can understand who is
+    responsible for reviewing what, as part of their post-commit review
+    responsibility. e.g.
+    ```
+    TBRing reviewers:
+    reviewer1: Please review changes to foo/
+    reviewer2: Please review changes to bar/
+    ```
 
   * Push the "send mail" button.
 
 ### TBR-ing certain types of mechanical changes
 
 Sometimes you might do something that affects many callers in different
-directories. For example, adding a parameter to a common function in //base.
-If the updates to the callers is mechanical, you can:
+directories. For example, adding a parameter to a common function in
+`//base`, with callers in `//chrome/browser/foo`, `//net/bar`, and many other
+directories. If the updates to the callers is mechanical, you can:
 
-  * Get a normal owner of the lower-level directory you're changing (in this
-    example, `//base`) to do a proper review of those changes.
+  * Get a normal owner of the lower-level code you're changing (in this
+    example, the function in `//base`) to do a proper review of those changes.
 
-  * Get _somebody_ to review the downstream changes. This is often the same
-    person from the previous step but could be somebody else.
+  * Get _somebody_ to review the downstream changes made to the callers as a
+    result of the `//base` change. This is often the same person from the
+    previous step but could be somebody else.
 
-  * Add the owners of the affected downstream directories as TBR.
+  * Add the owners of the affected downstream directories as TBR. (In this
+    example, reviewers from `//chrome/browser/foo/OWNERS`, `//net/bar/OWNERS`,
+    etc.)
 
 This process ensures that all code is reviewed prior to checkin and that the
 concept of the change is reviewed by a qualified person, but you don't have to
-track down many individual owners for trivial changes to their directories.
+wait for many individual owners to review trivial changes to their directories.
 
 ### TBR-ing documentation updates
 
@@ -155,7 +173,7 @@
 ### OWNERS file details
 
 Refer to the [source code](https://chromium.googlesource.com/chromium/tools/depot_tools/+/master/owners.py)
-for all details on the file format. 
+for all details on the file format.
 
 This example indicates that two people are owners, in addition to any owners
 from the parent directory. `git cl owners` will list the comment after an
diff --git a/extensions/renderer/extension_helper.cc b/extensions/renderer/extension_helper.cc
index d90a34d7..eb64c4b6 100644
--- a/extensions/renderer/extension_helper.cc
+++ b/extensions/renderer/extension_helper.cc
@@ -15,6 +15,7 @@
 #include "extensions/renderer/dispatcher.h"
 #include "third_party/WebKit/public/platform/WebURLRequest.h"
 #include "third_party/WebKit/public/web/WebDocument.h"
+#include "third_party/WebKit/public/web/WebLocalFrame.h"
 #include "third_party/WebKit/public/web/WebView.h"
 
 namespace extensions {
@@ -66,9 +67,16 @@
 }
 
 void ExtensionHelper::OnAppWindowClosed() {
+  // ExtensionMsg_AppWindowClosed is always sent to the current, non-swapped-out
+  // RenderView where the main frame is a local frame.
+  DCHECK(render_view()->GetWebView()->MainFrame()->IsWebLocalFrame());
+
   v8::HandleScope scope(v8::Isolate::GetCurrent());
-  v8::Local<v8::Context> v8_context =
-      render_view()->GetWebView()->MainFrame()->MainWorldScriptContext();
+  v8::Local<v8::Context> v8_context = render_view()
+                                          ->GetWebView()
+                                          ->MainFrame()
+                                          ->ToWebLocalFrame()
+                                          ->MainWorldScriptContext();
   ScriptContext* script_context =
       dispatcher_->script_context_set().GetByV8Context(v8_context);
   if (!script_context)
diff --git a/extensions/renderer/guest_view/guest_view_internal_custom_bindings.cc b/extensions/renderer/guest_view/guest_view_internal_custom_bindings.cc
index 77440ee..ddba6ea5 100644
--- a/extensions/renderer/guest_view/guest_view_internal_custom_bindings.cc
+++ b/extensions/renderer/guest_view/guest_view_internal_custom_bindings.cc
@@ -342,14 +342,7 @@
     return;
 
   blink::WebFrame* frame = view->GetWebView()->MainFrame();
-  // TODO(lazyboy,nasko): The WebLocalFrame branch is not used when running
-  // on top of out-of-process iframes. Remove it once the code is converted.
-  v8::Local<v8::Value> window;
-  if (frame->IsWebLocalFrame()) {
-    window = frame->MainWorldScriptContext()->Global();
-  } else {
-    window = frame->ToWebRemoteFrame()->GlobalProxy();
-  }
+  v8::Local<v8::Value> window = frame->GlobalProxy();
   args.GetReturnValue().Set(window);
 }
 
diff --git a/extensions/renderer/guest_view/mime_handler_view/mime_handler_view_container.cc b/extensions/renderer/guest_view/mime_handler_view/mime_handler_view_container.cc
index c27ded7..14d599a2 100644
--- a/extensions/renderer/guest_view/mime_handler_view/mime_handler_view_container.cc
+++ b/extensions/renderer/guest_view/mime_handler_view/mime_handler_view_container.cc
@@ -236,14 +236,7 @@
   v8::Context::Scope context_scope(
       render_frame()->GetWebFrame()->MainWorldScriptContext());
 
-  // TODO(lazyboy,nasko): The WebLocalFrame branch is not used when running
-  // on top of out-of-process iframes. Remove it once the code is converted.
-  v8::Local<v8::Object> guest_proxy_window;
-  if (guest_proxy_frame->IsWebLocalFrame()) {
-    guest_proxy_window = guest_proxy_frame->MainWorldScriptContext()->Global();
-  } else {
-    guest_proxy_window = guest_proxy_frame->ToWebRemoteFrame()->GlobalProxy();
-  }
+  v8::Local<v8::Object> guest_proxy_window = guest_proxy_frame->GlobalProxy();
   gin::Dictionary window_object(isolate, guest_proxy_window);
   v8::Local<v8::Function> post_message;
   if (!window_object.Get(std::string(kPostMessageName), &post_message))
@@ -261,7 +254,7 @@
 
 void MimeHandlerViewContainer::PostMessageFromValue(
     const base::Value& message) {
-  blink::WebFrame* frame = render_frame()->GetWebFrame();
+  blink::WebLocalFrame* frame = render_frame()->GetWebFrame();
   if (!frame)
     return;
 
@@ -302,7 +295,7 @@
     return;
 
   // Now that the guest has loaded, flush any unsent messages.
-  blink::WebFrame* frame = render_frame()->GetWebFrame();
+  blink::WebLocalFrame* frame = render_frame()->GetWebFrame();
   if (!frame)
     return;
 
diff --git a/extensions/renderer/runtime_custom_bindings.cc b/extensions/renderer/runtime_custom_bindings.cc
index 66ed982f..fb1e4814 100644
--- a/extensions/renderer/runtime_custom_bindings.cc
+++ b/extensions/renderer/runtime_custom_bindings.cc
@@ -94,7 +94,7 @@
     // main views, not any subframes. (Returning subframes can cause broken
     // behavior by treating an app window's iframe as its main frame, and maybe
     // other nastiness).
-    blink::WebFrame* web_frame = frame->GetWebFrame();
+    blink::WebLocalFrame* web_frame = frame->GetWebFrame();
     if (web_frame->Top() != web_frame)
       continue;
 
diff --git a/extensions/shell/installer/BUILD.gn b/extensions/shell/installer/BUILD.gn
new file mode 100644
index 0000000..091a8fc
--- /dev/null
+++ b/extensions/shell/installer/BUILD.gn
@@ -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.
+
+import("//build/config/ui.gni")
+import("//build/util/process_version.gni")
+import("//extensions/features/features.gni")
+
+assert(enable_extensions)
+
+declare_args() {
+  enable_app_shell_installer =
+      is_desktop_linux && is_chrome_branded && current_cpu == "x64"
+}
+
+# Meta-target that forwards to the installer of the correct type (if any).
+# Named "app_shell_installer" to not conflict with Chrome's "installer" target.
+group("app_shell_installer") {
+  # See the "app_shell_lib" definition for why testonly is needed.
+  testonly = true
+  if (enable_app_shell_installer) {
+    deps = [
+      "//extensions/shell/installer/linux",
+    ]
+  }
+}
diff --git a/extensions/shell/installer/linux/BUILD.gn b/extensions/shell/installer/linux/BUILD.gn
new file mode 100644
index 0000000..8883c20
--- /dev/null
+++ b/extensions/shell/installer/linux/BUILD.gn
@@ -0,0 +1,220 @@
+# 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.
+
+# TODO(michaelpg): Dedupe with Chrome installer.
+
+import("//build/config/chrome_build.gni")
+import("//build/config/features.gni")
+import("//build/config/sanitizers/sanitizers.gni")
+import("//build/config/sysroot.gni")
+import("//build/util/version.gni")
+import("//chrome/process_version_rc_template.gni")  # For branding_file_path.
+
+assert(is_desktop_linux)
+assert(current_cpu == "x64")
+
+# The packages list the exact versions of each library used. The versions used
+# on the bots are likely different than those on your workstation, so you'll
+# get a stream of errors like:
+#   < libasound2 (>= 1.0.23)
+#   ---
+#   > libasound2 (>= 1.0.16)
+#
+# To avoid these warnings for testing purposes, do:
+#
+#   export IGNORE_DEPS_CHANGES=1
+#
+# before you build.
+group("linux") {
+  # See the "app_shell_lib" definition for why testonly is needed.
+  testonly = true
+  deps = [
+    # TODO(michaelpg): Add beta/stable once we verify the unstable installer is
+    # building and installing properly.
+    ":unstable",
+  ]
+}
+
+branding_dir = "//chrome/app/theme/$branding_path_component"
+
+copy("common_packaging_files") {
+  visibility = [ ":*" ]
+  sources = [
+    "//chrome/installer/linux/common/apt.include",
+    "//chrome/installer/linux/common/repo.cron",
+    "//chrome/installer/linux/common/symlinks.include",
+    "//chrome/installer/linux/common/variables.include",
+    "/usr/bin/eu-strip",
+    "common/installer.include",
+    "common/wrapper",
+  ]
+
+  if (is_chrome_branded) {
+    sources += [ "common/google-app-shell/google-app-shell.info" ]
+  } else {
+    sources += [ "common/chromium-app-shell/chromium-app-shell.info" ]
+  }
+
+  outputs = [
+    "$root_out_dir/app_shell_installer/common/{{source_file_part}}",
+  ]
+}
+
+copy("deb_packaging_files") {
+  visibility = [ ":*" ]
+  sources = [
+    "//chrome/installer/linux/debian/changelog.template",
+    "//chrome/installer/linux/debian/control.template",
+    "debian/build.sh",
+    "debian/expected_deps_x64_jessie",
+    "debian/postinst",
+    "debian/postrm",
+  ]
+  outputs = [
+    "$root_out_dir/app_shell_installer/debian/{{source_file_part}}",
+  ]
+}
+
+copy("theme_files") {
+  visibility = [ ":*" ]
+  sources = [
+    "$branding_dir/BRANDING",
+  ]
+  outputs = [
+    "$root_out_dir/app_shell_installer/theme/{{source_file_part}}",
+  ]
+}
+
+process_version("save_build_info") {
+  # Just output the default version info variables (no template).
+  process_only = true
+  sources = [
+    "//build/util/LASTCHANGE",
+    "//chrome/VERSION",
+    branding_file_path,
+  ]
+  output = "$root_out_dir/app_shell_installer/version.txt"
+}
+
+# Dependencies for all Linux installer targets.
+group("installer_deps") {
+  testonly = true
+
+  # Though many of these things appear in data_deps further down the
+  # dependency chain, they must appear here as public_deps so that they can
+  # be listed as inputs to the actions that depend on ":installer_deps"
+  # and are guaranteed to have been built before those actions run.
+
+  public_deps = [
+    ":common_packaging_files",
+    ":deb_packaging_files",
+    ":save_build_info",
+    ":theme_files",
+    "//extensions:shell_and_test_pak",
+    "//extensions/shell:app_shell",
+  ]
+  if (enable_nacl) {
+    public_deps += [
+      "//components/nacl/loader:nacl_helper",
+
+      # These are data_deps of nacl_helper, but that is not enough,
+      # as explained above.
+      "//native_client/src/trusted/service_runtime/linux:bootstrap",
+      "//ppapi/native_client:irt",
+    ]
+  }
+  if (use_custom_libcxx) {
+    public_deps += [ "//buildtools/third_party/libc++" ]
+  }
+}
+
+# Creates .deb installer package.
+#
+# channel:
+#   Name of the channel.
+template("linux_package") {
+  testonly = true
+  assert(defined(invoker.channel))
+  channel = invoker.channel
+
+  packaging_files_binaries = [
+    # TODO(mmoss) Any convenient way to get all the relevant build
+    # files? (e.g. all locales, resources, etc.)
+    "$root_out_dir/app_shell",
+    "$root_out_dir/extensions_shell_and_test.pak",
+  ]
+
+  if (enable_nacl) {
+    packaging_files_binaries += [
+      "$root_out_dir/nacl_helper",
+      "$root_out_dir/nacl_helper_bootstrap",
+      "$root_out_dir/nacl_irt_x86_64.nexe",
+    ]
+  }
+
+  if (use_custom_libcxx) {
+    packaging_files_binaries += [ "$root_out_dir/libc++.so" ]
+  }
+
+  deb_target_name = "${target_name}_deb"
+  action(deb_target_name) {
+    visibility = [ ":*" ]
+    script = "//chrome/installer/linux/flock_make_package.py"
+    deb_arch = "amd64"
+
+    inputs = packaging_files_binaries
+    outputs = [
+      "$root_out_dir/google-app-shell-${channel}_${chrome_version_full}-1_${deb_arch}.deb",
+    ]
+
+    args = [
+      rebase_path("$root_out_dir/linux_package.lock", root_build_dir),
+      rebase_path("$root_out_dir/app_shell_installer/debian/build.sh",
+                  root_build_dir),
+      "-o",
+      rebase_path(root_out_dir, root_build_dir),
+      "-b",
+      rebase_path(root_out_dir, root_build_dir),
+      "-a",
+      current_cpu,
+      "-c",
+      invoker.channel,
+      "-d",
+      branding_path_component,
+      "-s",
+      rebase_path(sysroot),
+    ]
+    deps = [
+      ":installer_deps",
+    ]
+  }
+
+  group(target_name) {
+    deps = [
+      ":$deb_target_name",
+    ]
+  }
+}
+
+# Standard packages.
+linux_package("unstable") {
+  channel = "unstable"
+}
+linux_package("stable") {
+  channel = "stable"
+}
+linux_package("beta") {
+  channel = "beta"
+}
+
+# Other packages that we support that aren't included in the default "linux"
+# target.
+linux_package("trunk") {
+  channel = "trunk"
+}
+if (is_asan) {
+  linux_package("asan") {
+    channel = "asan"
+  }
+}
diff --git a/extensions/shell/installer/linux/common/chromium-app-shell/chromium-app-shell.info b/extensions/shell/installer/linux/common/chromium-app-shell/chromium-app-shell.info
new file mode 100644
index 0000000..ee88042
--- /dev/null
+++ b/extensions/shell/installer/linux/common/chromium-app-shell/chromium-app-shell.info
@@ -0,0 +1,30 @@
+# 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.
+#
+# This file provides common configuration information for building
+# chromium-app-shell for various platforms.
+
+# Base name of the package.
+PACKAGE="chromium-app-shell"
+
+# Filename of the main executable (for generating launcher scripts, etc.)
+PROGNAME=app_shell
+
+# Base directory for package installation.
+INSTALLDIR=/opt/chromium.org/app-shell
+
+# Display string for desktop menu/icon.
+MENUNAME="Chromium App Shell"
+
+# Brief package description.
+SHORTDESC="The app shell from Chromium.org"
+
+# Detailed package description.
+FULLDESC="The app shell from Chromium.org"
+
+# Package maintainer information.
+# TODO(mmoss) Setup a mailbox for this address
+MAINTNAME="Chromium Linux Team"
+MAINTMAIL="chromium-linux-packager@chromium.org"
+PRODUCTURL="http://www.chromium.org/"
diff --git a/extensions/shell/installer/linux/common/google-app-shell/google-app-shell.info b/extensions/shell/installer/linux/common/google-app-shell/google-app-shell.info
new file mode 100644
index 0000000..4086733
--- /dev/null
+++ b/extensions/shell/installer/linux/common/google-app-shell/google-app-shell.info
@@ -0,0 +1,30 @@
+# 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.
+#
+# This file provides common configuration information for building
+# app-shell packages for various platforms.
+
+# Base name of the package.
+PACKAGE="google-app-shell"
+
+# Filename of the main executable (for generating launcher scripts, etc.)
+PROGNAME=app_shell
+
+# Base directory for package installation.
+INSTALLDIR=/opt/google/app-shell
+
+# Display string for desktop menu/icon.
+MENUNAME="App Shell"
+
+# Brief package description.
+SHORTDESC="The app shell from Google"
+
+# Detailed package description.
+FULLDESC="The app shell from Google"
+
+# Package maintainer information.
+# TODO(mmoss) Setup a mailbox for this address
+MAINTNAME="Chrome Linux Team"
+MAINTMAIL="chromium-dev@chromium.org"
+PRODUCTURL="https://chrome.google.com/"
diff --git a/extensions/shell/installer/linux/common/installer.include b/extensions/shell/installer/linux/common/installer.include
new file mode 100644
index 0000000..521916f
--- /dev/null
+++ b/extensions/shell/installer/linux/common/installer.include
@@ -0,0 +1,193 @@
+# TODO(michaelpg): Dedupe common functionality with the Chrome installer.
+
+# Recursively replace @@include@@ template variables with the referenced file,
+# and write the resulting text to stdout.
+process_template_includes() {
+  INCSTACK+="$1->"
+  # Includes are relative to the file that does the include.
+  INCDIR=$(dirname $1)
+  # Clear IFS so 'read' doesn't trim whitespace
+  local OLDIFS="$IFS"
+  IFS=''
+  while read -r LINE
+  do
+    INCLINE=$(sed -e '/^[[:space:]]*@@include@@/!d' <<<$LINE)
+    if [ -n "$INCLINE" ]; then
+      INCFILE=$(echo $INCLINE | sed -e "s#@@include@@\(.*\)#\1#")
+      # Simple filename match to detect cyclic includes.
+      CYCLE=$(sed -e "\#$INCFILE#"'!d' <<<$INCSTACK)
+      if [ "$CYCLE" ]; then
+        echo "ERROR: Possible cyclic include detected." 1>&2
+        echo "$INCSTACK$INCFILE" 1>&2
+        exit 1
+      fi
+      if [ ! -r "$INCDIR/$INCFILE" ]; then
+        echo "ERROR: Couldn't read include file: $INCDIR/$INCFILE" 1>&2
+        exit 1
+      fi
+      process_template_includes "$INCDIR/$INCFILE"
+    else
+      echo "$LINE"
+    fi
+  done < "$1"
+  IFS="$OLDIFS"
+  INCSTACK=${INCSTACK%"$1->"}
+}
+
+# Replace template variables (@@VARNAME@@) in the given template file. If a
+# second argument is given, save the processed text to that filename, otherwise
+# modify the template file in place.
+process_template() (
+  # Don't worry if some of these substitution variables aren't set.
+  # Note that this function is run in a sub-shell so we don't leak this
+  # setting, since we still want unbound variables to be an error elsewhere.
+  set +u
+
+  local TMPLIN="$1"
+  if [ -z "$2" ]; then
+    local TMPLOUT="$TMPLIN"
+  else
+    local TMPLOUT="$2"
+  fi
+  # Process includes first so included text also gets substitutions.
+  TMPLINCL="$(process_template_includes "$TMPLIN")"
+  sed \
+    -e "s#@@PACKAGE@@#${PACKAGE}#g" \
+    -e "s#@@PACKAGE_FILENAME@@#${PACKAGE_FILENAME}#g" \
+    -e "s#@@PROGNAME@@#${PROGNAME}#g" \
+    -e "s#@@CHANNEL@@#${CHANNEL}#g" \
+    -e "s#@@COMPANY_FULLNAME@@#${COMPANY_FULLNAME}#g" \
+    -e "s#@@VERSION@@#${VERSION}#g" \
+    -e "s#@@PACKAGE_RELEASE@@#${PACKAGE_RELEASE}#g" \
+    -e "s#@@VERSIONFULL@@#${VERSIONFULL}#g" \
+    -e "s#@@INSTALLDIR@@#${INSTALLDIR}#g" \
+    -e "s#@@BUILDDIR@@#${BUILDDIR}#g" \
+    -e "s#@@STAGEDIR@@#${STAGEDIR}#g" \
+    -e "s#@@SCRIPTDIR@@#${SCRIPTDIR}#g" \
+    -e "s#@@MENUNAME@@#${MENUNAME}#g" \
+    -e "s#@@PRODUCTURL@@#${PRODUCTURL}#g" \
+    -e "s#@@PREDEPENDS@@#${PREDEPENDS}#g" \
+    -e "s#@@DEPENDS@@#${DEPENDS}#g" \
+    -e "s#@@PROVIDES@@#${PROVIDES}#g" \
+    -e "s#@@REPLACES@@#${REPLACES}#g" \
+    -e "s#@@CONFLICTS@@#${CONFLICTS}#g" \
+    -e "s#@@ARCHITECTURE@@#${ARCHITECTURE}#g" \
+    -e "s#@@MAINTNAME@@#${MAINTNAME}#g" \
+    -e "s#@@MAINTMAIL@@#${MAINTMAIL}#g" \
+    -e "s#@@REPOCONFIG@@#${REPOCONFIG}#g" \
+    -e "s#@@REPOCONFIGREGEX@@#${REPOCONFIGREGEX}#g" \
+    -e "s#@@SHORTDESC@@#${SHORTDESC}#g" \
+    -e "s#@@FULLDESC@@#${FULLDESC}#g" \
+    -e "s#@@USR_BIN_SYMLINK_NAME@@#${USR_BIN_SYMLINK_NAME:-}#g" \
+    > "$TMPLOUT" <<< "$TMPLINCL"
+)
+
+# Setup the installation directory hierachy in the package staging area.
+prep_staging_common() {
+  install -m 755 -d "${STAGEDIR}/${INSTALLDIR}" \
+    "${STAGEDIR}/usr/bin"
+}
+
+get_version_info() {
+  source "${BUILDDIR}/app_shell_installer/version.txt"
+  VERSION="${MAJOR}.${MINOR}.${BUILD}.${PATCH}"
+  # TODO(phajdan.jr): Provide a mechanism to pass a different package
+  # release number if needed. The meaning of it is to bump it for
+  # packaging-only changes while the underlying software has the same version.
+  # This corresponds to the Release field in RPM spec files and debian_revision
+  # component of the Version field for DEB control file.
+  # Generally with Chrome's fast release cycle it'd be more hassle to try
+  # to bump this number between releases.
+  PACKAGE_RELEASE="1"
+}
+
+stage_install_common() {
+  echo "Staging common install files in '${STAGEDIR}'..."
+
+  # TODO(mmoss) This assumes we built the static binaries. To support shared
+  # builds, we probably want an install target in scons so it can give us all
+  # the right files. See also:
+  # http://code.google.com/p/chromium/issues/detail?id=4451
+  #
+  # app
+  # We need to add the debug link so gdb knows to look for the symbols.
+  DEBUGFILE="${BUILDDIR}/${PROGNAME}.debug"
+  STRIPPEDFILE="${BUILDDIR}/${PROGNAME}.stripped"
+  "${BUILDDIR}/app_shell_installer/common/eu-strip" -o "${STRIPPEDFILE}" -f "${DEBUGFILE}" "${BUILDDIR}/${PROGNAME}"
+  install -m 755 "${STRIPPEDFILE}" "${STAGEDIR}/${INSTALLDIR}/${PROGNAME}"
+  rm "${DEBUGFILE}" "${STRIPPEDFILE}"
+
+  # resources
+  install -m 644 \
+    "${BUILDDIR}/extensions_shell_and_test.pak" \
+    "${STAGEDIR}/${INSTALLDIR}/"
+
+
+  # ICU data file; only necessary when icu_use_data_file_flag is set to 1
+  # in build/common.gypi.
+  install -m 644 "${BUILDDIR}/icudtl.dat" "${STAGEDIR}/${INSTALLDIR}/"
+
+  # V8 snapshot files; only necessary when v8_use_external_startup_data is
+  # set to 1 in build/common.gypi.
+  if [ -f "${BUILDDIR}/natives_blob.bin" ]; then
+    install -m 644 "${BUILDDIR}/natives_blob.bin" "${STAGEDIR}/${INSTALLDIR}/"
+    install -m 644 "${BUILDDIR}/snapshot_blob.bin" "${STAGEDIR}/${INSTALLDIR}/"
+  fi
+
+  # ANGLE
+  if [ "${CHANNEL}" != "stable" ]; then
+    install -m 644 "${BUILDDIR}/libGLESv2.so" "${STAGEDIR}/${INSTALLDIR}/"
+    install -m 644 "${BUILDDIR}/libEGL.so" "${STAGEDIR}/${INSTALLDIR}/"
+  fi
+
+  # SwiftShader
+  if [ -f "${BUILDDIR}/swiftshader/libEGL.so" ]; then
+    install -m 755 -d "${STAGEDIR}/${INSTALLDIR}/swiftshader/"
+    install -m 644 "${BUILDDIR}/swiftshader/libEGL.so" "${STAGEDIR}/${INSTALLDIR}/swiftshader/"
+    install -m 644 "${BUILDDIR}/swiftshader/libGLESv2.so" "${STAGEDIR}/${INSTALLDIR}/swiftshader/"
+  fi
+
+  # libc++
+  if [ -f "${BUILDDIR}/lib/libc++.so" ]; then
+    install -m 755 -d "${STAGEDIR}/${INSTALLDIR}/lib/"
+
+    install -m 644 -s "${BUILDDIR}/lib/libc++.so" "${STAGEDIR}/${INSTALLDIR}/lib/"
+  fi
+
+
+  # nacl_helper and nacl_helper_bootstrap
+  # Don't use "-s" (strip) because this runs binutils "strip", which
+  # mangles the special ELF program headers of nacl_helper_bootstrap.
+  # Explicitly use eu-strip instead, because it doesn't have that problem.
+  for file in nacl_helper nacl_helper_bootstrap; do
+    buildfile="${BUILDDIR}/${file}"
+    if [ -f "${buildfile}" ]; then
+      strippedfile="${buildfile}.stripped"
+      debugfile="${buildfile}.debug"
+      "${BUILDDIR}/app_shell_installer/common/eu-strip" -o "${strippedfile}" -f "${debugfile}" "${buildfile}"
+      install -m 755 "${strippedfile}" "${STAGEDIR}/${INSTALLDIR}/${file}"
+    fi
+  done
+  # Don't use "-s" (strip) because this would use the Linux toolchain to
+  # strip the NaCl binary, which has the potential to break it.  It
+  # certainly resets the OSABI and ABIVERSION fields to non-NaCl values,
+  # although the NaCl IRT loader doesn't care about these fields.  In any
+  # case, the IRT binaries are already stripped by NaCl's build process.
+  for filename in ${BUILDDIR}/nacl_irt_*.nexe; do
+    # Re-check the filename in case globbing matched nothing.
+    if [ -f "$filename" ]; then
+      install -m 644 "$filename" "${STAGEDIR}/${INSTALLDIR}/`basename "$filename"`"
+    fi
+  done
+
+  # launcher script and symlink
+  process_template "${BUILDDIR}/app_shell_installer/common/wrapper" \
+    "${STAGEDIR}/${INSTALLDIR}/${PACKAGE}"
+  chmod 755 "${STAGEDIR}/${INSTALLDIR}/${PACKAGE}"
+  if [ ! -f "${STAGEDIR}/${INSTALLDIR}/app-shell" ]; then
+    ln -sn "${INSTALLDIR}/${PACKAGE}" \
+      "${STAGEDIR}/${INSTALLDIR}/app-shell"
+  fi
+  ln -snf "${INSTALLDIR}/${PACKAGE}" \
+    "${STAGEDIR}/usr/bin/${USR_BIN_SYMLINK_NAME}"
+}
diff --git a/extensions/shell/installer/linux/common/wrapper b/extensions/shell/installer/linux/common/wrapper
new file mode 100755
index 0000000..487e522
--- /dev/null
+++ b/extensions/shell/installer/linux/common/wrapper
@@ -0,0 +1,32 @@
+#!/bin/bash
+#
+# 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.
+
+# Let the wrapped binary know that it has been run through the wrapper.
+export APP_SHELL_WRAPPER="`readlink -f "$0"`"
+
+HERE="`dirname "$APP_SHELL_WRAPPER"`"
+
+# Always use our versions of ffmpeg libs.
+if [[ -n "$LD_LIBRARY_PATH" ]]; then
+  LD_LIBRARY_PATH="$HERE:$HERE/lib:$LD_LIBRARY_PATH"
+else
+  LD_LIBRARY_PATH="$HERE:$HERE/lib"
+fi
+export LD_LIBRARY_PATH
+
+export CHROME_VERSION_EXTRA="@@CHANNEL@@"
+
+# We don't want bug-buddy intercepting our crashes. http://crbug.com/24120
+export GNOME_DISABLE_CRASH_DIALOG=SET_BY_GOOGLE_CHROME
+
+# Sanitize std{in,out,err} because they'll be shared with untrusted child
+# processes (http://crbug.com/376567).
+exec < /dev/null
+exec > >(exec cat)
+exec 2> >(exec cat >&2)
+
+# Note: exec -a below is a bashism.
+exec -a "$0" "$HERE/@@PROGNAME@@" "$@"
diff --git a/extensions/shell/installer/linux/debian/build.sh b/extensions/shell/installer/linux/debian/build.sh
new file mode 100755
index 0000000..331d9ad4
--- /dev/null
+++ b/extensions/shell/installer/linux/debian/build.sh
@@ -0,0 +1,351 @@
+#!/bin/bash
+#
+# 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.
+
+# TODO(michaelpg): Dedupe common functionality with the Chrome installer.
+
+# TODO(mmoss) This currently only works with official builds, since non-official
+# builds don't add the "${BUILDDIR}/app_shell_installer/" files needed for
+# packaging.
+
+set -e
+set -o pipefail
+if [ "$VERBOSE" ]; then
+  set -x
+fi
+set -u
+
+# Create the Debian changelog file needed by dpkg-gencontrol. This just adds a
+# placeholder change, indicating it is the result of an automatic build.
+# TODO(mmoss) Release packages should create something meaningful for a
+# changelog, but simply grabbing the actual 'svn log' is way too verbose. Do we
+# have any type of "significant/visible changes" log that we could use for this?
+gen_changelog() {
+  rm -f "${DEB_CHANGELOG}"
+  process_template "${SCRIPTDIR}/changelog.template" "${DEB_CHANGELOG}"
+  debchange -a --nomultimaint -m --changelog "${DEB_CHANGELOG}" \
+    "Release Notes: ${RELEASENOTES}"
+  GZLOG="${STAGEDIR}/usr/share/doc/${PACKAGE}-${CHANNEL}/changelog.gz"
+  mkdir -p "$(dirname "${GZLOG}")"
+  gzip -9 -c "${DEB_CHANGELOG}" > "${GZLOG}"
+  chmod 644 "${GZLOG}"
+}
+
+# Create the Debian control file needed by dpkg-deb.
+gen_control() {
+  dpkg-gencontrol -v"${VERSIONFULL}" -c"${DEB_CONTROL}" -l"${DEB_CHANGELOG}" \
+  -f"${DEB_FILES}" -p"${PACKAGE}-${CHANNEL}" -P"${STAGEDIR}" \
+  -O > "${STAGEDIR}/DEBIAN/control"
+  rm -f "${DEB_CONTROL}"
+}
+
+# Setup the installation directory hierachy in the package staging area.
+prep_staging_debian() {
+  prep_staging_common
+  install -m 755 -d "${STAGEDIR}/DEBIAN" \
+    "${STAGEDIR}/etc/cron.daily" \
+    "${STAGEDIR}/usr/share/doc/${USR_BIN_SYMLINK_NAME}"
+}
+
+# Put the package contents in the staging area.
+stage_install_debian() {
+  # Always use a different name for /usr/bin symlink depending on channel to
+  # avoid file collisions.
+  local USR_BIN_SYMLINK_NAME="${PACKAGE}-${CHANNEL}"
+
+  if [ "$CHANNEL" != "stable" ]; then
+    # Avoid file collisions between channels.
+    local INSTALLDIR="${INSTALLDIR}-${CHANNEL}"
+
+    local PACKAGE="${PACKAGE}-${CHANNEL}"
+  fi
+  prep_staging_debian
+  stage_install_common
+  echo "Staging Debian install files in '${STAGEDIR}'..."
+  install -m 755 -d "${STAGEDIR}/${INSTALLDIR}/cron"
+  process_template "${BUILDDIR}/app_shell_installer/common/repo.cron" \
+      "${STAGEDIR}/${INSTALLDIR}/cron/${PACKAGE}"
+  chmod 755 "${STAGEDIR}/${INSTALLDIR}/cron/${PACKAGE}"
+  pushd "${STAGEDIR}/etc/cron.daily/"
+  ln -snf "${INSTALLDIR}/cron/${PACKAGE}" "${PACKAGE}"
+  popd
+  process_template "${BUILDDIR}/app_shell_installer/debian/postinst" \
+    "${STAGEDIR}/DEBIAN/postinst"
+  chmod 755 "${STAGEDIR}/DEBIAN/postinst"
+  process_template "${BUILDDIR}/app_shell_installer/debian/postrm" \
+    "${STAGEDIR}/DEBIAN/postrm"
+  chmod 755 "${STAGEDIR}/DEBIAN/postrm"
+}
+
+# Actually generate the package file.
+do_package() {
+  echo "Packaging ${ARCHITECTURE}..."
+  PREDEPENDS="$COMMON_PREDEPS"
+  DEPENDS="${COMMON_DEPS}"
+  REPLACES=""
+  CONFLICTS=""
+  PROVIDES=""
+  gen_changelog
+  process_template "${SCRIPTDIR}/control.template" "${DEB_CONTROL}"
+  export DEB_HOST_ARCH="${ARCHITECTURE}"
+  if [ -f "${DEB_CONTROL}" ]; then
+    gen_control
+  fi
+  fakeroot dpkg-deb -Zxz -z9 -b "${STAGEDIR}" .
+}
+
+verify_package() {
+  DEPENDS="${COMMON_DEPS}"  # This needs to match do_package() above.
+  echo ${DEPENDS} | sed 's/, /\n/g' | LANG=C sort > expected_deb_depends
+  dpkg -I "${PACKAGE}-${CHANNEL}_${VERSIONFULL}_${ARCHITECTURE}.deb" | \
+      grep '^ Depends: ' | sed 's/^ Depends: //' | sed 's/, /\n/g' | \
+      LANG=C sort > actual_deb_depends
+  BAD_DIFF=0
+  diff -u expected_deb_depends actual_deb_depends || BAD_DIFF=1
+  if [ $BAD_DIFF -ne 0 ] && [ -z "${IGNORE_DEPS_CHANGES:-}" ]; then
+    echo
+    echo "ERROR: bad dpkg dependencies!"
+    echo
+    exit $BAD_DIFF
+  fi
+}
+
+# Remove temporary files and unwanted packaging output.
+cleanup() {
+  echo "Cleaning..."
+  rm -rf "${STAGEDIR}"
+  rm -rf "${TMPFILEDIR}"
+}
+
+usage() {
+  echo "usage: $(basename $0) [-c channel] [-a target_arch] [-o 'dir'] "
+  echo "                      [-b 'dir'] -d branding"
+  echo "-c channel the package channel (trunk, asan, unstable, beta, stable)"
+  echo "-a arch    package architecture (ia32 or x64)"
+  echo "-o dir     package output directory [${OUTPUTDIR}]"
+  echo "-b dir     build input directory    [${BUILDDIR}]"
+  echo "-d brand   either chromium or google_chrome"
+  echo "-s dir     /path/to/sysroot"
+  echo "-h         this help message"
+}
+
+# Check that the channel name is one of the allowable ones.
+verify_channel() {
+  case $CHANNEL in
+    stable )
+      CHANNEL=stable
+      RELEASENOTES="http://googlechromereleases.blogspot.com/search/label/Stable%20updates"
+      ;;
+    unstable|dev|alpha )
+      CHANNEL=unstable
+      RELEASENOTES="http://googlechromereleases.blogspot.com/search/label/Dev%20updates"
+      ;;
+    testing|beta )
+      CHANNEL=beta
+      RELEASENOTES="http://googlechromereleases.blogspot.com/search/label/Beta%20updates"
+      ;;
+    trunk|asan )
+      # Setting this to empty will prevent it from updating any existing configs
+      # from release packages.
+      REPOCONFIG=""
+      RELEASENOTES="http://googlechromereleases.blogspot.com/"
+      ;;
+    * )
+      echo
+      echo "ERROR: '$CHANNEL' is not a valid channel type."
+      echo
+      exit 1
+      ;;
+  esac
+}
+
+process_opts() {
+  while getopts ":s:o:b:c:a:d:h" OPTNAME
+  do
+    case $OPTNAME in
+      o )
+        OUTPUTDIR=$(readlink -f "${OPTARG}")
+        mkdir -p "${OUTPUTDIR}"
+        ;;
+      b )
+        BUILDDIR=$(readlink -f "${OPTARG}")
+        ;;
+      c )
+        CHANNEL="$OPTARG"
+        ;;
+      a )
+        TARGETARCH="$OPTARG"
+        ;;
+      d )
+        BRANDING="$OPTARG"
+        ;;
+      s )
+        SYSROOT="$OPTARG"
+        ;;
+      h )
+        usage
+        exit 0
+        ;;
+      \: )
+        echo "'-$OPTARG' needs an argument."
+        usage
+        exit 1
+        ;;
+      * )
+        echo "invalid command-line option: $OPTARG"
+        usage
+        exit 1
+        ;;
+    esac
+  done
+}
+
+#=========
+# MAIN
+#=========
+
+SCRIPTDIR=$(readlink -f "$(dirname "$0")")
+OUTPUTDIR="${PWD}"
+STAGEDIR=$(mktemp -d -t deb.build.XXXXXX) || exit 1
+TMPFILEDIR=$(mktemp -d -t deb.tmp.XXXXXX) || exit 1
+DEB_CHANGELOG="${TMPFILEDIR}/changelog"
+DEB_FILES="${TMPFILEDIR}/files"
+DEB_CONTROL="${TMPFILEDIR}/control"
+CHANNEL="trunk"
+# Default target architecture to same as build host.
+if [ "$(uname -m)" = "x86_64" ]; then
+  TARGETARCH="x64"
+else
+  TARGETARCH="ia32"
+fi
+
+# call cleanup() on exit
+trap cleanup 0
+process_opts "$@"
+BUILDDIR=${BUILDDIR:=$(readlink -f "${SCRIPTDIR}/../../../../out/Release")}
+
+if [[ "$(basename ${SYSROOT})" = "debian_jessie_"*"-sysroot" ]]; then
+  TARGET_DISTRO="jessie"
+else
+  echo "Debian package can only be built using the jessie sysroot."
+  exit 1
+fi
+
+source ${BUILDDIR}/app_shell_installer/common/installer.include
+
+get_version_info
+VERSIONFULL="${VERSION}-${PACKAGE_RELEASE}"
+
+if [ "$BRANDING" = "google_chrome" ]; then
+  source "${BUILDDIR}/app_shell_installer/common/google-app-shell.info"
+else
+  source "${BUILDDIR}/app_shell_installer/common/chromium-app-shell.info"
+fi
+eval $(sed -e "s/^\([^=]\+\)=\(.*\)$/export \1='\2'/" \
+  "${BUILDDIR}/app_shell_installer/theme/BRANDING")
+
+verify_channel
+
+# Some Debian packaging tools want these set.
+export DEBFULLNAME="${MAINTNAME}"
+export DEBEMAIL="${MAINTMAIL}"
+
+# We'd like to eliminate more of these deps by relying on the 'lsb' package, but
+# that brings in tons of unnecessary stuff, like an mta and rpm. Until that full
+# 'lsb' package is installed by default on DEB distros, we'll have to stick with
+# the LSB sub-packages, to avoid pulling in all that stuff that's not installed
+# by default.
+
+# Generate the dependencies,
+# TODO(mmoss): This is a workaround for a problem where dpkg-shlibdeps was
+# resolving deps using some of our build output shlibs (i.e.
+# out/Release/lib.target/libfreetype.so.6), and was then failing with:
+#   dpkg-shlibdeps: error: no dependency information found for ...
+# It's not clear if we ever want to look in LD_LIBRARY_PATH to resolve deps,
+# but it seems that we don't currently, so this is the most expediant fix.
+SAVE_LDLP=${LD_LIBRARY_PATH:-}
+unset LD_LIBRARY_PATH
+if [ ${TARGETARCH} = "x64" ]; then
+  SHLIB_ARGS="-l${SYSROOT}/usr/lib/x86_64-linux-gnu"
+  SHLIB_ARGS="${SHLIB_ARGS} -l${SYSROOT}/lib/x86_64-linux-gnu"
+else
+  SHLIB_ARGS="-l${SYSROOT}/usr/lib/i386-linux-gnu"
+  SHLIB_ARGS="${SHLIB_ARGS} -l${SYSROOT}/lib/i386-linux-gnu"
+fi
+SHLIB_ARGS="${SHLIB_ARGS} -l${SYSROOT}/usr/lib"
+DPKG_SHLIB_DEPS=$(cd ${SYSROOT} && dpkg-shlibdeps ${SHLIB_ARGS:-} -O \
+                  -e"$BUILDDIR/app_shell" | sed 's/^shlibs:Depends=//')
+if [ -n "$SAVE_LDLP" ]; then
+  LD_LIBRARY_PATH=$SAVE_LDLP
+fi
+
+# Format it nicely and save it for comparison.
+echo "$DPKG_SHLIB_DEPS" | sed 's/, /\n/g' | LANG=C sort > actual
+
+# Compare the expected dependency list to the generated list.
+BAD_DIFF=0
+diff -u "$SCRIPTDIR/expected_deps_${TARGETARCH}_${TARGET_DISTRO}" actual || \
+  BAD_DIFF=1
+if [ $BAD_DIFF -ne 0 ] && [ -z "${IGNORE_DEPS_CHANGES:-}" ]; then
+  echo
+  echo "ERROR: Shared library dependencies changed!"
+  echo "If this is intentional, please update:"
+  echo "extensions/shell/installer/linux/debian/expected_deps_*"
+  echo
+  exit $BAD_DIFF
+fi
+
+# Additional dependencies not in the dpkg-shlibdeps output.
+# ca-certificates: Make sure users have SSL certificates.
+# libnss3: Pull a more recent version of NSS than required by runtime linking,
+#          for security and stability updates in NSS.
+# lsb-release: For lsb-release.
+# wget: For uploading crash reports with Breakpad.
+ADDITIONAL_DEPS="ca-certificates, libnss3 (>= 3.26), lsb-release, wget"
+
+# Fix-up libnspr dependency due to renaming in Ubuntu (the old package still
+# exists, but it was moved to "universe" repository, which isn't installed by
+# default).
+DPKG_SHLIB_DEPS=$(sed \
+    's/\(libnspr4-0d ([^)]*)\), /\1 | libnspr4 (>= 4.9.5-0ubuntu0), /g' \
+    <<< $DPKG_SHLIB_DEPS)
+
+# Remove libnss dependency so the one in $ADDITIONAL_DEPS can supercede it.
+DPKG_SHLIB_DEPS=$(sed 's/\(libnss3 ([^)]*)\), //g' <<< $DPKG_SHLIB_DEPS)
+
+COMMON_DEPS="${DPKG_SHLIB_DEPS}, ${ADDITIONAL_DEPS}"
+COMMON_PREDEPS="dpkg (>= 1.14.0)"
+
+
+# Make everything happen in the OUTPUTDIR.
+cd "${OUTPUTDIR}"
+
+case "$TARGETARCH" in
+  ia32 )
+    export ARCHITECTURE="i386"
+    ;;
+  x64 )
+    export ARCHITECTURE="amd64"
+    ;;
+  * )
+    echo
+    echo "ERROR: Don't know how to build DEBs for '$TARGETARCH'."
+    echo
+    exit 1
+    ;;
+esac
+# TODO(michaelpg): Get a working repo URL.
+BASEREPOCONFIG="dl.google.com/linux/app-shell/deb/ stable main"
+# Only use the default REPOCONFIG if it's unset (e.g. verify_channel might have
+# set it to an empty string)
+REPOCONFIG="${REPOCONFIG-deb [arch=${ARCHITECTURE}] http://${BASEREPOCONFIG}}"
+# Allowed configs include optional HTTPS support and explicit multiarch
+# platforms.
+REPOCONFIGREGEX="deb (\\\\[arch=[^]]*\\\\b${ARCHITECTURE}\\\\b[^]]*\\\\]"
+REPOCONFIGREGEX+="[[:space:]]*) https?://${BASEREPOCONFIG}"
+stage_install_debian
+
+do_package
+verify_package
diff --git a/extensions/shell/installer/linux/debian/expected_deps_x64_jessie b/extensions/shell/installer/linux/debian/expected_deps_x64_jessie
new file mode 100644
index 0000000..5fc6eff
--- /dev/null
+++ b/extensions/shell/installer/linux/debian/expected_deps_x64_jessie
@@ -0,0 +1,28 @@
+gconf-service
+libasound2 (>= 1.0.16)
+libatk1.0-0 (>= 1.12.4)
+libc6 (>= 2.15)
+libdbus-1-3 (>= 1.1.4)
+libexpat1 (>= 2.0.1)
+libfontconfig1 (>= 2.11)
+libgcc1 (>= 1:4.1.1)
+libgconf-2-4 (>= 3.2.5)
+libglib2.0-0 (>= 2.14.0)
+libnspr4 (>= 2:4.9-2~)
+libnss3 (>= 2:3.13.4-2~)
+libpango-1.0-0 (>= 1.14.0)
+libpangocairo-1.0-0 (>= 1.14.0)
+libstdc++6 (>= 4.8.1)
+libx11-6 (>= 2:1.4.99.1)
+libx11-xcb1
+libxcb1 (>= 1.6)
+libxcomposite1 (>= 1:0.3-1)
+libxcursor1 (>> 1.1.2)
+libxdamage1 (>= 1:1.1)
+libxext6
+libxfixes3
+libxi6 (>= 2:1.2.99.4)
+libxrandr2
+libxrender1
+libxss1
+libxtst6
diff --git a/extensions/shell/installer/linux/debian/postinst b/extensions/shell/installer/linux/debian/postinst
new file mode 100755
index 0000000..68aa893
--- /dev/null
+++ b/extensions/shell/installer/linux/debian/postinst
@@ -0,0 +1,22 @@
+#!/bin/sh
+#
+# 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.
+
+set -e
+
+@@include@@../common/apt.include
+
+@@include@@../common/symlinks.include
+
+remove_udev_symlinks
+
+## MAIN ##
+if [ ! -e "$DEFAULTS_FILE" ]; then
+  echo 'repo_add_once="true"' > "$DEFAULTS_FILE"
+  echo 'repo_reenable_on_distupgrade="true"' >> "$DEFAULTS_FILE"
+fi
+
+# Run the cron job immediately to perform repository configuration.
+nohup sh /etc/cron.daily/@@PACKAGE@@ > /dev/null 2>&1 &
diff --git a/extensions/shell/installer/linux/debian/postrm b/extensions/shell/installer/linux/debian/postrm
new file mode 100755
index 0000000..6d369e7
--- /dev/null
+++ b/extensions/shell/installer/linux/debian/postrm
@@ -0,0 +1,33 @@
+#!/bin/sh
+#
+# 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.
+
+set -e
+
+action="$1"
+
+# Only do complete clean-up on purge.
+if [ "$action" != "purge" ] ; then
+  exit 0
+fi
+
+@@include@@../common/apt.include
+
+@@include@@../common/symlinks.include
+
+remove_udev_symlinks
+
+# Only remove the defaults file if it is not empty. An empty file was probably
+# put there by the sysadmin to disable automatic repository configuration, as
+# per the instructions on the package download page.
+if [ -s "$DEFAULTS_FILE" ]; then
+  # Make sure the package defaults are removed before the repository config,
+  # otherwise it could result in the repository config being removed, but the
+  # package defaults remain and are set to not recreate the repository config.
+  # In that case, future installs won't recreate it and won't get auto-updated.
+  rm "$DEFAULTS_FILE" || exit 1
+fi
+# Remove any Google repository added by the package.
+clean_sources_lists
diff --git a/gpu/command_buffer/service/scheduler.cc b/gpu/command_buffer/service/scheduler.cc
index ca080e4..2b4bfc3 100644
--- a/gpu/command_buffer/service/scheduler.cc
+++ b/gpu/command_buffer/service/scheduler.cc
@@ -366,9 +366,6 @@
   DCHECK(thread_checker_.CalledOnValidThread());
   base::AutoLock auto_lock(lock_);
 
-  if (should_yield_)
-    return true;
-
   RebuildSchedulingQueue();
 
   if (scheduling_queue_.empty())
@@ -382,9 +379,7 @@
   DCHECK(next_sequence);
   DCHECK(next_sequence->scheduled());
 
-  should_yield_ = running_sequence->ShouldYieldTo(next_sequence);
-
-  return should_yield_;
+  return running_sequence->ShouldYieldTo(next_sequence);
 }
 
 void Scheduler::SyncTokenFenceReleased(const SyncToken& sync_token,
@@ -456,8 +451,6 @@
   DCHECK(thread_checker_.CalledOnValidThread());
   base::AutoLock auto_lock(lock_);
 
-  should_yield_ = false;
-
   RebuildSchedulingQueue();
 
   if (scheduling_queue_.empty()) {
diff --git a/gpu/command_buffer/service/scheduler.h b/gpu/command_buffer/service/scheduler.h
index c6dab1cf..4aa3c70 100644
--- a/gpu/command_buffer/service/scheduler.h
+++ b/gpu/command_buffer/service/scheduler.h
@@ -118,10 +118,6 @@
   // SchedulingState with highest priority (lowest order) in front.
   std::vector<SchedulingState> scheduling_queue_;
 
-  // If the running sequence should yield so that a higher priority sequence can
-  // run.
-  bool should_yield_ = false;
-
   // If the scheduling queue needs to be rebuild because a sequence changed
   // priority.
   bool rebuild_scheduling_queue_ = false;
diff --git a/gpu/config/gpu_driver_bug_list.json b/gpu/config/gpu_driver_bug_list.json
index 1dfa1679f..e13c91ee 100644
--- a/gpu/config/gpu_driver_bug_list.json
+++ b/gpu/config/gpu_driver_bug_list.json
@@ -1,6 +1,6 @@
 {
   "name": "gpu driver bug list",
-  "version": "10.12",
+  "version": "10.13",
   "entries": [
     {
       "id": 1,
@@ -2496,6 +2496,34 @@
       "features": [
         "multisample_renderbuffer_resize_emulation"
       ]
+    },
+    {
+      "id": 232,
+      "description": "Delayed copy NV12 crashes on Intel on Windows <= 8.1.",
+      "cr_bugs": [727216],
+      "os": {
+        "type": "win",
+        "version": {
+          "op": "<=",
+          "value": "8.1"
+        }
+      },
+      "vendor_id": "0x8086",
+      "features": [
+        "disable_delayed_copy_nv12"
+      ]
+    },
+    {
+      "id": 233,
+      "description": "Delayed copy NV12 displays incorrect colors on NVIDIA drivers.",
+      "cr_bugs": [728670],
+      "os": {
+        "type": "win"
+      },
+      "vendor_id": "0x10de",
+      "features": [
+        "disable_delayed_copy_nv12"
+      ]
     }
   ],
   "comment": [
diff --git a/gpu/config/gpu_driver_bug_workaround_type.h b/gpu/config/gpu_driver_bug_workaround_type.h
index 5b0f4eb..f252881 100644
--- a/gpu/config/gpu_driver_bug_workaround_type.h
+++ b/gpu/config/gpu_driver_bug_workaround_type.h
@@ -51,6 +51,8 @@
          disable_chromium_framebuffer_multisample)           \
   GPU_OP(DISABLE_D3D11,                                      \
          disable_d3d11)                                      \
+  GPU_OP(DISABLE_DELAYED_COPY_NV12,                          \
+         disable_delayed_copy_nv12)                          \
   GPU_OP(DISABLE_DEPTH_TEXTURE,                              \
          disable_depth_texture)                              \
   GPU_OP(DISABLE_DIRECT_COMPOSITION,                         \
diff --git a/ios/OWNERS b/ios/OWNERS
index 0fbae9fe..a5fae73 100644
--- a/ios/OWNERS
+++ b/ios/OWNERS
@@ -4,6 +4,9 @@
 rohitrao@chromium.org
 sdefresne@chromium.org
 
+# US-Pacific backup owner when no one on the above list is available
+pkl@chromium.org
+
 # per-file rules:
 # These are for the common case of adding or renaming files. If you're doing
 # structural changes, please get a review from a reviewer in this file.
diff --git a/ios/chrome/browser/ui/payments/address_edit_coordinator.mm b/ios/chrome/browser/ui/payments/address_edit_coordinator.mm
index 9ac3976..37e6d22 100644
--- a/ios/chrome/browser/ui/payments/address_edit_coordinator.mm
+++ b/ios/chrome/browser/ui/payments/address_edit_coordinator.mm
@@ -7,11 +7,14 @@
 #include "base/guid.h"
 #include "base/logging.h"
 #include "base/strings/sys_string_conversions.h"
+#include "components/autofill/core/browser/autofill_country.h"
 #include "components/autofill/core/browser/autofill_profile.h"
 #include "components/autofill/core/browser/personal_data_manager.h"
+#include "components/autofill/core/browser/validation.h"
 #include "components/autofill/core/common/autofill_constants.h"
 #include "components/payments/core/payments_profile_comparator.h"
 #include "components/strings/grit/components_strings.h"
+#include "ios/chrome/browser/application_context.h"
 #include "ios/chrome/browser/payments/payment_request.h"
 #import "ios/chrome/browser/ui/autofill/autofill_ui_type_util.h"
 #import "ios/chrome/browser/ui/payments/address_edit_mediator.h"
@@ -82,7 +85,22 @@
 - (NSString*)paymentRequestEditViewController:
                  (PaymentRequestEditViewController*)controller
                                 validateField:(EditorField*)field {
-  if (!field.value.length && field.isRequired) {
+  if (field.value.length) {
+    switch (field.autofillUIType) {
+      case AutofillUITypeProfileHomePhoneWholeNumber: {
+        const std::string selectedCountryCode =
+            base::SysNSStringToUTF8(self.mediator.selectedCountryCode);
+        if (!autofill::IsValidPhoneNumber(base::SysNSStringToUTF16(field.value),
+                                          selectedCountryCode)) {
+          return l10n_util::GetNSString(
+              IDS_PAYMENTS_PHONE_INVALID_VALIDATION_MESSAGE);
+        }
+        break;
+      }
+      default:
+        break;
+    }
+  } else if (field.isRequired) {
     return l10n_util::GetNSString(
         IDS_PAYMENTS_FIELD_REQUIRED_VALIDATION_MESSAGE);
   }
diff --git a/media/gpu/dxva_video_decode_accelerator_win.cc b/media/gpu/dxva_video_decode_accelerator_win.cc
index 47b9343..764a7c2 100644
--- a/media/gpu/dxva_video_decode_accelerator_win.cc
+++ b/media/gpu/dxva_video_decode_accelerator_win.cc
@@ -501,6 +501,9 @@
           !workarounds.disable_dxgi_zero_copy_video),
       support_copy_nv12_textures_(gpu_preferences.enable_nv12_dxgi_video &&
                                   !workarounds.disable_nv12_dxgi_video),
+      support_delayed_copy_nv12_textures_(
+          base::FeatureList::IsEnabled(kDelayCopyNV12Textures) &&
+          !workarounds.disable_delayed_copy_nv12),
       use_dx11_(false),
       use_keyed_mutex_(false),
       using_angle_device_(false),
@@ -3053,8 +3056,7 @@
     return PictureBufferMechanism::COPY_TO_RGB;
   if (support_share_nv12_textures_)
     return PictureBufferMechanism::BIND;
-  if (base::FeatureList::IsEnabled(kDelayCopyNV12Textures) &&
-      support_copy_nv12_textures_)
+  if (support_delayed_copy_nv12_textures_ && support_copy_nv12_textures_)
     return PictureBufferMechanism::DELAYED_COPY_TO_NV12;
   if (support_copy_nv12_textures_)
     return PictureBufferMechanism::COPY_TO_NV12;
diff --git a/media/gpu/dxva_video_decode_accelerator_win.h b/media/gpu/dxva_video_decode_accelerator_win.h
index 69aee2a..006f188 100644
--- a/media/gpu/dxva_video_decode_accelerator_win.h
+++ b/media/gpu/dxva_video_decode_accelerator_win.h
@@ -549,6 +549,9 @@
   // ANGLE.
   bool support_copy_nv12_textures_;
 
+  // Supports copying NV12 textures on the main thread to use in ANGLE.
+  bool support_delayed_copy_nv12_textures_;
+
   // Copy video to FP16 scRGB textures.
   bool use_fp16_ = false;
 
diff --git a/net/http/bidirectional_stream_unittest.cc b/net/http/bidirectional_stream_unittest.cc
index 037a21e..7a7407ed 100644
--- a/net/http/bidirectional_stream_unittest.cc
+++ b/net/http/bidirectional_stream_unittest.cc
@@ -1658,7 +1658,7 @@
   ASSERT_EQ(1u, alternative_service_info_vector.size());
   AlternativeService alternative_service(kProtoQUIC, "www.example.org", 443);
   EXPECT_EQ(alternative_service,
-            alternative_service_info_vector[0].alternative_service);
+            alternative_service_info_vector[0].alternative_service());
 }
 
 }  // namespace net
diff --git a/net/http/http_network_transaction_unittest.cc b/net/http/http_network_transaction_unittest.cc
index 0056e9f..c5a32fe 100644
--- a/net/http/http_network_transaction_unittest.cc
+++ b/net/http/http_network_transaction_unittest.cc
@@ -10110,7 +10110,7 @@
   ASSERT_EQ(1u, alternative_service_info_vector.size());
   AlternativeService alternative_service(kProtoHTTP2, "mail.example.org", 443);
   EXPECT_EQ(alternative_service,
-            alternative_service_info_vector[0].alternative_service);
+            alternative_service_info_vector[0].alternative_service());
 }
 
 // Regression test for https://crbug.com/615497.
@@ -10352,11 +10352,11 @@
 
   AlternativeService alternative_service(kProtoHTTP2, "www.example.com", 443);
   EXPECT_EQ(alternative_service,
-            alternative_service_info_vector[0].alternative_service);
+            alternative_service_info_vector[0].alternative_service());
   AlternativeService alternative_service_2(kProtoHTTP2, "www.example.org",
                                            1234);
   EXPECT_EQ(alternative_service_2,
-            alternative_service_info_vector[1].alternative_service);
+            alternative_service_info_vector[1].alternative_service());
 }
 
 TEST_F(HttpNetworkTransactionTest, IdentifyQuicBroken) {
@@ -10543,7 +10543,7 @@
       http_server_properties->GetAlternativeServiceInfos(server);
   ASSERT_EQ(1u, alternative_service_info_vector.size());
   EXPECT_EQ(alternative_service,
-            alternative_service_info_vector[0].alternative_service);
+            alternative_service_info_vector[0].alternative_service());
   EXPECT_TRUE(
       http_server_properties->IsAlternativeServiceBroken(alternative_service));
 }
diff --git a/net/http/http_server_properties.cc b/net/http/http_server_properties.cc
index 386a00d0..15947b2 100644
--- a/net/http/http_server_properties.cc
+++ b/net/http/http_server_properties.cc
@@ -78,6 +78,25 @@
   return false;
 }
 
+AlternativeServiceInfo::AlternativeServiceInfo() : alternative_service_() {}
+
+AlternativeServiceInfo::AlternativeServiceInfo(
+    const AlternativeService& alternative_service,
+    base::Time expiration)
+    : alternative_service_(alternative_service), expiration_(expiration) {}
+
+AlternativeServiceInfo::AlternativeServiceInfo(NextProto protocol,
+                                               const std::string& host,
+                                               uint16_t port,
+                                               base::Time expiration)
+    : alternative_service_(protocol, host, port), expiration_(expiration) {}
+
+AlternativeServiceInfo::AlternativeServiceInfo(
+    const AlternativeServiceInfo& alternative_service_info) = default;
+
+AlternativeServiceInfo& AlternativeServiceInfo::operator=(
+    const AlternativeServiceInfo& alternative_service_info) = default;
+
 std::string AlternativeService::ToString() const {
   return base::StringPrintf("%s %s:%d", NextProtoToString(protocol),
                             host.c_str(), port);
@@ -85,10 +104,10 @@
 
 std::string AlternativeServiceInfo::ToString() const {
   base::Time::Exploded exploded;
-  expiration.LocalExplode(&exploded);
+  expiration_.LocalExplode(&exploded);
   return base::StringPrintf(
       "%s, expires %04d-%02d-%02d %02d:%02d:%02d",
-      alternative_service.ToString().c_str(), exploded.year, exploded.month,
+      alternative_service_.ToString().c_str(), exploded.year, exploded.month,
       exploded.day_of_month, exploded.hour, exploded.minute, exploded.second);
 }
 
diff --git a/net/http/http_server_properties.h b/net/http/http_server_properties.h
index fc19d24..27d8b07 100644
--- a/net/http/http_server_properties.h
+++ b/net/http/http_server_properties.h
@@ -114,28 +114,26 @@
     std::ostream& os,
     const AlternativeService& alternative_service);
 
-struct NET_EXPORT AlternativeServiceInfo {
-  AlternativeServiceInfo() : alternative_service() {}
+class NET_EXPORT_PRIVATE AlternativeServiceInfo {
+ public:
+  AlternativeServiceInfo();
 
   AlternativeServiceInfo(const AlternativeService& alternative_service,
-                         base::Time expiration)
-      : alternative_service(alternative_service),
-        expiration(expiration) {}
+                         base::Time expiration);
 
   AlternativeServiceInfo(NextProto protocol,
                          const std::string& host,
                          uint16_t port,
-                         base::Time expiration)
-      : alternative_service(protocol, host, port), expiration(expiration) {}
+                         base::Time expiration);
 
   AlternativeServiceInfo(
-      const AlternativeServiceInfo& alternative_service_info) = default;
+      const AlternativeServiceInfo& alternative_service_info);
   AlternativeServiceInfo& operator=(
-      const AlternativeServiceInfo& alternative_service_info) = default;
+      const AlternativeServiceInfo& alternative_service_info);
 
   bool operator==(const AlternativeServiceInfo& other) const {
-    return alternative_service == other.alternative_service &&
-           expiration == other.expiration;
+    return alternative_service_ == other.alternative_service() &&
+           expiration_ == other.expiration();
   }
 
   bool operator!=(const AlternativeServiceInfo& other) const {
@@ -144,8 +142,31 @@
 
   std::string ToString() const;
 
-  AlternativeService alternative_service;
-  base::Time expiration;
+  void set_alternative_service(const AlternativeService& alternative_service) {
+    alternative_service_ = alternative_service;
+  }
+
+  void set_protocol(const NextProto& protocol) {
+    alternative_service_.protocol = protocol;
+  }
+
+  void set_host(const std::string& host) { alternative_service_.host = host; }
+
+  void set_port(uint16_t port) { alternative_service_.port = port; }
+
+  void set_expiration(const base::Time& expiration) {
+    expiration_ = expiration;
+  }
+
+  const AlternativeService& alternative_service() const {
+    return alternative_service_;
+  }
+
+  base::Time expiration() const { return expiration_; }
+
+ private:
+  AlternativeService alternative_service_;
+  base::Time expiration_;
 };
 
 struct NET_EXPORT SupportsQuic {
diff --git a/net/http/http_server_properties_impl.cc b/net/http/http_server_properties_impl.cc
index 4220ce2d..4140e1d 100644
--- a/net/http/http_server_properties_impl.cc
+++ b/net/http/http_server_properties_impl.cc
@@ -222,7 +222,7 @@
       GetAlternativeServiceInfos(server);
   for (const AlternativeServiceInfo& alternative_service_info :
        alternative_service_info_vector) {
-    if (alternative_service_info.alternative_service.protocol == kProtoQUIC) {
+    if (alternative_service_info.alternative_service().protocol == kProtoQUIC) {
       return true;
     }
   }
@@ -307,11 +307,11 @@
     HostPortPair host_port_pair(origin.host(), origin.port());
     for (AlternativeServiceInfoVector::iterator it = map_it->second.begin();
          it != map_it->second.end();) {
-      if (it->expiration < now) {
+      if (it->expiration() < now) {
         it = map_it->second.erase(it);
         continue;
       }
-      AlternativeService alternative_service(it->alternative_service);
+      AlternativeService alternative_service(it->alternative_service());
       if (alternative_service.host.empty()) {
         alternative_service.host = origin.host();
       }
@@ -323,7 +323,7 @@
         continue;
       }
       valid_alternative_service_infos.push_back(
-          AlternativeServiceInfo(alternative_service, it->expiration));
+          AlternativeServiceInfo(alternative_service, it->expiration()));
       ++it;
     }
     if (map_it->second.empty()) {
@@ -342,11 +342,11 @@
   }
   for (AlternativeServiceInfoVector::iterator it = map_it->second.begin();
        it != map_it->second.end();) {
-    if (it->expiration < now) {
+    if (it->expiration() < now) {
       it = map_it->second.erase(it);
       continue;
     }
-    AlternativeService alternative_service(it->alternative_service);
+    AlternativeService alternative_service(it->alternative_service());
     if (alternative_service.host.empty()) {
       alternative_service.host = canonical->second.host();
       if (IsAlternativeServiceBroken(alternative_service)) {
@@ -359,7 +359,7 @@
       continue;
     }
     valid_alternative_service_infos.push_back(
-        AlternativeServiceInfo(alternative_service, it->expiration));
+        AlternativeServiceInfo(alternative_service, it->expiration()));
     ++it;
   }
   if (map_it->second.empty()) {
@@ -402,14 +402,14 @@
       for (const auto& old : it->second) {
         // Persist to disk immediately if new entry has different scheme, host,
         // or port.
-        if (old.alternative_service != new_it->alternative_service) {
+        if (old.alternative_service() != new_it->alternative_service()) {
           changed = true;
           break;
         }
         // Also persist to disk if new expiration it more that twice as far or
         // less than half as far in the future.
-        base::Time old_time = old.expiration;
-        base::Time new_time = new_it->expiration;
+        base::Time old_time = old.expiration();
+        base::Time new_time = new_it->expiration();
         if (new_time - now > 2 * (old_time - now) ||
             2 * (new_time - now) < (old_time - now)) {
           changed = true;
@@ -494,7 +494,7 @@
       std::string alternative_service_string(
           alternative_service_info.ToString());
       AlternativeService alternative_service(
-          alternative_service_info.alternative_service);
+          alternative_service_info.alternative_service());
       if (alternative_service.host.empty()) {
         alternative_service.host = server.host();
       }
@@ -630,7 +630,7 @@
 
   for (const AlternativeServiceInfo& alternative_service_info : it->second) {
     AlternativeService alternative_service(
-        alternative_service_info.alternative_service);
+        alternative_service_info.alternative_service());
     if (alternative_service.host.empty()) {
       alternative_service.host = canonical_server.host();
     }
@@ -677,7 +677,7 @@
        map_it != alternative_service_map_.end();) {
     for (AlternativeServiceInfoVector::iterator it = map_it->second.begin();
          it != map_it->second.end();) {
-      AlternativeService alternative_service(it->alternative_service);
+      AlternativeService alternative_service(it->alternative_service());
       // Empty hostname in map means hostname of key: substitute before
       // comparing to |expired_alternative_service|.
       if (alternative_service.host.empty()) {
diff --git a/net/http/http_server_properties_impl_unittest.cc b/net/http/http_server_properties_impl_unittest.cc
index 46cd0df..a144e67 100644
--- a/net/http/http_server_properties_impl_unittest.cc
+++ b/net/http/http_server_properties_impl_unittest.cc
@@ -379,7 +379,7 @@
       impl_.GetAlternativeServiceInfos(test_server);
   ASSERT_EQ(1u, alternative_service_info_vector.size());
   EXPECT_EQ(alternative_service,
-            alternative_service_info_vector[0].alternative_service);
+            alternative_service_info_vector[0].alternative_service());
 
   impl_.Clear();
   EXPECT_FALSE(HasAlternativeService(test_server));
@@ -475,18 +475,18 @@
 
   EXPECT_TRUE(map_it->first.Equals(test_server2));
   ASSERT_EQ(1u, map_it->second.size());
-  EXPECT_EQ(alternative_service3, map_it->second[0].alternative_service);
-  EXPECT_EQ(expiration3, map_it->second[0].expiration);
+  EXPECT_EQ(alternative_service3, map_it->second[0].alternative_service());
+  EXPECT_EQ(expiration3, map_it->second[0].expiration());
   ++map_it;
   EXPECT_TRUE(map_it->first.Equals(test_server1));
   ASSERT_EQ(1u, map_it->second.size());
-  EXPECT_EQ(alternative_service1, map_it->second[0].alternative_service);
-  EXPECT_EQ(expiration1, map_it->second[0].expiration);
+  EXPECT_EQ(alternative_service1, map_it->second[0].alternative_service());
+  EXPECT_EQ(expiration1, map_it->second[0].expiration());
   ++map_it;
   EXPECT_TRUE(map_it->first.Equals(test_server3));
   ASSERT_EQ(1u, map_it->second.size());
-  EXPECT_EQ(alternative_service4, map_it->second[0].alternative_service);
-  EXPECT_EQ(expiration4, map_it->second[0].expiration);
+  EXPECT_EQ(alternative_service4, map_it->second[0].alternative_service());
+  EXPECT_EQ(expiration4, map_it->second[0].expiration());
 }
 
 // Regression test for https://crbug.com/504032:
@@ -511,7 +511,7 @@
       impl_.GetAlternativeServiceInfos(server);
   ASSERT_EQ(1u, alternative_service_info_vector.size());
   EXPECT_EQ(alternative_service_with_foo_hostname,
-            alternative_service_info_vector[0].alternative_service);
+            alternative_service_info_vector[0].alternative_service());
 }
 
 // Regression test for https://crbug.com/516486:
@@ -600,8 +600,8 @@
       impl_.GetAlternativeServiceInfos(server);
   ASSERT_EQ(1u, alternative_service_info_vector.size());
   EXPECT_EQ(kProtoQUIC,
-            alternative_service_info_vector[0].alternative_service.protocol);
-  EXPECT_EQ(443, alternative_service_info_vector[0].alternative_service.port);
+            alternative_service_info_vector[0].alternative_service().protocol);
+  EXPECT_EQ(443, alternative_service_info_vector[0].alternative_service().port);
 
   // Now clear the alternatives for the other server and make sure it stays
   // cleared.
@@ -624,19 +624,19 @@
   AlternativeServiceMap::const_iterator it = map.begin();
   EXPECT_TRUE(it->first.Equals(test_server2));
   ASSERT_EQ(1u, it->second.size());
-  EXPECT_EQ(alternative_service2, it->second[0].alternative_service);
+  EXPECT_EQ(alternative_service2, it->second[0].alternative_service());
 
   const AlternativeServiceInfoVector alternative_service_info_vector =
       impl_.GetAlternativeServiceInfos(test_server1);
   ASSERT_EQ(1u, alternative_service_info_vector.size());
   EXPECT_EQ(alternative_service1,
-            alternative_service_info_vector[0].alternative_service);
+            alternative_service_info_vector[0].alternative_service());
 
   // GetAlternativeServices should reorder the AlternateProtocol map.
   it = map.begin();
   EXPECT_TRUE(it->first.Equals(test_server1));
   ASSERT_EQ(1u, it->second.size());
-  EXPECT_EQ(alternative_service1, it->second[0].alternative_service);
+  EXPECT_EQ(alternative_service1, it->second[0].alternative_service());
 }
 
 TEST_F(AlternateProtocolServerPropertiesTest, SetBroken) {
@@ -647,7 +647,7 @@
       impl_.GetAlternativeServiceInfos(test_server);
   ASSERT_EQ(1u, alternative_service_info_vector.size());
   EXPECT_EQ(alternative_service1,
-            alternative_service_info_vector[0].alternative_service);
+            alternative_service_info_vector[0].alternative_service());
   EXPECT_FALSE(impl_.IsAlternativeServiceBroken(alternative_service1));
 
   // GetAlternativeServiceInfos should return the broken alternative service.
@@ -656,7 +656,7 @@
       impl_.GetAlternativeServiceInfos(test_server);
   ASSERT_EQ(1u, alternative_service_info_vector.size());
   EXPECT_EQ(alternative_service1,
-            alternative_service_info_vector[0].alternative_service);
+            alternative_service_info_vector[0].alternative_service());
   EXPECT_TRUE(impl_.IsAlternativeServiceBroken(alternative_service1));
 
   // SetAlternativeServices should add a broken alternative service to the map.
@@ -672,9 +672,9 @@
       impl_.GetAlternativeServiceInfos(test_server);
   ASSERT_EQ(2u, alternative_service_info_vector.size());
   EXPECT_EQ(alternative_service1,
-            alternative_service_info_vector[0].alternative_service);
+            alternative_service_info_vector[0].alternative_service());
   EXPECT_EQ(alternative_service2,
-            alternative_service_info_vector[1].alternative_service);
+            alternative_service_info_vector[1].alternative_service());
   EXPECT_TRUE(impl_.IsAlternativeServiceBroken(alternative_service1));
   EXPECT_FALSE(impl_.IsAlternativeServiceBroken(alternative_service2));
 
@@ -684,7 +684,7 @@
       impl_.GetAlternativeServiceInfos(test_server);
   ASSERT_EQ(1u, alternative_service_info_vector.size());
   EXPECT_EQ(alternative_service1,
-            alternative_service_info_vector[0].alternative_service);
+            alternative_service_info_vector[0].alternative_service());
   EXPECT_TRUE(impl_.IsAlternativeServiceBroken(alternative_service1));
 }
 
@@ -712,7 +712,7 @@
       impl_.GetAlternativeServiceInfos(test_server);
   ASSERT_EQ(1u, alternative_service_info_vector2.size());
   EXPECT_EQ(alternative_service2,
-            alternative_service_info_vector2[0].alternative_service);
+            alternative_service_info_vector2[0].alternative_service());
 }
 
 TEST_F(AlternateProtocolServerPropertiesTest, MaxAgeCanonical) {
@@ -741,7 +741,7 @@
       impl_.GetAlternativeServiceInfos(test_server);
   ASSERT_EQ(1u, alternative_service_info_vector2.size());
   EXPECT_EQ(alternative_service2,
-            alternative_service_info_vector2[0].alternative_service);
+            alternative_service_info_vector2[0].alternative_service());
 }
 
 TEST_F(AlternateProtocolServerPropertiesTest, AlternativeServiceWithScheme) {
@@ -761,8 +761,8 @@
   net::AlternativeServiceMap::const_iterator it = map.begin();
   EXPECT_TRUE(it->first.Equals(http_server));
   ASSERT_EQ(2u, it->second.size());
-  EXPECT_EQ(alternative_service1, it->second[0].alternative_service);
-  EXPECT_EQ(alternative_service2, it->second[1].alternative_service);
+  EXPECT_EQ(alternative_service1, it->second[0].alternative_service());
+  EXPECT_EQ(alternative_service2, it->second[1].alternative_service());
 
   // Check Alt-Svc list should not be set for |https_server|.
   url::SchemeHostPort https_server("https", "foo", 80);
@@ -796,8 +796,8 @@
   net::AlternativeServiceMap::const_iterator it = map.begin();
   EXPECT_TRUE(it->first.Equals(test_server));
   ASSERT_EQ(2u, it->second.size());
-  EXPECT_EQ(alternative_service1, it->second[0].alternative_service);
-  EXPECT_EQ(alternative_service2, it->second[1].alternative_service);
+  EXPECT_EQ(alternative_service1, it->second[0].alternative_service());
+  EXPECT_EQ(alternative_service2, it->second[1].alternative_service());
 
   impl_.SetAlternativeServices(test_server, AlternativeServiceInfoVector());
   EXPECT_TRUE(map.empty());
@@ -817,7 +817,7 @@
       impl_.GetAlternativeServiceInfos(test_server);
   ASSERT_EQ(1u, alternative_service_info_vector.size());
   EXPECT_EQ(canonical_alternative_service,
-            alternative_service_info_vector[0].alternative_service);
+            alternative_service_info_vector[0].alternative_service());
 
   const AlternativeService broken_alternative_service(kProtoHTTP2, "foo", 443);
   impl_.MarkAlternativeServiceBroken(broken_alternative_service);
@@ -828,7 +828,7 @@
       impl_.GetAlternativeServiceInfos(test_server);
   ASSERT_EQ(1u, alternative_service_info_vector.size());
   EXPECT_EQ(broken_alternative_service,
-            alternative_service_info_vector[0].alternative_service);
+            alternative_service_info_vector[0].alternative_service());
   EXPECT_TRUE(impl_.IsAlternativeServiceBroken(broken_alternative_service));
 }
 
@@ -887,17 +887,17 @@
       impl_.GetAlternativeServiceInfos(test_server);
   ASSERT_EQ(2u, alternative_service_info_vector2.size());
   EXPECT_EQ(canonical_alternative_service1,
-            alternative_service_info_vector2[0].alternative_service);
+            alternative_service_info_vector2[0].alternative_service());
 
   // Since |canonical_alternative_service2| has an empty host,
   // GetAlternativeServiceInfos should substitute the hostname of its |origin|
   // argument.
   EXPECT_EQ(test_server.host(),
-            alternative_service_info_vector2[1].alternative_service.host);
+            alternative_service_info_vector2[1].alternative_service().host);
   EXPECT_EQ(canonical_alternative_service2.protocol,
-            alternative_service_info_vector2[1].alternative_service.protocol);
+            alternative_service_info_vector2[1].alternative_service().protocol);
   EXPECT_EQ(canonical_alternative_service2.port,
-            alternative_service_info_vector2[1].alternative_service.port);
+            alternative_service_info_vector2[1].alternative_service().port);
 
   // Verify the canonical suffix.
   EXPECT_EQ(".c.youtube.com", *impl_.GetCanonicalSuffix(test_server.host()));
@@ -939,7 +939,7 @@
       impl_.GetAlternativeServiceInfos(foo_server);
   ASSERT_EQ(1u, alternative_service_info_vector.size());
   EXPECT_EQ(bar_alternative_service,
-            alternative_service_info_vector[0].alternative_service);
+            alternative_service_info_vector[0].alternative_service());
 
   url::SchemeHostPort qux_server("https", "qux.c.youtube.com", 443);
   AlternativeService qux_alternative_service(kProtoQUIC, "qux.c.youtube.com",
@@ -949,7 +949,7 @@
       impl_.GetAlternativeServiceInfos(foo_server);
   ASSERT_EQ(1u, alternative_service_info_vector.size());
   EXPECT_EQ(qux_alternative_service,
-            alternative_service_info_vector[0].alternative_service);
+            alternative_service_info_vector[0].alternative_service());
 }
 
 TEST_F(AlternateProtocolServerPropertiesTest, ClearWithCanonical) {
diff --git a/net/http/http_server_properties_manager.cc b/net/http/http_server_properties_manager.cc
index 95ef2f4..6a4aaaa 100644
--- a/net/http/http_server_properties_manager.cc
+++ b/net/http/http_server_properties_manager.cc
@@ -587,17 +587,18 @@
              << server_str;
     return false;
   }
-  alternative_service_info->alternative_service.protocol = protocol;
+  alternative_service_info->set_protocol(protocol);
 
   // Host is optional, defaults to "".
-  alternative_service_info->alternative_service.host.clear();
+  std::string host = "";
   if (alternative_service_dict.HasKey(kHostKey) &&
-      !alternative_service_dict.GetStringWithoutPathExpansion(
-          kHostKey, &(alternative_service_info->alternative_service.host))) {
+      !alternative_service_dict.GetStringWithoutPathExpansion(kHostKey,
+                                                              &host)) {
     DVLOG(1) << "Malformed alternative service host string for server: "
              << server_str;
     return false;
   }
+  alternative_service_info->set_host(host);
 
   // Port is mandatory.
   int port = 0;
@@ -606,14 +607,13 @@
     DVLOG(1) << "Malformed alternative service port for server: " << server_str;
     return false;
   }
-  alternative_service_info->alternative_service.port =
-      static_cast<uint32_t>(port);
+  alternative_service_info->set_port(static_cast<uint32_t>(port));
 
   // Expiration is optional, defaults to one day.
   base::Time expiration;
   if (!alternative_service_dict.HasKey(kExpirationKey)) {
-    alternative_service_info->expiration =
-        base::Time::Now() + base::TimeDelta::FromDays(1);
+    alternative_service_info->set_expiration(base::Time::Now() +
+                                             base::TimeDelta::FromDays(1));
     return true;
   }
 
@@ -626,8 +626,8 @@
                << server_str;
       return false;
     }
-    alternative_service_info->expiration =
-        base::Time::FromInternalValue(expiration_int64);
+    alternative_service_info->set_expiration(
+        base::Time::FromInternalValue(expiration_int64));
     return true;
   }
 
@@ -663,7 +663,7 @@
                                      &alternative_service_info)) {
       return false;
     }
-    if (base::Time::Now() < alternative_service_info.expiration) {
+    if (base::Time::Now() < alternative_service_info.expiration()) {
       alternative_service_info_vector.push_back(alternative_service_info);
     }
   }
@@ -858,11 +858,11 @@
     AlternativeServiceInfoVector notbroken_alternative_service_info_vector;
     for (const AlternativeServiceInfo& alternative_service_info : it->second) {
       // Do not persist expired entries.
-      if (alternative_service_info.expiration < base::Time::Now()) {
+      if (alternative_service_info.expiration() < base::Time::Now()) {
         continue;
       }
       AlternativeService alternative_service(
-          alternative_service_info.alternative_service);
+          alternative_service_info.alternative_service());
       if (!IsAlternateProtocolValid(alternative_service.protocol)) {
         continue;
       }
@@ -1072,7 +1072,7 @@
   for (const AlternativeServiceInfo& alternative_service_info :
        *alternative_service_info_vector) {
     const AlternativeService alternative_service =
-        alternative_service_info.alternative_service;
+        alternative_service_info.alternative_service();
     DCHECK(IsAlternateProtocolValid(alternative_service.protocol));
     std::unique_ptr<base::DictionaryValue> alternative_service_dict(
         new base::DictionaryValue);
@@ -1086,7 +1086,7 @@
     alternative_service_dict->SetString(
         kExpirationKey,
         base::Int64ToString(
-            alternative_service_info.expiration.ToInternalValue()));
+            alternative_service_info.expiration().ToInternalValue()));
     alternative_service_list->Append(std::move(alternative_service_dict));
   }
   if (alternative_service_list->GetSize() == 0)
diff --git a/net/http/http_server_properties_manager_unittest.cc b/net/http/http_server_properties_manager_unittest.cc
index b10b337..4460fa76 100644
--- a/net/http/http_server_properties_manager_unittest.cc
+++ b/net/http/http_server_properties_manager_unittest.cc
@@ -414,18 +414,18 @@
     AlternativeServiceMap::const_iterator map_it = map.begin();
     EXPECT_EQ("mail.google.com", map_it->first.host());
     ASSERT_EQ(1u, map_it->second.size());
-    EXPECT_EQ(kProtoHTTP2, map_it->second[0].alternative_service.protocol);
-    EXPECT_TRUE(map_it->second[0].alternative_service.host.empty());
-    EXPECT_EQ(444, map_it->second[0].alternative_service.port);
+    EXPECT_EQ(kProtoHTTP2, map_it->second[0].alternative_service().protocol);
+    EXPECT_TRUE(map_it->second[0].alternative_service().host.empty());
+    EXPECT_EQ(444, map_it->second[0].alternative_service().port);
     ++map_it;
     EXPECT_EQ("www.google.com", map_it->first.host());
     ASSERT_EQ(2u, map_it->second.size());
-    EXPECT_EQ(kProtoHTTP2, map_it->second[0].alternative_service.protocol);
-    EXPECT_TRUE(map_it->second[0].alternative_service.host.empty());
-    EXPECT_EQ(443, map_it->second[0].alternative_service.port);
-    EXPECT_EQ(kProtoQUIC, map_it->second[1].alternative_service.protocol);
-    EXPECT_TRUE(map_it->second[1].alternative_service.host.empty());
-    EXPECT_EQ(1234, map_it->second[1].alternative_service.port);
+    EXPECT_EQ(kProtoHTTP2, map_it->second[0].alternative_service().protocol);
+    EXPECT_TRUE(map_it->second[0].alternative_service().host.empty());
+    EXPECT_EQ(443, map_it->second[0].alternative_service().port);
+    EXPECT_EQ(kProtoQUIC, map_it->second[1].alternative_service().protocol);
+    EXPECT_TRUE(map_it->second[1].alternative_service().host.empty());
+    EXPECT_EQ(1234, map_it->second[1].alternative_service().port);
   } else {
     const AlternativeServiceMap& map =
         http_server_props_manager_->alternative_service_map();
@@ -433,18 +433,18 @@
     AlternativeServiceMap::const_iterator map_it = map.begin();
     EXPECT_EQ("www.google.com", map_it->first.host());
     ASSERT_EQ(2u, map_it->second.size());
-    EXPECT_EQ(kProtoHTTP2, map_it->second[0].alternative_service.protocol);
-    EXPECT_TRUE(map_it->second[0].alternative_service.host.empty());
-    EXPECT_EQ(443, map_it->second[0].alternative_service.port);
-    EXPECT_EQ(kProtoQUIC, map_it->second[1].alternative_service.protocol);
-    EXPECT_TRUE(map_it->second[1].alternative_service.host.empty());
-    EXPECT_EQ(1234, map_it->second[1].alternative_service.port);
+    EXPECT_EQ(kProtoHTTP2, map_it->second[0].alternative_service().protocol);
+    EXPECT_TRUE(map_it->second[0].alternative_service().host.empty());
+    EXPECT_EQ(443, map_it->second[0].alternative_service().port);
+    EXPECT_EQ(kProtoQUIC, map_it->second[1].alternative_service().protocol);
+    EXPECT_TRUE(map_it->second[1].alternative_service().host.empty());
+    EXPECT_EQ(1234, map_it->second[1].alternative_service().port);
     ++map_it;
     EXPECT_EQ("mail.google.com", map_it->first.host());
     ASSERT_EQ(1u, map_it->second.size());
-    EXPECT_EQ(kProtoHTTP2, map_it->second[0].alternative_service.protocol);
-    EXPECT_TRUE(map_it->second[0].alternative_service.host.empty());
-    EXPECT_EQ(444, map_it->second[0].alternative_service.port);
+    EXPECT_EQ(kProtoHTTP2, map_it->second[0].alternative_service().protocol);
+    EXPECT_TRUE(map_it->second[0].alternative_service().host.empty());
+    EXPECT_EQ(444, map_it->second[0].alternative_service().port);
   }
 
   // Verify SupportsQuic.
@@ -749,7 +749,7 @@
       http_server_props_manager_->GetAlternativeServiceInfos(spdy_server_mail);
   ASSERT_EQ(1u, alternative_service_info_vector.size());
   EXPECT_EQ(alternative_service,
-            alternative_service_info_vector[0].alternative_service);
+            alternative_service_info_vector[0].alternative_service());
 }
 
 TEST_P(HttpServerPropertiesManagerTest, SetAlternativeServices) {
@@ -788,9 +788,9 @@
       http_server_props_manager_->GetAlternativeServiceInfos(spdy_server_mail);
   ASSERT_EQ(2u, alternative_service_info_vector2.size());
   EXPECT_EQ(alternative_service1,
-            alternative_service_info_vector2[0].alternative_service);
+            alternative_service_info_vector2[0].alternative_service());
   EXPECT_EQ(alternative_service2,
-            alternative_service_info_vector2[1].alternative_service);
+            alternative_service_info_vector2[1].alternative_service());
 }
 
 TEST_P(HttpServerPropertiesManagerTest, SetAlternativeServicesEmpty) {
@@ -1145,9 +1145,10 @@
     AlternativeServiceInfoVector alternative_service_info_vector =
         http_server_props_manager_->GetAlternativeServiceInfos(server);
     ASSERT_EQ(1u, alternative_service_info_vector.size());
-    EXPECT_EQ(kProtoQUIC,
-              alternative_service_info_vector[0].alternative_service.protocol);
-    EXPECT_EQ(i, alternative_service_info_vector[0].alternative_service.port);
+    EXPECT_EQ(
+        kProtoQUIC,
+        alternative_service_info_vector[0].alternative_service().protocol);
+    EXPECT_EQ(i, alternative_service_info_vector[0].alternative_service().port);
   }
 
   // Verify SupportsQuic.
@@ -1294,31 +1295,33 @@
   ASSERT_EQ(3u, alternative_service_info_vector.size());
 
   EXPECT_EQ(kProtoHTTP2,
-            alternative_service_info_vector[0].alternative_service.protocol);
-  EXPECT_EQ("", alternative_service_info_vector[0].alternative_service.host);
-  EXPECT_EQ(443, alternative_service_info_vector[0].alternative_service.port);
+            alternative_service_info_vector[0].alternative_service().protocol);
+  EXPECT_EQ("", alternative_service_info_vector[0].alternative_service().host);
+  EXPECT_EQ(443, alternative_service_info_vector[0].alternative_service().port);
   // Expiration defaults to one day from now, testing with tolerance.
   const base::Time now = base::Time::Now();
-  const base::Time expiration = alternative_service_info_vector[0].expiration;
+  const base::Time expiration = alternative_service_info_vector[0].expiration();
   EXPECT_LE(now + base::TimeDelta::FromHours(23), expiration);
   EXPECT_GE(now + base::TimeDelta::FromDays(1), expiration);
 
   EXPECT_EQ(kProtoQUIC,
-            alternative_service_info_vector[1].alternative_service.protocol);
-  EXPECT_EQ("", alternative_service_info_vector[1].alternative_service.host);
-  EXPECT_EQ(123, alternative_service_info_vector[1].alternative_service.port);
+            alternative_service_info_vector[1].alternative_service().protocol);
+  EXPECT_EQ("", alternative_service_info_vector[1].alternative_service().host);
+  EXPECT_EQ(123, alternative_service_info_vector[1].alternative_service().port);
   // numeric_limits<int64_t>::max() represents base::Time::Max().
-  EXPECT_EQ(base::Time::Max(), alternative_service_info_vector[1].expiration);
+  EXPECT_EQ(base::Time::Max(), alternative_service_info_vector[1].expiration());
 
   EXPECT_EQ(kProtoHTTP2,
-            alternative_service_info_vector[2].alternative_service.protocol);
+            alternative_service_info_vector[2].alternative_service().protocol);
   EXPECT_EQ("example.org",
-            alternative_service_info_vector[2].alternative_service.host);
-  EXPECT_EQ(1234, alternative_service_info_vector[2].alternative_service.port);
+            alternative_service_info_vector[2].alternative_service().host);
+  EXPECT_EQ(1234,
+            alternative_service_info_vector[2].alternative_service().port);
   base::Time expected_expiration;
   ASSERT_TRUE(
       base::Time::FromUTCString("2036-12-31 10:00:00", &expected_expiration));
-  EXPECT_EQ(expected_expiration, alternative_service_info_vector[2].expiration);
+  EXPECT_EQ(expected_expiration,
+            alternative_service_info_vector[2].expiration());
 }
 
 // Regression test for https://crbug.com/615497.
@@ -1459,11 +1462,11 @@
   ASSERT_EQ(1u, alternative_service_info_vector.size());
 
   EXPECT_EQ(kProtoHTTP2,
-            alternative_service_info_vector[0].alternative_service.protocol);
+            alternative_service_info_vector[0].alternative_service().protocol);
   EXPECT_EQ("valid.example.com",
-            alternative_service_info_vector[0].alternative_service.host);
-  EXPECT_EQ(443, alternative_service_info_vector[0].alternative_service.port);
-  EXPECT_EQ(one_day_from_now_, alternative_service_info_vector[0].expiration);
+            alternative_service_info_vector[0].alternative_service().host);
+  EXPECT_EQ(443, alternative_service_info_vector[0].alternative_service().port);
+  EXPECT_EQ(one_day_from_now_, alternative_service_info_vector[0].expiration());
 }
 
 TEST_P(HttpServerPropertiesManagerTest, ShutdownWithPendingUpdateCache0) {
diff --git a/net/http/http_stream_factory_impl_job_controller.cc b/net/http/http_stream_factory_impl_job_controller.cc
index a4575e09..69209d4 100644
--- a/net/http/http_stream_factory_impl_job_controller.cc
+++ b/net/http/http_stream_factory_impl_job_controller.cc
@@ -771,7 +771,7 @@
   // Create an alternative job if alternative service is set up for this domain.
   const AlternativeService alternative_service =
       GetAlternativeServiceInfoFor(request_info_, delegate_, stream_type_)
-          .alternative_service;
+          .alternative_service();
 
   if (is_preconnect_) {
     // Due to how the socket pools handle priorities and idle sockets, only IDLE
@@ -1031,19 +1031,20 @@
   AlternativeServiceInfo alternative_service_info =
       GetAlternativeServiceInfoInternal(request_info, delegate, stream_type);
   AlternativeServiceType type;
-  if (alternative_service_info.alternative_service.protocol == kProtoUnknown) {
+  if (alternative_service_info.alternative_service().protocol ==
+      kProtoUnknown) {
     type = NO_ALTERNATIVE_SERVICE;
-  } else if (alternative_service_info.alternative_service.protocol ==
+  } else if (alternative_service_info.alternative_service().protocol ==
              kProtoQUIC) {
     if (request_info.url.host_piece() ==
-        alternative_service_info.alternative_service.host) {
+        alternative_service_info.alternative_service().host) {
       type = QUIC_SAME_DESTINATION;
     } else {
       type = QUIC_DIFFERENT_DESTINATION;
     }
   } else {
     if (request_info.url.host_piece() ==
-        alternative_service_info.alternative_service.host) {
+        alternative_service_info.alternative_service().host) {
       type = NOT_QUIC_SAME_DESTINATION;
     } else {
       type = NOT_QUIC_DIFFERENT_DESTINATION;
@@ -1081,12 +1082,12 @@
   for (const AlternativeServiceInfo& alternative_service_info :
        alternative_service_info_vector) {
     DCHECK(IsAlternateProtocolValid(
-        alternative_service_info.alternative_service.protocol));
+        alternative_service_info.alternative_service().protocol));
     if (!quic_advertised &&
-        alternative_service_info.alternative_service.protocol == kProtoQUIC)
+        alternative_service_info.alternative_service().protocol == kProtoQUIC)
       quic_advertised = true;
     if (http_server_properties.IsAlternativeServiceBroken(
-            alternative_service_info.alternative_service)) {
+            alternative_service_info.alternative_service())) {
       HistogramAlternateProtocolUsage(ALTERNATE_PROTOCOL_USAGE_BROKEN, false);
       continue;
     }
@@ -1099,24 +1100,25 @@
     // allow protocol upgrades to user-controllable ports.
     const int kUnrestrictedPort = 1024;
     if (!session_->params().enable_user_alternate_protocol_ports &&
-        (alternative_service_info.alternative_service.port >=
+        (alternative_service_info.alternative_service().port >=
              kUnrestrictedPort &&
          origin.port() < kUnrestrictedPort))
       continue;
 
-    if (alternative_service_info.alternative_service.protocol == kProtoHTTP2) {
+    if (alternative_service_info.alternative_service().protocol ==
+        kProtoHTTP2) {
       if (!session_->params().enable_http2_alternative_service)
         continue;
 
       // Cache this entry if we don't have a non-broken Alt-Svc yet.
-      if (first_alternative_service_info.alternative_service.protocol ==
+      if (first_alternative_service_info.alternative_service().protocol ==
           kProtoUnknown)
         first_alternative_service_info = alternative_service_info;
       continue;
     }
 
     DCHECK_EQ(kProtoQUIC,
-              alternative_service_info.alternative_service.protocol);
+              alternative_service_info.alternative_service().protocol);
     quic_all_broken = false;
     if (!session_->IsQuicEnabled())
       continue;
@@ -1135,7 +1137,7 @@
     QuicServerId server_id(mapped_origin, request_info.privacy_mode);
 
     HostPortPair destination(
-        alternative_service_info.alternative_service.host_port_pair());
+        alternative_service_info.alternative_service().host_port_pair());
     ignore_result(ApplyHostMappingRules(original_url, &destination));
 
     if (session_->quic_stream_factory()->CanUseExistingSession(server_id,
@@ -1144,7 +1146,7 @@
     }
 
     // Cache this entry if we don't have a non-broken Alt-Svc yet.
-    if (first_alternative_service_info.alternative_service.protocol ==
+    if (first_alternative_service_info.alternative_service().protocol ==
         kProtoUnknown)
       first_alternative_service_info = alternative_service_info;
   }
diff --git a/net/http/http_stream_factory_impl_job_controller_unittest.cc b/net/http/http_stream_factory_impl_job_controller_unittest.cc
index cf7e526a5..4160bc8 100644
--- a/net/http/http_stream_factory_impl_job_controller_unittest.cc
+++ b/net/http/http_stream_factory_impl_job_controller_unittest.cc
@@ -266,7 +266,7 @@
     EXPECT_EQ(1u, alternative_service_info_vector.size());
     EXPECT_EQ(should_mark_broken,
               session_->http_server_properties()->IsAlternativeServiceBroken(
-                  alternative_service_info_vector[0].alternative_service));
+                  alternative_service_info_vector[0].alternative_service()));
   }
 
   TestJobFactory job_factory_;
diff --git a/net/quic/chromium/quic_network_transaction_unittest.cc b/net/quic/chromium/quic_network_transaction_unittest.cc
index 0d56c1d4..19412bc 100644
--- a/net/quic/chromium/quic_network_transaction_unittest.cc
+++ b/net/quic/chromium/quic_network_transaction_unittest.cc
@@ -633,7 +633,7 @@
         http_server_properties_.GetAlternativeServiceInfos(server);
     EXPECT_EQ(1u, alternative_service_info_vector.size());
     EXPECT_TRUE(http_server_properties_.IsAlternativeServiceBroken(
-        alternative_service_info_vector[0].alternative_service));
+        alternative_service_info_vector[0].alternative_service()));
   }
 
   void ExpectQuicAlternateProtocolMapping() {
@@ -641,10 +641,11 @@
     const AlternativeServiceInfoVector alternative_service_info_vector =
         http_server_properties_.GetAlternativeServiceInfos(server);
     EXPECT_EQ(1u, alternative_service_info_vector.size());
-    EXPECT_EQ(kProtoQUIC,
-              alternative_service_info_vector[0].alternative_service.protocol);
+    EXPECT_EQ(
+        kProtoQUIC,
+        alternative_service_info_vector[0].alternative_service().protocol);
     EXPECT_FALSE(http_server_properties_.IsAlternativeServiceBroken(
-        alternative_service_info_vector[0].alternative_service));
+        alternative_service_info_vector[0].alternative_service()));
   }
 
   void AddHangingNonAlternateProtocolSocketData() {
@@ -2997,7 +2998,7 @@
       http_server_properties_.GetAlternativeServiceInfos(http_server);
   ASSERT_EQ(1u, alternative_service_info_vector.size());
   const AlternativeService alternative_service =
-      alternative_service_info_vector[0].alternative_service;
+      alternative_service_info_vector[0].alternative_service();
   EXPECT_EQ(kProtoQUIC, alternative_service.protocol);
   EXPECT_EQ(kDefaultServerHostName, alternative_service.host);
   EXPECT_EQ(137, alternative_service.port);
diff --git a/net/spdy/chromium/spdy_session_unittest.cc b/net/spdy/chromium/spdy_session_unittest.cc
index ce7b9dd..7033354 100644
--- a/net/spdy/chromium/spdy_session_unittest.cc
+++ b/net/spdy/chromium/spdy_session_unittest.cc
@@ -5654,7 +5654,7 @@
   ASSERT_EQ(1u, altsvc_info_vector.size());
   AlternativeService alternative_service(kProtoQUIC, "alternative.example.org",
                                          443u);
-  EXPECT_EQ(alternative_service, altsvc_info_vector[0].alternative_service);
+  EXPECT_EQ(alternative_service, altsvc_info_vector[0].alternative_service());
 }
 
 TEST_F(AltSvcFrameTest, DoNotProcessAltSvcFrameOnInsecureSession) {
@@ -5797,10 +5797,10 @@
       spdy_session_pool_->http_server_properties()->GetAlternativeServiceInfos(
           url::SchemeHostPort(GURL(request_origin)));
   ASSERT_EQ(1u, altsvc_info_vector.size());
-  EXPECT_EQ(kProtoQUIC, altsvc_info_vector[0].alternative_service.protocol);
+  EXPECT_EQ(kProtoQUIC, altsvc_info_vector[0].alternative_service().protocol);
   EXPECT_EQ("alternative.example.org",
-            altsvc_info_vector[0].alternative_service.host);
-  EXPECT_EQ(443u, altsvc_info_vector[0].alternative_service.port);
+            altsvc_info_vector[0].alternative_service().host);
+  EXPECT_EQ(443u, altsvc_info_vector[0].alternative_service().port);
 }
 
 TEST_F(AltSvcFrameTest, DoNotProcessAltSvcFrameOnStreamWithInsecureOrigin) {
diff --git a/remoting/proto/BUILD.gn b/remoting/proto/BUILD.gn
index 64e0f23..b1c520aa 100644
--- a/remoting/proto/BUILD.gn
+++ b/remoting/proto/BUILD.gn
@@ -19,6 +19,7 @@
     "internal.proto",
     "mux.proto",
     "process_stats.proto",
+    "test.proto",
     "video.proto",
     "video_stats.proto",
   ]
diff --git a/remoting/proto/test.proto b/remoting/proto/test.proto
new file mode 100644
index 0000000..c02f408
--- /dev/null
+++ b/remoting/proto/test.proto
@@ -0,0 +1,17 @@
+// 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.
+
+// Protocol for test only.
+
+syntax = "proto2";
+
+option optimize_for = LITE_RUNTIME;
+
+package remoting.protocol;
+
+// The proto for DataChannelManager test cases.
+// Next Id: 2
+message DataChannelManagerTestProto {
+  optional int32 data = 1;
+}
diff --git a/remoting/protocol/BUILD.gn b/remoting/protocol/BUILD.gn
index f495575..b38627d 100644
--- a/remoting/protocol/BUILD.gn
+++ b/remoting/protocol/BUILD.gn
@@ -49,6 +49,8 @@
     "content_description.cc",
     "content_description.h",
     "datagram_channel_factory.h",
+    "data_channel_manager.cc",
+    "data_channel_manager.h",
     "errors.cc",
     "errors.h",
     "frame_consumer.h",
@@ -104,6 +106,8 @@
     "monitored_video_stub.h",
     "mouse_input_filter.cc",
     "mouse_input_filter.h",
+    "named_message_pipe_handler.cc",
+    "named_message_pipe_handler.h",
     "negotiating_authenticator_base.cc",
     "negotiating_authenticator_base.h",
     "negotiating_client_authenticator.cc",
@@ -266,6 +270,10 @@
     "fake_connection_to_host.h",
     "fake_datagram_socket.cc",
     "fake_datagram_socket.h",
+    "fake_message_pipe.cc",
+    "fake_message_pipe.h",
+    "fake_message_pipe_wrapper.cc",
+    "fake_message_pipe_wrapper.h",
     "fake_session.cc",
     "fake_session.h",
     "fake_stream_socket.cc",
@@ -309,6 +317,7 @@
     "connection_tester.cc",
     "connection_tester.h",
     "content_description_unittest.cc",
+    "data_channel_manager_unittest.cc",
     "http_ice_config_request_unittest.cc",
     "ice_config_unittest.cc",
     "ice_transport_unittest.cc",
diff --git a/remoting/protocol/data_channel_manager.cc b/remoting/protocol/data_channel_manager.cc
new file mode 100644
index 0000000..c8f0782
--- /dev/null
+++ b/remoting/protocol/data_channel_manager.cc
@@ -0,0 +1,39 @@
+// 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 "remoting/protocol/data_channel_manager.h"
+
+#include <utility>
+
+#include "base/logging.h"
+#include "remoting/protocol/message_pipe.h"
+
+namespace remoting {
+namespace protocol {
+
+DataChannelManager::DataChannelManager() = default;
+DataChannelManager::~DataChannelManager() = default;
+
+void DataChannelManager::RegisterCreateHandlerCallback(
+    const std::string& prefix,
+    CreateHandlerCallback constructor) {
+  DCHECK(!prefix.empty());
+  DCHECK(constructor);
+  constructors_.push_back(std::make_pair(prefix, constructor));
+}
+
+bool DataChannelManager::OnIncomingDataChannel(
+    const std::string& name,
+    std::unique_ptr<MessagePipe> pipe) {
+  for (auto& constructor : constructors_) {
+    if (name.find(constructor.first) == 0) {
+      constructor.second.Run(name, std::move(pipe));
+      return true;
+    }
+  }
+  return false;
+}
+
+}  // namespace protocol
+}  // namespace remoting
diff --git a/remoting/protocol/data_channel_manager.h b/remoting/protocol/data_channel_manager.h
new file mode 100644
index 0000000..32b0198
--- /dev/null
+++ b/remoting/protocol/data_channel_manager.h
@@ -0,0 +1,47 @@
+// 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 REMOTING_PROTOCOL_DATA_CHANNEL_MANAGER_H_
+#define REMOTING_PROTOCOL_DATA_CHANNEL_MANAGER_H_
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "base/callback.h"
+
+namespace remoting {
+namespace protocol {
+
+class MessagePipe;
+
+// DataChannelManager helps to manage optional data channels. Consumers can
+// register a function to handle data from a named data channel.
+class DataChannelManager final {
+ public:
+  using CreateHandlerCallback = base::Callback<void(
+      const std::string& name,
+      std::unique_ptr<MessagePipe> pipe)>;
+
+  DataChannelManager();
+  ~DataChannelManager();
+
+  // Registers a factory function to handle a new incoming data channel with a
+  // name matching |prefix|. Both |constructor| and |prefix| cannot be empty.
+  void RegisterCreateHandlerCallback(const std::string& prefix,
+                                     CreateHandlerCallback constructor);
+
+  // Executes the registered callback to handle the new incoming data channel.
+  // Returns true if a handler of the new data channel has been executed.
+  bool OnIncomingDataChannel(const std::string& name,
+                             std::unique_ptr<MessagePipe> pipe);
+
+ private:
+  std::vector<std::pair<std::string, CreateHandlerCallback>> constructors_;
+};
+
+}  // namespace protocol
+}  // namespace remoting
+
+#endif  // REMOTING_PROTOCOL_DATA_CHANNEL_MANAGER_H_
diff --git a/remoting/protocol/data_channel_manager_unittest.cc b/remoting/protocol/data_channel_manager_unittest.cc
new file mode 100644
index 0000000..52fdfc5f4
--- /dev/null
+++ b/remoting/protocol/data_channel_manager_unittest.cc
@@ -0,0 +1,267 @@
+// 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 "remoting/protocol/data_channel_manager.h"
+
+#include <map>
+#include <utility>
+
+#include "base/bind.h"
+#include "base/memory/ptr_util.h"
+#include "base/message_loop/message_loop.h"
+#include "base/run_loop.h"
+#include "remoting/base/compound_buffer.h"
+#include "remoting/proto/test.pb.h"
+#include "remoting/protocol/fake_message_pipe.h"
+#include "remoting/protocol/fake_message_pipe_wrapper.h"
+#include "remoting/protocol/named_message_pipe_handler.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace remoting {
+namespace protocol {
+
+namespace {
+
+class FakeNamedMessagePipeHandler final : public NamedMessagePipeHandler {
+ public:
+  FakeNamedMessagePipeHandler(const std::string& name,
+                         std::unique_ptr<MessagePipe> pipe,
+                         const std::string& expected_data)
+      : NamedMessagePipeHandler(name, std::move(pipe)),
+        expected_data_(expected_data) {
+    handlers_[name] = this;
+  }
+
+  int received_message_count() const { return received_message_count_; }
+
+  static FakeNamedMessagePipeHandler* Find(const std::string& name);
+
+  static int instance_count() { return handlers_.size(); }
+
+  // NamedMessagePipeHandler implementation.
+  void OnIncomingMessage(std::unique_ptr<CompoundBuffer> message) override;
+
+  // Public functions for test cases.
+  void Close() { NamedMessagePipeHandler::Close(); }
+
+  bool connected() const { return NamedMessagePipeHandler::connected(); }
+
+  void Send(google::protobuf::MessageLite* message,
+            const base::Closure& done);
+
+ protected:
+  ~FakeNamedMessagePipeHandler() override {
+    EXPECT_EQ(handlers_.erase(pipe_name()), 1U);
+  }
+
+ private:
+  static std::map<std::string, FakeNamedMessagePipeHandler*> handlers_;
+
+  const std::string expected_data_;
+  int received_message_count_ = 0;
+};
+
+void FakeNamedMessagePipeHandler::OnIncomingMessage(
+    std::unique_ptr<CompoundBuffer> message) {
+  ASSERT_TRUE(message != nullptr);
+  std::string content;
+  content.resize(expected_data_.size());
+  message->CopyTo(&(content[0]), content.size());
+  ASSERT_EQ(content, expected_data_);
+  received_message_count_++;
+}
+
+// static
+FakeNamedMessagePipeHandler* FakeNamedMessagePipeHandler::Find(
+    const std::string& name) {
+  auto it = handlers_.find(name);
+  if (it == handlers_.end()) {
+    return nullptr;
+  }
+  return it->second;
+}
+
+void FakeNamedMessagePipeHandler::Send(google::protobuf::MessageLite* message,
+                                       const base::Closure& done) {
+  if (connected()) {
+    NamedMessagePipeHandler::Send(message, done);
+    return;
+  }
+#if DCHECK_IS_ON()
+  ASSERT_DEATH_IF_SUPPORTED(
+    NamedMessagePipeHandler::Send(message, done),
+    "connected");
+#else
+  NamedMessagePipeHandler::Send(message, done);
+#endif
+}
+
+// static
+std::map<std::string, FakeNamedMessagePipeHandler*>
+FakeNamedMessagePipeHandler::handlers_;
+
+void TestDataChannelManagerFullMatch(bool asynchronous) {
+  base::MessageLoop message_loop;
+  DataChannelManager manager;
+  manager.RegisterCreateHandlerCallback("FullMatch", base::Bind(
+      [](const std::string& expected_data,
+         const std::string& name,
+         std::unique_ptr<MessagePipe> pipe) -> void {
+        new FakeNamedMessagePipeHandler(name, std::move(pipe), expected_data);
+      },
+      "FullMatchContent"));
+  manager.RegisterCreateHandlerCallback("AnotherFullMatch", base::Bind(
+      [](const std::string& expected_data,
+         const std::string& name,
+         std::unique_ptr<MessagePipe> pipe) -> void {
+        new FakeNamedMessagePipeHandler(name, std::move(pipe), expected_data);
+      },
+      "AnotherFullMatchContent"));
+
+  FakeMessagePipe pipe1(asynchronous);
+  FakeMessagePipe pipe2(asynchronous);
+  ASSERT_TRUE(manager.OnIncomingDataChannel("FullMatch", pipe1.Wrap()));
+  ASSERT_TRUE(manager.OnIncomingDataChannel("AnotherFullMatch", pipe2.Wrap()));
+  base::RunLoop().RunUntilIdle();
+
+  FakeNamedMessagePipeHandler* handler1 =
+      FakeNamedMessagePipeHandler::Find("FullMatch");
+  FakeNamedMessagePipeHandler* handler2 =
+      FakeNamedMessagePipeHandler::Find("AnotherFullMatch");
+  ASSERT_TRUE(handler1 != nullptr);
+  ASSERT_TRUE(handler2 != nullptr);
+
+  pipe1.OpenPipe();
+  base::RunLoop().RunUntilIdle();
+  {
+    DataChannelManagerTestProto message;
+    int sent = 0;
+    base::Closure sent_callback = base::Bind([](int* sent) {
+          (*sent)++;
+        },
+        base::Unretained(&sent));
+    ASSERT_TRUE(handler1->connected());
+    handler1->Send(&message, sent_callback);
+    ASSERT_FALSE(handler2->connected());
+    handler2->Send(&message, sent_callback);
+
+    base::RunLoop().RunUntilIdle();
+    ASSERT_EQ(sent, 1);
+  }
+
+  pipe2.OpenPipe();
+  base::RunLoop().RunUntilIdle();
+  {
+    DataChannelManagerTestProto message;
+    int sent = 0;
+    base::Closure sent_callback = base::Bind([](int* sent) {
+          (*sent)++;
+        },
+        base::Unretained(&sent));
+    ASSERT_TRUE(handler2->connected());
+
+    handler1->Send(&message, sent_callback);
+    handler2->Send(&message, sent_callback);
+
+    base::RunLoop().RunUntilIdle();
+    ASSERT_EQ(sent, 2);
+  }
+
+  {
+    std::string content;
+    auto message = base::MakeUnique<CompoundBuffer>();
+    content = "FullMatchContent";
+    message->AppendCopyOf(&(content[0]), content.size());
+    pipe1.Receive(std::move(message));
+
+    message = base::MakeUnique<CompoundBuffer>();
+    content = "AnotherFullMatchContent";
+    message->AppendCopyOf(&(content[0]), content.size());
+    pipe2.Receive(std::move(message));
+
+    base::RunLoop().RunUntilIdle();
+    ASSERT_EQ(handler1->received_message_count(), 1);
+    ASSERT_EQ(handler2->received_message_count(), 1);
+  }
+
+  pipe2.ClosePipe();
+  if (asynchronous) {
+    base::RunLoop().RunUntilIdle();
+  }
+  ASSERT_TRUE(FakeNamedMessagePipeHandler::Find("AnotherFullMatch") == nullptr);
+
+  handler1->Close();
+  ASSERT_TRUE(FakeNamedMessagePipeHandler::Find("FullMatch") == nullptr);
+
+  ASSERT_EQ(FakeNamedMessagePipeHandler::instance_count(), 0);
+}
+
+void TestDataChannelManagerMultipleRegistrations(bool asynchronous) {
+  base::MessageLoop message_loop;
+  DataChannelManager manager;
+  manager.RegisterCreateHandlerCallback("FullMatch", base::Bind(
+      [](const std::string& expected_data,
+         const std::string& name,
+         std::unique_ptr<MessagePipe> pipe) -> void {
+        new FakeNamedMessagePipeHandler(name, std::move(pipe), expected_data);
+      },
+      "FullMatchContent"));
+  manager.RegisterCreateHandlerCallback("Prefix-", base::Bind(
+      [](const std::string& expected_data,
+         const std::string& name,
+         std::unique_ptr<MessagePipe> pipe) -> void {
+        new FakeNamedMessagePipeHandler(name, std::move(pipe), expected_data);
+      },
+      "PrefixMatchContent"));
+
+  FakeMessagePipe pipe1(asynchronous);
+  FakeMessagePipe pipe2(asynchronous);
+  FakeMessagePipe pipe3(asynchronous);
+  FakeMessagePipe pipe4(asynchronous);
+  ASSERT_TRUE(manager.OnIncomingDataChannel("Prefix-1", pipe1.Wrap()));
+  ASSERT_TRUE(manager.OnIncomingDataChannel("Prefix-2", pipe2.Wrap()));
+  ASSERT_FALSE(manager.OnIncomingDataChannel(
+      "PrefixShouldNotMatch", pipe3.Wrap()));
+  ASSERT_TRUE(manager.OnIncomingDataChannel("FullMatch", pipe4.Wrap()));
+  base::RunLoop().RunUntilIdle();
+
+  FakeNamedMessagePipeHandler* handler1 =
+      FakeNamedMessagePipeHandler::Find("Prefix-1");
+  FakeNamedMessagePipeHandler* handler2 =
+      FakeNamedMessagePipeHandler::Find("Prefix-2");
+  FakeNamedMessagePipeHandler* handler3 =
+      FakeNamedMessagePipeHandler::Find("PrefixShouldNotMatch");
+  FakeNamedMessagePipeHandler* handler4 =
+      FakeNamedMessagePipeHandler::Find("FullMatch");
+  ASSERT_TRUE(handler1 != nullptr);
+  ASSERT_TRUE(handler2 != nullptr);
+  ASSERT_TRUE(handler3 == nullptr);
+  ASSERT_TRUE(handler4 != nullptr);
+
+  handler1->Close();
+  handler2->Close();
+  handler4->Close();
+  base::RunLoop().RunUntilIdle();
+}
+
+}  // namespace
+
+TEST(DataChannelManagerTest, FullMatchWithSynchronousPipe) {
+  TestDataChannelManagerFullMatch(false);
+}
+
+TEST(DataChannelManagerTest, FullMatchWithAsynchronousPipe) {
+  TestDataChannelManagerFullMatch(true);
+}
+
+TEST(DataChannelManagerTest, MultipleRegistrationsWithSynchronousPipe) {
+  TestDataChannelManagerMultipleRegistrations(false);
+}
+
+TEST(DataChannelManagerTest, MultipleRegistrationsWithAsynchronousPipe) {
+  TestDataChannelManagerMultipleRegistrations(true);
+}
+
+}  // namespace protocol
+}  // namespace remoting
diff --git a/remoting/protocol/fake_message_pipe.cc b/remoting/protocol/fake_message_pipe.cc
new file mode 100644
index 0000000..3829786
--- /dev/null
+++ b/remoting/protocol/fake_message_pipe.cc
@@ -0,0 +1,124 @@
+// 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 "remoting/protocol/fake_message_pipe.h"
+
+#include "base/bind.h"
+#include "base/location.h"
+#include "base/memory/ptr_util.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "remoting/base/compound_buffer.h"
+#include "remoting/protocol/fake_message_pipe_wrapper.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace remoting {
+namespace protocol {
+
+FakeMessagePipe::FakeMessagePipe(bool asynchronous)
+    : asynchronous_(asynchronous) {}
+
+FakeMessagePipe::~FakeMessagePipe() = default;
+
+std::unique_ptr<FakeMessagePipeWrapper> FakeMessagePipe::Wrap() {
+  return base::MakeUnique<FakeMessagePipeWrapper>(this);
+}
+
+void FakeMessagePipe::Start(EventHandler* event_handler) {
+  ASSERT_TRUE(event_handler_ == nullptr);
+  ASSERT_TRUE(event_handler != nullptr);
+  event_handler_ = event_handler;
+}
+
+void FakeMessagePipe::Send(google::protobuf::MessageLite* message,
+                           const base::Closure& done) {
+  if (asynchronous_) {
+    base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
+        base::Bind([](FakeMessagePipe* me,
+                      google::protobuf::MessageLite* message,
+                      const base::Closure& done) {
+                     me->SendImpl(message, done);
+                   },
+                   base::Unretained(this),
+                   base::Unretained(message),
+                   done));
+    return;
+  }
+  SendImpl(message, done);
+}
+
+void FakeMessagePipe::Receive(std::unique_ptr<CompoundBuffer> message) {
+  if (asynchronous_) {
+    base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
+        base::Bind([](FakeMessagePipe* me,
+                      std::unique_ptr<CompoundBuffer> message) {
+                     me->ReceiveImpl(std::move(message));
+                   },
+                   base::Unretained(this),
+                   base::Passed(std::move(message))));
+    return;
+  }
+
+  ReceiveImpl(std::move(message));
+}
+
+void FakeMessagePipe::OpenPipe() {
+  if (asynchronous_) {
+    base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, base::Bind(
+        [](FakeMessagePipe* me) {
+          me->OpenPipeImpl();
+        },
+        base::Unretained(this)));
+    return;
+  }
+
+  OpenPipeImpl();
+}
+
+void FakeMessagePipe::ClosePipe() {
+  if (asynchronous_) {
+    base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, base::Bind(
+        [](FakeMessagePipe* me) {
+          me->ClosePipeImpl();
+        },
+        base::Unretained(this)));
+    return;
+  }
+
+  ClosePipeImpl();
+}
+
+void FakeMessagePipe::SendImpl(
+    google::protobuf::MessageLite* message,
+    const base::Closure& done) {
+  ASSERT_TRUE(pipe_opened_);
+  if (done) {
+    done.Run();
+  }
+}
+
+void FakeMessagePipe::ReceiveImpl(std::unique_ptr<CompoundBuffer> message) {
+  ASSERT_TRUE(pipe_opened_);
+  ASSERT_TRUE(event_handler_ != nullptr);
+  if (message) {
+    message->Lock();
+  }
+  event_handler_->OnMessageReceived(std::move(message));
+}
+
+void FakeMessagePipe::OpenPipeImpl() {
+  ASSERT_FALSE(pipe_opened_);
+  ASSERT_TRUE(event_handler_ != nullptr);
+  pipe_opened_ = true;
+  event_handler_->OnMessagePipeOpen();
+}
+
+void FakeMessagePipe::ClosePipeImpl() {
+  ASSERT_TRUE(pipe_opened_);
+  ASSERT_TRUE(event_handler_ != nullptr);
+  pipe_opened_ = false;
+  event_handler_->OnMessagePipeClosed();
+}
+
+}  // namespace protocol
+}  // namespace remoting
diff --git a/remoting/protocol/fake_message_pipe.h b/remoting/protocol/fake_message_pipe.h
new file mode 100644
index 0000000..6be457b
--- /dev/null
+++ b/remoting/protocol/fake_message_pipe.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 REMOTING_PROTOCOL_FAKE_MESSAGE_PIPE_H_
+#define REMOTING_PROTOCOL_FAKE_MESSAGE_PIPE_H_
+
+#include <memory>
+
+#include "remoting/protocol/message_pipe.h"
+
+namespace google {
+namespace protobuf {
+class MessageLite;
+}  // namespace protobuf
+}  // namespace google
+
+namespace remoting {
+namespace protocol {
+
+class FakeMessagePipeWrapper;
+
+class FakeMessagePipe final : public MessagePipe {
+ public:
+  explicit FakeMessagePipe(bool asynchronous);
+  ~FakeMessagePipe() override;
+
+  // Creates an std::unique_ptr<FakeMessagePipeWrapper> instance to wrap |this|.
+  // All operations will be forwarded to |this| except for the destructor.
+  //
+  // Most of the components take ownership of std::unique_ptr<MessagePipe>,
+  // which makes the test cases hard to maintain the lifetime of a
+  // FakeMessagePipe. So this function creates a "weak" unique_ptr of this
+  // instance to let the test case decide the lifetime of a FakeMessagePipe.
+  std::unique_ptr<FakeMessagePipeWrapper> Wrap();
+
+  // MessagePipe implementation.
+  void Start(EventHandler* event_handler) override;
+  void Send(google::protobuf::MessageLite* message,
+            const base::Closure& done) override;
+
+  // Forwards |message| to EventHandler.
+  void Receive(std::unique_ptr<CompoundBuffer> message);
+
+  // Simulates the operation to open the pipe.
+  void OpenPipe();
+
+  // Simulates the operation to close the pipe.
+  void ClosePipe();
+
+ private:
+  void SendImpl(google::protobuf::MessageLite* message,
+                const base::Closure& done);
+  void ReceiveImpl(std::unique_ptr<CompoundBuffer> message);
+  void OpenPipeImpl();
+  void ClosePipeImpl();
+
+  const bool asynchronous_;
+  bool pipe_opened_ = false;
+  EventHandler* event_handler_ = nullptr;
+};
+
+}  // namespace protocol
+}  // namespace remoting
+
+#endif  // REMOTING_PROTOCOL_FAKE_MESSAGE_PIPE_H_
diff --git a/remoting/protocol/fake_message_pipe_wrapper.cc b/remoting/protocol/fake_message_pipe_wrapper.cc
new file mode 100644
index 0000000..7e1eafa
--- /dev/null
+++ b/remoting/protocol/fake_message_pipe_wrapper.cc
@@ -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.
+
+#include "remoting/protocol/fake_message_pipe_wrapper.h"
+
+#include <utility>
+
+#include "base/logging.h"
+#include "remoting/base/compound_buffer.h"
+#include "remoting/protocol/fake_message_pipe.h"
+
+namespace remoting {
+namespace protocol {
+
+FakeMessagePipeWrapper::FakeMessagePipeWrapper(FakeMessagePipe* pipe)
+    : pipe_(pipe) {
+  DCHECK(pipe_);
+}
+
+FakeMessagePipeWrapper::~FakeMessagePipeWrapper() = default;
+
+void FakeMessagePipeWrapper::Start(EventHandler* event_handler) {
+  pipe_->Start(event_handler);
+}
+
+void FakeMessagePipeWrapper::Send(google::protobuf::MessageLite* message,
+                                  const base::Closure& done) {
+  pipe_->Send(message, done);
+}
+
+void FakeMessagePipeWrapper::Receive(std::unique_ptr<CompoundBuffer> message) {
+  pipe_->Receive(std::move(message));
+}
+
+void FakeMessagePipeWrapper::OpenPipe() {
+  pipe_->OpenPipe();
+}
+
+void FakeMessagePipeWrapper::ClosePipe() {
+  pipe_->ClosePipe();
+}
+
+}  // namespace protocol
+}  // namespace remoting
diff --git a/remoting/protocol/fake_message_pipe_wrapper.h b/remoting/protocol/fake_message_pipe_wrapper.h
new file mode 100644
index 0000000..0ea4589
--- /dev/null
+++ b/remoting/protocol/fake_message_pipe_wrapper.h
@@ -0,0 +1,46 @@
+// 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 REMOTING_PROTOCOL_FAKE_MESSAGE_PIPE_WRAPPER_H_
+#define REMOTING_PROTOCOL_FAKE_MESSAGE_PIPE_WRAPPER_H_
+
+#include <memory>
+
+#include "remoting/protocol/message_pipe.h"
+
+namespace google {
+namespace protobuf {
+class MessageLite;
+}  // namespace protobuf
+}  // namespace google
+
+namespace remoting {
+namespace protocol {
+
+class FakeMessagePipe;
+
+// This class should not be used explicitly: use FakeMessagePipe::Wrap().
+class FakeMessagePipeWrapper final : public MessagePipe {
+ public:
+  // |pipe| must outlive this instance.
+  explicit FakeMessagePipeWrapper(FakeMessagePipe* pipe);
+  ~FakeMessagePipeWrapper() override;
+
+  // MessagePipe implementation.
+  void Start(EventHandler* event_handler) override;
+  void Send(google::protobuf::MessageLite* message,
+            const base::Closure& done) override;
+
+  void Receive(std::unique_ptr<CompoundBuffer> message);
+  void OpenPipe();
+  void ClosePipe();
+
+ private:
+  FakeMessagePipe* const pipe_;
+};
+
+}  // namespace protocol
+}  // namespace remoting
+
+#endif  // REMOTING_PROTOCOL_FAKE_MESSAGE_PIPE_WRAPPER_H_
diff --git a/remoting/protocol/named_message_pipe_handler.cc b/remoting/protocol/named_message_pipe_handler.cc
new file mode 100644
index 0000000..4741d57
--- /dev/null
+++ b/remoting/protocol/named_message_pipe_handler.cc
@@ -0,0 +1,69 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "remoting/protocol/named_message_pipe_handler.h"
+
+#include <utility>
+
+#include "base/location.h"
+#include "base/logging.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "remoting/base/compound_buffer.h"
+
+namespace remoting {
+namespace protocol {
+
+NamedMessagePipeHandler::NamedMessagePipeHandler(
+    const std::string& name,
+    std::unique_ptr<MessagePipe> pipe)
+    : name_(name),
+      pipe_(std::move(pipe)) {
+  DCHECK(pipe_);
+  pipe_->Start(this);
+}
+
+NamedMessagePipeHandler::~NamedMessagePipeHandler() = default;
+
+void NamedMessagePipeHandler::Close() {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  if (connected()) {
+    OnDisconnecting();
+    is_connected_ = false;
+  }
+  delete this;
+}
+
+void NamedMessagePipeHandler::Send(google::protobuf::MessageLite* message,
+                              const base::Closure& done) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK(connected());
+  pipe_->Send(message, done);
+}
+
+void NamedMessagePipeHandler::OnIncomingMessage(
+    std::unique_ptr<CompoundBuffer> message) {}
+
+void NamedMessagePipeHandler::OnConnected() {}
+
+void NamedMessagePipeHandler::OnDisconnecting() {}
+
+void NamedMessagePipeHandler::OnMessagePipeOpen() {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK(!is_connected_);
+  is_connected_ = true;
+  OnConnected();
+}
+
+void NamedMessagePipeHandler::OnMessageReceived(
+    std::unique_ptr<CompoundBuffer> message) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  OnIncomingMessage(std::move(message));
+}
+
+void NamedMessagePipeHandler::OnMessagePipeClosed() {
+  Close();
+}
+
+}  // namespace protocol
+}  // namespace remoting
diff --git a/remoting/protocol/named_message_pipe_handler.h b/remoting/protocol/named_message_pipe_handler.h
new file mode 100644
index 0000000..877b033
--- /dev/null
+++ b/remoting/protocol/named_message_pipe_handler.h
@@ -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.
+
+#ifndef REMOTING_PROTOCOL_NAMED_MESSAGE_PIPE_HANDLER_H_
+#define REMOTING_PROTOCOL_NAMED_MESSAGE_PIPE_HANDLER_H_
+
+#include <memory>
+#include <string>
+
+#include "base/callback.h"
+#include "base/sequenced_task_runner_helpers.h"
+#include "base/threading/thread_checker.h"
+#include "remoting/protocol/message_pipe.h"
+
+namespace google {
+namespace protobuf {
+class MessageLite;
+}  // namespace protobuf
+}  // namespace google
+
+namespace remoting {
+
+class CompoundBuffer;
+
+namespace protocol {
+
+// A base class to handle data from a named MessagePipe. This class manages the
+// lifetime itself: it deletes itself once the MessagePipe is closed or the
+// derived instance actively calls Close() function.
+class NamedMessagePipeHandler : public MessagePipe::EventHandler {
+ protected:
+  // The callers should create instances of derived classes instead of this
+  // class. So hide the constructor.
+  NamedMessagePipeHandler(const std::string& name,
+                          std::unique_ptr<MessagePipe> pipe);
+
+  ~NamedMessagePipeHandler() override;
+
+  // Closes the channel and eventually destructs this instance. No operations
+  // should be performed after executing this function.
+  void Close();
+
+  const std::string& pipe_name() const { return name_; }
+
+  // Whether |pipe_| has been connected.
+  bool connected() const { return is_connected_; }
+
+  // Sends the message through the pipe. This function should only be called
+  // once connected() returns true.
+  void Send(google::protobuf::MessageLite* message, const base::Closure& done);
+
+  // Derived classes can override these functions to receive data from the
+  // connection or observe the connection state. These functions will not be
+  // called unless |pipe_| has been opened.
+  virtual void OnIncomingMessage(std::unique_ptr<CompoundBuffer> message);
+  virtual void OnConnected();
+  virtual void OnDisconnecting();
+
+ private:
+  friend class base::DeleteHelper<NamedMessagePipeHandler>;
+
+  // MessagePipe::EventHandler implementation.
+  void OnMessagePipeOpen() override;
+  void OnMessageReceived(std::unique_ptr<CompoundBuffer> message) override;
+  void OnMessagePipeClosed() override;
+
+  const std::string name_;
+  std::unique_ptr<MessagePipe> pipe_;
+  base::ThreadChecker thread_checker_;
+  bool is_connected_ = false;
+};
+
+}  // namespace protocol
+}  // namespace remoting
+
+#endif  // REMOTING_PROTOCOL_NAMED_MESSAGE_PIPE_HANDLER_H_
diff --git a/remoting/protocol/webrtc_data_stream_adapter.h b/remoting/protocol/webrtc_data_stream_adapter.h
index bb2a227..51a7825 100644
--- a/remoting/protocol/webrtc_data_stream_adapter.h
+++ b/remoting/protocol/webrtc_data_stream_adapter.h
@@ -39,10 +39,6 @@
   void OnStateChange() override;
   void OnMessage(const webrtc::DataBuffer& buffer) override;
 
-  void OnConnected();
-
-  void OnClosed();
-
   rtc::scoped_refptr<webrtc::DataChannelInterface> channel_;
 
   EventHandler* event_handler_ = nullptr;
diff --git a/services/ui/public/cpp/input_devices/BUILD.gn b/services/ui/public/cpp/input_devices/BUILD.gn
index fa8f255..43a323de 100644
--- a/services/ui/public/cpp/input_devices/BUILD.gn
+++ b/services/ui/public/cpp/input_devices/BUILD.gn
@@ -19,6 +19,17 @@
   public_deps = [
     "//services/ui/public/interfaces/input_devices",
   ]
+
+  if (is_chromeos && use_ozone) {
+    sources += [
+      "input_device_controller_client.cc",
+      "input_device_controller_client.h",
+    ]
+    public_deps += [
+      "//services/ui/public/interfaces:constants",
+      "//ui/ozone",
+    ]
+  }
 }
 
 if (is_chromeos && use_ozone) {
diff --git a/services/ui/public/cpp/input_devices/input_device_controller_client.cc b/services/ui/public/cpp/input_devices/input_device_controller_client.cc
new file mode 100644
index 0000000..2ac09d4
--- /dev/null
+++ b/services/ui/public/cpp/input_devices/input_device_controller_client.cc
@@ -0,0 +1,140 @@
+// 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/ui/public/cpp/input_devices/input_device_controller_client.h"
+
+#include <utility>
+
+#include "base/bind_helpers.h"
+#include "base/memory/ptr_util.h"
+#include "services/service_manager/public/cpp/connector.h"
+#include "services/ui/public/interfaces/constants.mojom.h"
+
+namespace ui {
+
+InputDeviceControllerClient::InputDeviceControllerClient(
+    service_manager::Connector* connector,
+    const std::string& service_name)
+    : binding_(this) {
+  connector->BindInterface(
+      service_name.empty() ? mojom::kServiceName : service_name,
+      &input_device_controller_);
+  mojom::KeyboardDeviceObserverPtr ptr;
+  binding_.Bind(mojo::MakeRequest(&ptr));
+  input_device_controller_->AddKeyboardDeviceObserver(std::move(ptr));
+}
+
+InputDeviceControllerClient::~InputDeviceControllerClient() = default;
+
+void InputDeviceControllerClient::GetHasMouse(GetHasMouseCallback callback) {
+  input_device_controller_->GetHasMouse(std::move(callback));
+}
+
+void InputDeviceControllerClient::GetHasTouchpad(
+    GetHasTouchpadCallback callback) {
+  input_device_controller_->GetHasTouchpad(std::move(callback));
+}
+
+bool InputDeviceControllerClient::IsCapsLockEnabled() {
+  return keyboard_device_state_.is_caps_lock_enabled;
+}
+
+void InputDeviceControllerClient::SetCapsLockEnabled(bool enabled) {
+  keyboard_device_state_.is_caps_lock_enabled = enabled;
+  input_device_controller_->SetCapsLockEnabled(enabled);
+}
+
+void InputDeviceControllerClient::SetNumLockEnabled(bool enabled) {
+  input_device_controller_->SetNumLockEnabled(enabled);
+}
+
+bool InputDeviceControllerClient::IsAutoRepeatEnabled() {
+  return keyboard_device_state_.is_auto_repeat_enabled;
+}
+
+void InputDeviceControllerClient::SetAutoRepeatEnabled(bool enabled) {
+  keyboard_device_state_.is_auto_repeat_enabled = enabled;
+  input_device_controller_->SetAutoRepeatEnabled(enabled);
+}
+
+void InputDeviceControllerClient::SetAutoRepeatRate(base::TimeDelta delay,
+                                                    base::TimeDelta interval) {
+  input_device_controller_->SetAutoRepeatRate(delay, interval);
+}
+
+void InputDeviceControllerClient::SetKeyboardLayoutByName(
+    const std::string& layout_name) {
+  input_device_controller_->SetKeyboardLayoutByName(layout_name);
+}
+
+void InputDeviceControllerClient::SetTouchpadSensitivity(int value) {
+  input_device_controller_->SetTouchpadSensitivity(value);
+}
+
+void InputDeviceControllerClient::SetTapToClick(bool enable) {
+  input_device_controller_->SetTapToClick(enable);
+}
+
+void InputDeviceControllerClient::SetThreeFingerClick(bool enable) {
+  input_device_controller_->SetThreeFingerClick(enable);
+}
+
+void InputDeviceControllerClient::SetTapDragging(bool enable) {
+  input_device_controller_->SetTapDragging(enable);
+}
+
+void InputDeviceControllerClient::SetNaturalScroll(bool enable) {
+  input_device_controller_->SetNaturalScroll(enable);
+}
+
+void InputDeviceControllerClient::SetMouseSensitivity(int value) {
+  input_device_controller_->SetMouseSensitivity(value);
+}
+
+void InputDeviceControllerClient::SetPrimaryButtonRight(bool right) {
+  input_device_controller_->SetPrimaryButtonRight(right);
+}
+
+void InputDeviceControllerClient::GetTouchDeviceStatus(
+    GetTouchDeviceStatusCallback callback) {
+  input_device_controller_->GetTouchDeviceStatus(std::move(callback));
+}
+
+void InputDeviceControllerClient::GetTouchEventLog(
+    const base::FilePath& out_dir,
+    GetTouchEventLogCallback callback) {
+  input_device_controller_->GetTouchEventLog(out_dir, std::move(callback));
+}
+
+void InputDeviceControllerClient::SetTapToClickPaused(bool state) {
+  input_device_controller_->SetTapToClickPaused(state);
+}
+
+void InputDeviceControllerClient::SetTouchscreensEnabled(bool enable) {
+  input_device_controller_->SetTouchscreensEnabled(enable);
+}
+
+void InputDeviceControllerClient::SetInternalKeyboardFilter(
+    bool enable_filter,
+    const std::vector<DomCode>& allowed_keys) {
+  std::vector<uint32_t> transport_keys(allowed_keys.size());
+  for (size_t i = 0; i < allowed_keys.size(); ++i)
+    transport_keys[i] = static_cast<uint32_t>(allowed_keys[i]);
+  input_device_controller_->SetInternalKeyboardFilter(enable_filter,
+                                                      transport_keys);
+}
+
+void InputDeviceControllerClient::SetInternalTouchpadEnabled(
+    bool enable,
+    SetInternalTouchpadEnabledCallback callback) {
+  input_device_controller_->SetInternalTouchpadEnabled(enable,
+                                                       std::move(callback));
+}
+
+void InputDeviceControllerClient::OnKeyboardStateChanged(
+    mojom::KeyboardDeviceStatePtr state) {
+  keyboard_device_state_ = *state;
+}
+
+}  // namespace ui
diff --git a/services/ui/public/cpp/input_devices/input_device_controller_client.h b/services/ui/public/cpp/input_devices/input_device_controller_client.h
new file mode 100644
index 0000000..8e710e96
--- /dev/null
+++ b/services/ui/public/cpp/input_devices/input_device_controller_client.h
@@ -0,0 +1,93 @@
+// 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_UI_PUBLIC_CPP_INPUT_DEVICES_INPUT_DEVICE_CONTROLLER_CLIENT_H_
+#define SERVICES_UI_PUBLIC_CPP_INPUT_DEVICES_INPUT_DEVICE_CONTROLLER_CLIENT_H_
+
+#include <string>
+
+#include "base/callback_forward.h"
+#include "base/macros.h"
+#include "mojo/public/cpp/bindings/binding.h"
+#include "services/ui/public/interfaces/input_devices/input_device_controller.mojom.h"
+
+namespace base {
+class FilePath;
+}
+
+namespace service_manager {
+class Connector;
+}
+
+namespace ui {
+
+enum class DomCode;
+
+// InputDeviceControllerClient is mostly a call through to
+// mojom::InputDeviceController. It does a minimal amount of caching and is
+// itself a KeyboardDeviceObserver to maintain local keyboard state.
+class InputDeviceControllerClient : public mojom::KeyboardDeviceObserver {
+ public:
+  // |service_Name| is the name of the service providing mojom::KeyboardDevice,
+  // generally use the default, unless a specific service is needed.
+  explicit InputDeviceControllerClient(
+      service_manager::Connector* connector,
+      const std::string& service_name = std::string());
+  ~InputDeviceControllerClient() override;
+
+  using GetHasMouseCallback = base::OnceCallback<void(bool)>;
+  void GetHasMouse(GetHasMouseCallback callback);
+
+  using GetHasTouchpadCallback = base::OnceCallback<void(bool)>;
+  void GetHasTouchpad(GetHasTouchpadCallback callback);
+
+  bool IsCapsLockEnabled();
+  void SetCapsLockEnabled(bool enabled);
+  void SetNumLockEnabled(bool enabled);
+  bool IsAutoRepeatEnabled();
+  void SetAutoRepeatEnabled(bool enabled);
+  void SetAutoRepeatRate(base::TimeDelta delay, base::TimeDelta interval);
+  void SetKeyboardLayoutByName(const std::string& layout_name);
+  void SetTouchpadSensitivity(int value);
+  void SetTapToClick(bool enabled);
+  void SetThreeFingerClick(bool enabled);
+  void SetTapDragging(bool enabled);
+  void SetNaturalScroll(bool enabled);
+  void SetMouseSensitivity(int value);
+  void SetPrimaryButtonRight(bool right);
+
+  using GetTouchDeviceStatusCallback =
+      base::OnceCallback<void(const std::string&)>;
+  void GetTouchDeviceStatus(GetTouchDeviceStatusCallback callback);
+
+  using GetTouchEventLogCallback =
+      base::OnceCallback<void(const std::vector<base::FilePath>&)>;
+  void GetTouchEventLog(const base::FilePath& out_dir,
+                        GetTouchEventLogCallback callback);
+  void SetTapToClickPaused(bool state);
+
+  void SetTouchscreensEnabled(bool enabled);
+  void SetInternalKeyboardFilter(bool enable_filter,
+                                 const std::vector<DomCode>& allowed_keys);
+
+  // Sets whether the internal touch pad. Returns true if there is an internal
+  // touchpad.
+  using SetInternalTouchpadEnabledCallback = base::OnceCallback<void(bool)>;
+  void SetInternalTouchpadEnabled(bool enable,
+                                  SetInternalTouchpadEnabledCallback callback);
+
+ private:
+  // mojom::KeyboardDeviceObserver:
+  void OnKeyboardStateChanged(mojom::KeyboardDeviceStatePtr state) override;
+
+  mojom::InputDeviceControllerPtr input_device_controller_;
+  mojom::KeyboardDeviceState keyboard_device_state_;
+  mojo::Binding<mojom::KeyboardDeviceObserver> binding_;
+
+  DISALLOW_COPY_AND_ASSIGN(InputDeviceControllerClient);
+};
+
+}  // namespace ui
+
+#endif  // SERVICES_UI_PUBLIC_CPP_INPUT_DEVICES_INPUT_DEVICE_CONTROLLER_CLIENT_H_
diff --git a/testing/buildbot/chromium.android.json b/testing/buildbot/chromium.android.json
index e3ce448..dc1788a9 100644
--- a/testing/buildbot/chromium.android.json
+++ b/testing/buildbot/chromium.android.json
@@ -2709,7 +2709,7 @@
             }
           ],
           "expiration": 10800,
-          "hard_timeout": 600,
+          "hard_timeout": 1200,
           "output_links": [
             {
               "link": [
@@ -7673,7 +7673,7 @@
             }
           ],
           "expiration": 10800,
-          "hard_timeout": 60,
+          "hard_timeout": 120,
           "output_links": [
             {
               "link": [
@@ -7714,7 +7714,7 @@
             }
           ],
           "expiration": 10800,
-          "hard_timeout": 180,
+          "hard_timeout": 360,
           "output_links": [
             {
               "link": [
@@ -7919,7 +7919,7 @@
             }
           ],
           "expiration": 10800,
-          "hard_timeout": 60,
+          "hard_timeout": 120,
           "output_links": [
             {
               "link": [
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json
index 5cd21e8..149ffb46 100644
--- a/testing/variations/fieldtrial_testing_config.json
+++ b/testing/variations/fieldtrial_testing_config.json
@@ -3085,7 +3085,7 @@
             ],
             "experiments": [
                 {
-                    "name": "Query",
+                    "name": "QueryExperiment2",
                     "params": {
                         "translate-ranker-model-url": "https://www.gstatic.com/chrome/intelligence/assist/ranker/models/translate/2017/03/translate_ranker_model_20170329.pb.bin"
                     },
@@ -3097,7 +3097,7 @@
                     ]
                 },
                 {
-                    "name": "Enforcement",
+                    "name": "EnforcementExperiment2",
                     "params": {
                         "translate-ranker-model-url": "https://www.gstatic.com/chrome/intelligence/assist/ranker/models/translate/2017/03/translate_ranker_model_20170329.pb.bin"
                     },
diff --git a/third_party/WebKit/LayoutTests/FlagExpectations/enable-browser-side-navigation b/third_party/WebKit/LayoutTests/FlagExpectations/enable-browser-side-navigation
index 3027b09..1517a8e 100644
--- a/third_party/WebKit/LayoutTests/FlagExpectations/enable-browser-side-navigation
+++ b/third_party/WebKit/LayoutTests/FlagExpectations/enable-browser-side-navigation
@@ -45,10 +45,6 @@
 Bug(726178) virtual/mojo-loading/http/tests/security/mixedContent/redirect-http-to-https-iframe-in-main-frame.html [ Failure ]
 Bug(726178) virtual/mojo-loading/http/tests/security/mixedContent/redirect-https-to-http-iframe-in-main-frame.html [ Failure ]
 
-# PlzNavigate: Navigation requests upgraded via upgrade-insecure-requests will not get reported	
-# See https://crbug.com/713388
-Bug(713388) external/wpt/content-security-policy/securitypolicyviolation/upgrade-insecure-requests-reporting.https.html [ Timeout ]
-
 # ------------------------------------------------------------
 # These tests are correct with PlzNavigate but not without it
 # ------------------------------------------------------------
diff --git a/third_party/WebKit/LayoutTests/external/wpt/content-security-policy/securitypolicyviolation/upgrade-insecure-requests-reporting.https.html b/third_party/WebKit/LayoutTests/external/wpt/content-security-policy/securitypolicyviolation/upgrade-insecure-requests-reporting.https.html
index 5a0bc7c8..692db02 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/content-security-policy/securitypolicyviolation/upgrade-insecure-requests-reporting.https.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/content-security-policy/securitypolicyviolation/upgrade-insecure-requests-reporting.https.html
@@ -56,5 +56,37 @@
       i.src = url;
       document.body.appendChild(i);
     }, "Upgraded iframe is reported");
+
+    async_test(t => {
+      // Load an HTTPS iframe, then navigate it to an HTTP URL and check that the HTTP URL is both upgraded and reported.
+      var url = generateURL(Host.SAME_ORIGIN, Protocol.SECURE, ResourceType.FRAME).url;
+      var navigate_to = generateURL(Host.CROSS_ORIGIN, Protocol.INSECURE, ResourceType.FRAME).url;
+      var upgraded = new URL(navigate_to);
+      upgraded.protocol = "https";
+
+      var i = document.createElement('iframe');
+      var loaded = false;
+      var reported = false;
+
+      window.addEventListener("message", t.step_func(e => {
+        if (e.source == i.contentWindow) {
+          if (e.data == (new URL(url)).origin) {
+            waitForViolation(window, "frame-src")
+              .then(t.step_func(e => {
+                reported = true;
+                if (loaded)
+                  t.done();
+            }));
+            i.contentWindow.location.href = navigate_to;
+          } else if (e.data == (new URL(upgraded)).origin) {
+            loaded = true;
+            if (reported)
+              t.done();
+          }
+        }
+      }));
+      i.src = url;
+      document.body.appendChild(i);
+    }, "Navigated iframe is upgraded and reported");
 </script>
 </html>
diff --git a/third_party/WebKit/Source/core/exported/WebRemoteFrameImpl.cpp b/third_party/WebKit/Source/core/exported/WebRemoteFrameImpl.cpp
index 1748b82d..0892a9d 100644
--- a/third_party/WebKit/Source/core/exported/WebRemoteFrameImpl.cpp
+++ b/third_party/WebKit/Source/core/exported/WebRemoteFrameImpl.cpp
@@ -147,57 +147,6 @@
   NOTREACHED();
 }
 
-void WebRemoteFrameImpl::ExecuteScriptInIsolatedWorld(
-    int world_id,
-    const WebScriptSource* sources,
-    unsigned num_sources) {
-  NOTREACHED();
-}
-
-void WebRemoteFrameImpl::SetIsolatedWorldSecurityOrigin(
-    int world_id,
-    const WebSecurityOrigin&) {
-  NOTREACHED();
-}
-
-void WebRemoteFrameImpl::SetIsolatedWorldContentSecurityPolicy(
-    int world_id,
-    const WebString&) {
-  NOTREACHED();
-}
-
-void WebRemoteFrameImpl::CollectGarbage() {
-  NOTREACHED();
-}
-
-v8::Local<v8::Value> WebRemoteFrameImpl::ExecuteScriptAndReturnValue(
-    const WebScriptSource&) {
-  NOTREACHED();
-  return v8::Local<v8::Value>();
-}
-
-void WebRemoteFrameImpl::ExecuteScriptInIsolatedWorld(
-    int world_id,
-    const WebScriptSource* sources_in,
-    unsigned num_sources,
-    WebVector<v8::Local<v8::Value>>* results) {
-  NOTREACHED();
-}
-
-v8::Local<v8::Value> WebRemoteFrameImpl::CallFunctionEvenIfScriptDisabled(
-    v8::Local<v8::Function>,
-    v8::Local<v8::Value>,
-    int argc,
-    v8::Local<v8::Value> argv[]) {
-  NOTREACHED();
-  return v8::Local<v8::Value>();
-}
-
-v8::Local<v8::Context> WebRemoteFrameImpl::MainWorldScriptContext() const {
-  NOTREACHED();
-  return v8::Local<v8::Context>();
-}
-
 void WebRemoteFrameImpl::Reload(WebFrameLoadType) {
   NOTREACHED();
 }
diff --git a/third_party/WebKit/Source/core/exported/WebRemoteFrameImpl.h b/third_party/WebKit/Source/core/exported/WebRemoteFrameImpl.h
index 0792d9a..879f7d8 100644
--- a/third_party/WebKit/Source/core/exported/WebRemoteFrameImpl.h
+++ b/third_party/WebKit/Source/core/exported/WebRemoteFrameImpl.h
@@ -45,27 +45,6 @@
   WebDocument GetDocument() const override;
   WebPerformance Performance() const override;
   void DispatchUnloadEvent() override;
-  void ExecuteScriptInIsolatedWorld(int world_id,
-                                    const WebScriptSource* sources,
-                                    unsigned num_sources) override;
-  void SetIsolatedWorldSecurityOrigin(int world_id,
-                                      const WebSecurityOrigin&) override;
-  void SetIsolatedWorldContentSecurityPolicy(int world_id,
-                                             const WebString&) override;
-  void CollectGarbage() override;
-  v8::Local<v8::Value> ExecuteScriptAndReturnValue(
-      const WebScriptSource&) override;
-  void ExecuteScriptInIsolatedWorld(
-      int world_id,
-      const WebScriptSource* sources_in,
-      unsigned num_sources,
-      WebVector<v8::Local<v8::Value>>* results) override;
-  v8::Local<v8::Value> CallFunctionEvenIfScriptDisabled(
-      v8::Local<v8::Function>,
-      v8::Local<v8::Value>,
-      int argc,
-      v8::Local<v8::Value> argv[]) override;
-  v8::Local<v8::Context> MainWorldScriptContext() const override;
   void Reload(WebFrameLoadType) override;
   void ReloadWithOverrideURL(const WebURL& override_url,
                              WebFrameLoadType) override;
diff --git a/third_party/WebKit/Source/core/frame/csp/CSPDirectiveList.cpp b/third_party/WebKit/Source/core/frame/csp/CSPDirectiveList.cpp
index 281ca456..ed713dc3 100644
--- a/third_party/WebKit/Source/core/frame/csp/CSPDirectiveList.cpp
+++ b/third_party/WebKit/Source/core/frame/csp/CSPDirectiveList.cpp
@@ -1395,6 +1395,11 @@
           directive->ExposeForNavigationalChecks()});
     }
   }
+  if (upgrade_insecure_requests_) {
+    directives.push_back(WebContentSecurityPolicyDirective{
+        blink::WebString("upgrade-insecure-requests"),
+        WebContentSecurityPolicySourceList()});
+  }
   policy.directives = directives;
   policy.report_endpoints = ReportEndpoints();
   policy.header = Header();
diff --git a/third_party/WebKit/Source/core/frame/csp/CSPDirectiveList.h b/third_party/WebKit/Source/core/frame/csp/CSPDirectiveList.h
index 667724a..ed3d0cd 100644
--- a/third_party/WebKit/Source/core/frame/csp/CSPDirectiveList.h
+++ b/third_party/WebKit/Source/core/frame/csp/CSPDirectiveList.h
@@ -175,6 +175,7 @@
   // * child-src
   // * frame-src
   // * form-action
+  // * upgrade-insecure-requests
   // The exported directives only contains sources that affect navigation. For
   // instance it doesn't contains 'unsafe-inline' or 'unsafe-eval'
   WebContentSecurityPolicy ExposeForNavigationalChecks() const;
diff --git a/third_party/WebKit/Source/core/inspector/InspectorWorkerAgent.cpp b/third_party/WebKit/Source/core/inspector/InspectorWorkerAgent.cpp
index f1bb437..9c5c332 100644
--- a/third_party/WebKit/Source/core/inspector/InspectorWorkerAgent.cpp
+++ b/third_party/WebKit/Source/core/inspector/InspectorWorkerAgent.cpp
@@ -91,6 +91,10 @@
   return Response::OK();
 }
 
+Response InspectorWorkerAgent::setAttachToFrames(bool attach) {
+  return Response::OK();
+}
+
 bool InspectorWorkerAgent::AutoAttachEnabled() {
   return state_->booleanProperty(WorkerAgentState::kAutoAttach, false);
 }
diff --git a/third_party/WebKit/Source/core/inspector/InspectorWorkerAgent.h b/third_party/WebKit/Source/core/inspector/InspectorWorkerAgent.h
index 61fce17..28d0109c 100644
--- a/third_party/WebKit/Source/core/inspector/InspectorWorkerAgent.h
+++ b/third_party/WebKit/Source/core/inspector/InspectorWorkerAgent.h
@@ -64,6 +64,7 @@
   // Called from Dispatcher
   protocol::Response setAutoAttach(bool auto_attach,
                                    bool wait_for_debugger_on_start) override;
+  protocol::Response setAttachToFrames(bool attach) override;
   protocol::Response sendMessageToTarget(const String& target_id,
                                          const String& message) override;
 
diff --git a/third_party/WebKit/Source/core/inspector/inspector_protocol_config.json b/third_party/WebKit/Source/core/inspector/inspector_protocol_config.json
index 4d8f70bb..84ae2db 100644
--- a/third_party/WebKit/Source/core/inspector/inspector_protocol_config.json
+++ b/third_party/WebKit/Source/core/inspector/inspector_protocol_config.json
@@ -95,7 +95,7 @@
             },
             {
                 "domain": "Target",
-                "include": ["setAutoAttach", "sendMessageToTarget"],
+                "include": ["setAutoAttach", "sendMessageToTarget", "setAttachToFrames"],
                 "include_events": ["attachedToTarget", "detachedFromTarget", "receivedMessageFromTarget"]
             }
         ]
diff --git a/third_party/WebKit/Source/core/layout/LayoutGeometryMap.cpp b/third_party/WebKit/Source/core/layout/LayoutGeometryMap.cpp
index bc3df43e..a134c04 100644
--- a/third_party/WebKit/Source/core/layout/LayoutGeometryMap.cpp
+++ b/third_party/WebKit/Source/core/layout/LayoutGeometryMap.cpp
@@ -68,7 +68,8 @@
       !ancestor || (mapping_.size() && mapping_[0].layout_object_ == ancestor);
 #endif
 
-  for (int i = mapping_.size() - 1; i >= 0; --i) {
+  int i = mapping_.size() - 1;
+  for (; i >= 0; --i) {
     const LayoutGeometryMapStep& current_step = mapping_[i];
 
     // If container is the root LayoutView (step 0) we want to apply its fixed
@@ -111,9 +112,24 @@
                              current_step.offset_.Height(), accumulate);
     }
 
-    if (in_fixed && !current_step.offset_for_fixed_position_.IsZero()) {
-      DCHECK(current_step.layout_object_->IsLayoutView());
+    if (in_fixed && current_step.layout_object_->IsLayoutView()) {
       transform_state.Move(current_step.offset_for_fixed_position_);
+      in_fixed = false;
+    }
+  }
+
+  if (in_fixed) {
+    // In case we've not reached top ('ancestor' isn't top level view) either
+    // assure that 'ancestor' and object both fixed or apply fixed offset of
+    // the nearest containing view.
+    for (; i >= 0; --i) {
+      const LayoutGeometryMapStep& current_step = mapping_[i];
+      if (current_step.flags_ & (kContainsFixedPosition | kIsFixedPosition))
+        break;
+      if (current_step.layout_object_->IsLayoutView()) {
+        transform_state.Move(current_step.offset_for_fixed_position_);
+        break;
+      }
     }
   }
 
diff --git a/third_party/WebKit/Source/core/loader/FrameLoader.cpp b/third_party/WebKit/Source/core/loader/FrameLoader.cpp
index add4ac6..d0bfe59 100644
--- a/third_party/WebKit/Source/core/loader/FrameLoader.cpp
+++ b/third_party/WebKit/Source/core/loader/FrameLoader.cpp
@@ -66,7 +66,6 @@
 #include "core/loader/FormSubmission.h"
 #include "core/loader/FrameLoadRequest.h"
 #include "core/loader/LinkLoader.h"
-#include "core/loader/MixedContentChecker.h"
 #include "core/loader/NavigationScheduler.h"
 #include "core/loader/NetworkHintsInterface.h"
 #include "core/loader/ProgressTracker.h"
@@ -1353,11 +1352,6 @@
   RecordLatestRequiredCSP();
   // Before modifying the request, check report-only CSP headers to give the
   // site owner a chance to learn about requests that need to be modified.
-  //
-  // TODO(estark): this doesn't work with --enable-browser-side-navigation,
-  // wherein 'frame-src' is checked in the browser process. Figure out what to
-  // do; maybe with browser-side navigation the upgrade should be happening in
-  // the browser process too. See also https://crbug.com/692595
   Settings* settings = frame_->GetSettings();
   MaybeCheckCSP(
       resource_request, navigation_type, frame_, navigation_policy,
@@ -1599,6 +1593,12 @@
                                         "1");
   }
 
+  // PlzNavigate: Upgrading subframe requests is handled by the browser process.
+  Settings* settings = frame_->GetSettings();
+  if (resource_request.GetFrameType() == WebURLRequest::kFrameTypeNested &&
+      settings && settings->GetBrowserSideNavigationEnabled()) {
+    return;
+  }
   UpgradeInsecureRequest(resource_request, document);
 }
 
diff --git a/third_party/WebKit/Source/core/page/scrolling/ScrollingCoordinator.cpp b/third_party/WebKit/Source/core/page/scrolling/ScrollingCoordinator.cpp
index 2e4683b..4c74d84 100644
--- a/third_party/WebKit/Source/core/page/scrolling/ScrollingCoordinator.cpp
+++ b/third_party/WebKit/Source/core/page/scrolling/ScrollingCoordinator.cpp
@@ -598,10 +598,6 @@
     LayoutGeometryMap& geometry_map,
     HashSet<const PaintLayer*>& layers_with_rects,
     LayerFrameMap& layer_child_frame_map) {
-  // If this layer is throttled, ignore it.
-  if (cur_layer->GetLayoutObject().GetFrameView() &&
-      cur_layer->GetLayoutObject().GetFrameView()->ShouldThrottleRendering())
-    return;
   // Project any rects for the current layer
   LayerHitTestRects::const_iterator layer_iter = layer_rects.find(cur_layer);
   if (layer_iter != layer_rects.end()) {
@@ -664,6 +660,9 @@
   if (map_iter != layer_child_frame_map.end()) {
     for (size_t i = 0; i < map_iter->value.size(); i++) {
       const LocalFrame* child_frame = map_iter->value[i];
+      if (child_frame->ShouldThrottleRendering())
+        continue;
+
       const PaintLayer* child_layer =
           child_frame->View()->GetLayoutViewItem().Layer();
       if (layers_with_rects.Contains(child_layer)) {
@@ -685,6 +684,10 @@
     GraphicsLayerHitTestRects& graphics_rects) {
   TRACE_EVENT0("input",
                "ScrollingCoordinator::projectRectsToGraphicsLayerSpace");
+
+  if (main_frame->ShouldThrottleRendering())
+    return;
+
   bool touch_handler_in_child_frame = false;
 
   // We have a set of rects per Layer, we need to map them to their bounding
diff --git a/third_party/WebKit/Source/devtools/front_end/sdk/TargetManager.js b/third_party/WebKit/Source/devtools/front_end/sdk/TargetManager.js
index cc8ee55f..f7105c38 100644
--- a/third_party/WebKit/Source/devtools/front_end/sdk/TargetManager.js
+++ b/third_party/WebKit/Source/devtools/front_end/sdk/TargetManager.js
@@ -522,7 +522,6 @@
     var target = this._targetManager.createTarget(
         targetInfo.targetId, targetName, this._capabilitiesForType(targetInfo.type),
         this._createChildConnection.bind(this, this._targetAgent, targetInfo.targetId), this._parentTarget);
-    target[SDK.TargetManager._isWorkerSymbol] = targetInfo.type === 'worker';
 
     // Only pause the new worker if debugging SW - we are going through the pause on start checkbox.
     if (!this._parentTarget.parentTarget() && Runtime.queryParam('isSharedWorker') && waitingForDebugger) {
@@ -610,8 +609,6 @@
   AvailableNodeTargetsChanged: Symbol('AvailableNodeTargetsChanged')
 };
 
-SDK.TargetManager._isWorkerSymbol = Symbol('SDK.TargetManager.IsWorker');
-
 /**
  * @interface
  */
diff --git a/third_party/WebKit/Source/devtools/front_end/ui/TextPrompt.js b/third_party/WebKit/Source/devtools/front_end/ui/TextPrompt.js
index fc8404cb..8de1500d 100644
--- a/third_party/WebKit/Source/devtools/front_end/ui/TextPrompt.js
+++ b/third_party/WebKit/Source/devtools/front_end/ui/TextPrompt.js
@@ -583,6 +583,9 @@
     if (!node.isSelfOrDescendant(this._element))
       return false;
 
+    if (this._ghostTextElement.isAncestor(node))
+      return true;
+
     if (node.nodeType === Node.TEXT_NODE && selectionRange.startOffset < node.nodeValue.length)
       return false;
 
diff --git a/third_party/WebKit/Source/devtools/front_end/ui/treeoutline.js b/third_party/WebKit/Source/devtools/front_end/ui/treeoutline.js
index 4fafc44..43e5444f 100644
--- a/third_party/WebKit/Source/devtools/front_end/ui/treeoutline.js
+++ b/third_party/WebKit/Source/devtools/front_end/ui/treeoutline.js
@@ -1008,13 +1008,15 @@
   select(omitFocus, selectedByUser) {
     if (!this.treeOutline || !this.selectable || this.selected)
       return false;
-
-    if (this.treeOutline.selectedTreeElement)
-      this.treeOutline.selectedTreeElement.deselect();
+    // Wait to deselect this element so that focus only changes once
+    var lastSelected = this.treeOutline.selectedTreeElement;
     this.treeOutline.selectedTreeElement = null;
 
-    if (this.treeOutline._rootElement === this)
+    if (this.treeOutline._rootElement === this) {
+      if (lastSelected)
+        lastSelected.deselect();
       return false;
+    }
 
     this.selected = true;
 
@@ -1026,6 +1028,8 @@
 
     this._listItemNode.classList.add('selected');
     this.treeOutline.dispatchEventToListeners(UI.TreeOutline.Events.ElementSelected, this);
+    if (lastSelected)
+      lastSelected.deselect();
     return this.onselect(selectedByUser);
   }
 
diff --git a/third_party/WebKit/Source/modules/indexeddb/IDBObjectStore.cpp b/third_party/WebKit/Source/modules/indexeddb/IDBObjectStore.cpp
index ae56bd5..7b5031a 100644
--- a/third_party/WebKit/Source/modules/indexeddb/IDBObjectStore.cpp
+++ b/third_party/WebKit/Source/modules/indexeddb/IDBObjectStore.cpp
@@ -516,7 +516,8 @@
     index_ids.push_back(it.key);
     index_keys.push_back(keys);
   }
-  UMA_HISTOGRAM_MEMORY_KB("WebCore.IndexedDB.PutValueSize",
+  // Records 1KB to 1GB.
+  UMA_HISTOGRAM_COUNTS_1M("WebCore.IndexedDB.PutValueSize2",
                           value_wrapper.DataLengthBeforeWrapInBytes() / 1024);
 
   IDBRequest* request = IDBRequest::Create(
diff --git a/third_party/WebKit/Source/modules/presentation/PresentationAvailability.cpp b/third_party/WebKit/Source/modules/presentation/PresentationAvailability.cpp
index 81e0c28..8de53d79e 100644
--- a/third_party/WebKit/Source/modules/presentation/PresentationAvailability.cpp
+++ b/third_party/WebKit/Source/modules/presentation/PresentationAvailability.cpp
@@ -67,7 +67,9 @@
   }
 }
 
-void PresentationAvailability::AvailabilityChanged(bool value) {
+void PresentationAvailability::AvailabilityChanged(
+    blink::mojom::ScreenAvailability availability) {
+  bool value = availability == blink::mojom::ScreenAvailability::AVAILABLE;
   if (value_ == value)
     return;
 
diff --git a/third_party/WebKit/Source/modules/presentation/PresentationAvailability.h b/third_party/WebKit/Source/modules/presentation/PresentationAvailability.h
index fed9aa6..28ba75d 100644
--- a/third_party/WebKit/Source/modules/presentation/PresentationAvailability.h
+++ b/third_party/WebKit/Source/modules/presentation/PresentationAvailability.h
@@ -45,7 +45,7 @@
   ExecutionContext* GetExecutionContext() const override;
 
   // WebPresentationAvailabilityObserver implementation.
-  void AvailabilityChanged(bool) override;
+  void AvailabilityChanged(blink::mojom::ScreenAvailability) override;
   const WebVector<WebURL>& Urls() const override;
 
   // ScriptWrappable implementation.
diff --git a/third_party/WebKit/Source/modules/remoteplayback/RemotePlayback.cpp b/third_party/WebKit/Source/modules/remoteplayback/RemotePlayback.cpp
index 7b38671..55d99ad 100644
--- a/third_party/WebKit/Source/modules/remoteplayback/RemotePlayback.cpp
+++ b/third_party/WebKit/Source/modules/remoteplayback/RemotePlayback.cpp
@@ -21,6 +21,7 @@
 #include "platform/MemoryCoordinator.h"
 #include "platform/json/JSONValues.h"
 #include "platform/wtf/text/Base64.h"
+#include "public/platform/modules/presentation/WebPresentationClient.h"
 
 namespace blink {
 
@@ -202,7 +203,6 @@
     return promise;
   }
 
-  // TODO(avayvod): this state is not propagated with the new pipeline.
   if (availability_ == WebRemotePlaybackAvailability::kSourceNotCompatible) {
     resolver->Reject(DOMException::Create(
         kNotSupportedError,
@@ -394,12 +394,35 @@
     media_element_->RequestRemotePlaybackStop();
 }
 
-void RemotePlayback::AvailabilityChanged(bool availability) {
+void RemotePlayback::AvailabilityChanged(
+    mojom::ScreenAvailability availability) {
   DCHECK(RuntimeEnabledFeatures::NewRemotePlaybackPipelineEnabled());
   DCHECK(is_listening_);
-  AvailabilityChanged(availability
-                          ? WebRemotePlaybackAvailability::kDeviceAvailable
-                          : WebRemotePlaybackAvailability::kDeviceNotAvailable);
+
+  // TODO(avayvod): Use mojom::ScreenAvailability directly once
+  // WebRemotePlaybackAvailability is gone with the old pipeline.
+  WebRemotePlaybackAvailability remote_playback_availability =
+      WebRemotePlaybackAvailability::kUnknown;
+  switch (availability) {
+    case mojom::ScreenAvailability::UNKNOWN:
+    case mojom::ScreenAvailability::DISABLED:
+      NOTREACHED();
+      remote_playback_availability = WebRemotePlaybackAvailability::kUnknown;
+      break;
+    case mojom::ScreenAvailability::UNAVAILABLE:
+      remote_playback_availability =
+          WebRemotePlaybackAvailability::kDeviceNotAvailable;
+      break;
+    case mojom::ScreenAvailability::SOURCE_NOT_SUPPORTED:
+      remote_playback_availability =
+          WebRemotePlaybackAvailability::kSourceNotCompatible;
+      break;
+    case mojom::ScreenAvailability::AVAILABLE:
+      remote_playback_availability =
+          WebRemotePlaybackAvailability::kDeviceAvailable;
+      break;
+  }
+  AvailabilityChanged(remote_playback_availability);
 }
 
 const WebVector<WebURL>& RemotePlayback::Urls() const {
diff --git a/third_party/WebKit/Source/modules/remoteplayback/RemotePlayback.h b/third_party/WebKit/Source/modules/remoteplayback/RemotePlayback.h
index c18c729..70ac0b39 100644
--- a/third_party/WebKit/Source/modules/remoteplayback/RemotePlayback.h
+++ b/third_party/WebKit/Source/modules/remoteplayback/RemotePlayback.h
@@ -78,7 +78,7 @@
   WebRemotePlaybackState GetState() const { return state_; }
 
   // WebPresentationAvailabilityObserver implementation.
-  void AvailabilityChanged(bool) override;
+  void AvailabilityChanged(mojom::ScreenAvailability) override;
   const WebVector<WebURL>& Urls() const override;
 
   // WebRemotePlaybackClient implementation.
diff --git a/third_party/WebKit/Source/modules/remoteplayback/RemotePlaybackTest.cpp b/third_party/WebKit/Source/modules/remoteplayback/RemotePlaybackTest.cpp
index 096411d..837461e 100644
--- a/third_party/WebKit/Source/modules/remoteplayback/RemotePlaybackTest.cpp
+++ b/third_party/WebKit/Source/modules/remoteplayback/RemotePlaybackTest.cpp
@@ -419,7 +419,7 @@
       WebURL(KURL(kParsedURLString, "http://www.example.com")));
   ASSERT_EQ((size_t)1, remote_playback->Urls().size());
   ASSERT_TRUE(IsListening(remote_playback));
-  remote_playback->AvailabilityChanged(true);
+  remote_playback->AvailabilityChanged(mojom::ScreenAvailability::AVAILABLE);
 
   remote_playback->cancelWatchAvailability(scope.GetScriptState());
   ASSERT_EQ((size_t)1, remote_playback->Urls().size());
@@ -429,7 +429,7 @@
                                      availability_callback);
   ASSERT_EQ((size_t)1, remote_playback->Urls().size());
   ASSERT_TRUE(IsListening(remote_playback));
-  remote_playback->AvailabilityChanged(true);
+  remote_playback->AvailabilityChanged(mojom::ScreenAvailability::AVAILABLE);
 
   remote_playback->SourceChanged(WebURL());
   ASSERT_TRUE(remote_playback->Urls().empty());
diff --git a/third_party/WebKit/Source/web/WebLocalFrameImpl.cpp b/third_party/WebKit/Source/web/WebLocalFrameImpl.cpp
index aae2a40..9ed6926 100644
--- a/third_party/WebKit/Source/web/WebLocalFrameImpl.cpp
+++ b/third_party/WebKit/Source/web/WebLocalFrameImpl.cpp
@@ -843,6 +843,10 @@
   return script_state->GetContext();
 }
 
+v8::Local<v8::Object> WebLocalFrameImpl::GlobalProxy() const {
+  return MainWorldScriptContext()->Global();
+}
+
 bool WebFrame::ScriptCanAccess(WebFrame* target) {
   return BindingSecurity::ShouldAllowAccessToFrame(
       CurrentDOMWindow(MainThreadIsolate()), ToCoreFrame(*target),
diff --git a/third_party/WebKit/Source/web/WebLocalFrameImpl.h b/third_party/WebKit/Source/web/WebLocalFrameImpl.h
index e807e41..554e446 100644
--- a/third_party/WebKit/Source/web/WebLocalFrameImpl.h
+++ b/third_party/WebKit/Source/web/WebLocalFrameImpl.h
@@ -146,6 +146,7 @@
       int argc,
       v8::Local<v8::Value> argv[]) override;
   v8::Local<v8::Context> MainWorldScriptContext() const override;
+  v8::Local<v8::Object> GlobalProxy() const override;
   void Reload(WebFrameLoadType) override;
   void ReloadWithOverrideURL(const WebURL& override_url,
                              WebFrameLoadType) override;
diff --git a/third_party/WebKit/Source/web/tests/LayoutGeometryMapTest.cpp b/third_party/WebKit/Source/web/tests/LayoutGeometryMapTest.cpp
index 1e0d49cf..dd588373 100644
--- a/third_party/WebKit/Source/web/tests/LayoutGeometryMapTest.cpp
+++ b/third_party/WebKit/Source/web/tests/LayoutGeometryMapTest.cpp
@@ -335,6 +335,9 @@
                              GetLayoutBox(web_view, "simple-container"));
   EXPECT_EQ(FloatRect(8.0f, 50.0f, 100.0f, 100.0f),
             AdjustForFrameScroll(web_view, rgm.AbsoluteRect(rect)));
+  EXPECT_EQ(
+      FloatQuad(FloatRect(0.0f, -50.0f, 100.0f, 100.0f)),
+      rgm.MapToAncestor(rect, GetLayoutBox(web_view, "simple-container")));
   rgm.PopMappingsToAncestor(static_cast<PaintLayer*>(nullptr));
 
   // Transforms contain fixed position descendants.
diff --git a/third_party/WebKit/Source/web/tests/PrerenderingTest.cpp b/third_party/WebKit/Source/web/tests/PrerenderingTest.cpp
index 8eaf3cb..5c0338a 100644
--- a/third_party/WebKit/Source/web/tests/PrerenderingTest.cpp
+++ b/third_party/WebKit/Source/web/tests/PrerenderingTest.cpp
@@ -182,7 +182,7 @@
   }
 
   void Close() {
-    web_view_helper_.WebView()->MainFrame()->CollectGarbage();
+    web_view_helper_.WebView()->MainFrameImpl()->CollectGarbage();
     web_view_helper_.Reset();
 
     WebCache::Clear();
diff --git a/third_party/WebKit/Source/web/tests/WebFrameTest.cpp b/third_party/WebKit/Source/web/tests/WebFrameTest.cpp
index 9171bd5c..ba09ca08 100644
--- a/third_party/WebKit/Source/web/tests/WebFrameTest.cpp
+++ b/third_party/WebKit/Source/web/tests/WebFrameTest.cpp
@@ -403,14 +403,15 @@
   web_view_helper.InitializeAndLoad(base_url_ + "iframes_test.html", true);
 
   v8::HandleScope scope(v8::Isolate::GetCurrent());
-  EXPECT_EQ(
-      web_view_helper.WebView()->MainFrame(),
-      WebLocalFrame::FrameForContext(
-          web_view_helper.WebView()->MainFrame()->MainWorldScriptContext()));
+  EXPECT_EQ(web_view_helper.WebView()->MainFrame(),
+            WebLocalFrame::FrameForContext(web_view_helper.WebView()
+                                               ->MainFrameImpl()
+                                               ->MainWorldScriptContext()));
   EXPECT_EQ(web_view_helper.WebView()->MainFrame()->FirstChild(),
             WebLocalFrame::FrameForContext(web_view_helper.WebView()
                                                ->MainFrame()
                                                ->FirstChild()
+                                               ->ToWebLocalFrame()
                                                ->MainWorldScriptContext()));
 }
 
@@ -451,7 +452,7 @@
 
   v8::HandleScope scope(v8::Isolate::GetCurrent());
   ScriptExecutionCallbackHelper callback_helper(
-      web_view_helper.WebView()->MainFrame()->MainWorldScriptContext());
+      web_view_helper.WebView()->MainFrameImpl()->MainWorldScriptContext());
   web_view_helper.WebView()
       ->MainFrameImpl()
       ->RequestExecuteScriptAndReturnValue(
@@ -470,7 +471,7 @@
 
   v8::HandleScope scope(v8::Isolate::GetCurrent());
   ScriptExecutionCallbackHelper callback_helper(
-      web_view_helper.WebView()->MainFrame()->MainWorldScriptContext());
+      web_view_helper.WebView()->MainFrameImpl()->MainWorldScriptContext());
 
   // Suspend scheduled tasks so the script doesn't run.
   web_view_helper.WebView()
@@ -505,7 +506,7 @@
 
   v8::HandleScope scope(v8::Isolate::GetCurrent());
   v8::Local<v8::Context> context =
-      web_view_helper.WebView()->MainFrame()->MainWorldScriptContext();
+      web_view_helper.WebView()->MainFrameImpl()->MainWorldScriptContext();
   ScriptExecutionCallbackHelper callback_helper(context);
   v8::Local<v8::Function> function =
       v8::Function::New(context, callback).ToLocalChecked();
@@ -532,7 +533,7 @@
 
   v8::HandleScope scope(v8::Isolate::GetCurrent());
   v8::Local<v8::Context> context =
-      web_view_helper.WebView()->MainFrame()->MainWorldScriptContext();
+      web_view_helper.WebView()->MainFrameImpl()->MainWorldScriptContext();
 
   // Suspend scheduled tasks so the script doesn't run.
   WebLocalFrameBase* main_frame = web_view_helper.WebView()->MainFrameImpl();
@@ -572,7 +573,7 @@
 
   v8::HandleScope scope(v8::Isolate::GetCurrent());
   v8::Local<v8::Context> context =
-      web_view_helper.WebView()->MainFrame()->MainWorldScriptContext();
+      web_view_helper.WebView()->MainFrameImpl()->MainWorldScriptContext();
 
   std::unique_ptr<UserGestureIndicator> indicator =
       WTF::WrapUnique(new UserGestureIndicator(
@@ -602,7 +603,7 @@
 
   v8::HandleScope scope(v8::Isolate::GetCurrent());
   ScriptExecutionCallbackHelper callback_helper(
-      web_view_helper.WebView()->MainFrame()->MainWorldScriptContext());
+      web_view_helper.WebView()->MainFrameImpl()->MainWorldScriptContext());
   web_view_helper.WebView()
       ->MainFrame()
       ->FirstChild()
@@ -4574,7 +4575,7 @@
   web_view_helper.InitializeAndLoad(
       base_url_ + "context_notifications_test.html", true, &web_frame_client);
 
-  WebFrame* main_frame = web_view_helper.WebView()->MainFrame();
+  WebLocalFrameBase* main_frame = web_view_helper.WebView()->MainFrameImpl();
   WebFrame* child_frame = main_frame->FirstChild();
 
   ASSERT_EQ(2u, create_notifications.size());
@@ -4589,7 +4590,7 @@
   EXPECT_EQ(0, first_create_notification->world_id);
 
   EXPECT_EQ(child_frame, second_create_notification->frame);
-  EXPECT_EQ(child_frame->MainWorldScriptContext(),
+  EXPECT_EQ(child_frame->ToWebLocalFrame()->MainWorldScriptContext(),
             second_create_notification->context);
   EXPECT_EQ(0, second_create_notification->world_id);
 
@@ -4638,7 +4639,7 @@
 
   // The last two create notifications should be for the current frames and
   // context.
-  WebFrame* main_frame = web_view_helper.WebView()->MainFrame();
+  WebLocalFrameBase* main_frame = web_view_helper.WebView()->MainFrameImpl();
   WebFrame* child_frame = main_frame->FirstChild();
   auto& first_refresh_notification = create_notifications[2];
   auto& second_refresh_notification = create_notifications[3];
@@ -4649,7 +4650,7 @@
   EXPECT_EQ(0, first_refresh_notification->world_id);
 
   EXPECT_EQ(child_frame, second_refresh_notification->frame);
-  EXPECT_EQ(child_frame->MainWorldScriptContext(),
+  EXPECT_EQ(child_frame->ToWebLocalFrame()->MainWorldScriptContext(),
             second_refresh_notification->context);
   EXPECT_EQ(0, second_refresh_notification->world_id);
 }
@@ -4688,8 +4689,9 @@
 
   // We don't have an API to enumarate isolated worlds for a frame, but we can
   // at least assert that the context we got is *not* the main world's context.
-  ASSERT_NE(web_view_helper.WebView()->MainFrame()->MainWorldScriptContext(),
-            v8::Local<v8::Context>::New(isolate, notification->context));
+  ASSERT_NE(
+      web_view_helper.WebView()->MainFrameImpl()->MainWorldScriptContext(),
+      v8::Local<v8::Context>::New(isolate, notification->context));
 
   web_view_helper.Reset();
 
@@ -9942,7 +9944,7 @@
     String code = "dumpSize('" + id + "')";
     v8::HandleScope scope(v8::Isolate::GetCurrent());
     ScriptExecutionCallbackHelper callback_helper(
-        web_view_helper_.WebView()->MainFrame()->MainWorldScriptContext());
+        web_view_helper_.WebView()->MainFrameImpl()->MainWorldScriptContext());
     web_view_helper_.WebView()
         ->MainFrameImpl()
         ->RequestExecuteScriptAndReturnValue(WebScriptSource(WebString(code)),
diff --git a/third_party/WebKit/Source/web/tests/WebViewTest.cpp b/third_party/WebKit/Source/web/tests/WebViewTest.cpp
index 0d012e0c..685aaba 100644
--- a/third_party/WebKit/Source/web/tests/WebViewTest.cpp
+++ b/third_party/WebKit/Source/web/tests/WebViewTest.cpp
@@ -3809,7 +3809,7 @@
   FrameTestHelpers::WebViewHelper main_web_view;
   main_web_view.InitializeAndLoad("about:blank", true, 0, &client);
 
-  WebFrame* frame = main_web_view.WebView()->MainFrame();
+  WebLocalFrame* frame = main_web_view.WebView()->MainFrameImpl();
   v8::HandleScope scope(v8::Isolate::GetCurrent());
   v8::Local<v8::Value> v8_value =
       frame->ExecuteScriptAndReturnValue(WebScriptSource(
diff --git a/third_party/WebKit/public/platform/modules/presentation/OWNERS b/third_party/WebKit/public/platform/modules/presentation/OWNERS
index b372098..cc1e3e9 100644
--- a/third_party/WebKit/public/platform/modules/presentation/OWNERS
+++ b/third_party/WebKit/public/platform/modules/presentation/OWNERS
@@ -1,2 +1,4 @@
 file://third_party/WebKit/Source/modules/presentation/OWNERS
 
+per-file *.mojom=set noparent
+per-file *.mojom=file://ipc/SECURITY_OWNERS
diff --git a/third_party/WebKit/public/platform/modules/presentation/WebPresentationAvailabilityObserver.h b/third_party/WebKit/public/platform/modules/presentation/WebPresentationAvailabilityObserver.h
index a32cec6..eea4ed1 100644
--- a/third_party/WebKit/public/platform/modules/presentation/WebPresentationAvailabilityObserver.h
+++ b/third_party/WebKit/public/platform/modules/presentation/WebPresentationAvailabilityObserver.h
@@ -7,6 +7,7 @@
 
 #include "public/platform/WebCommon.h"
 #include "public/platform/WebURL.h"
+#include "public/platform/modules/presentation/presentation.mojom-blink.h"
 
 namespace blink {
 
@@ -21,7 +22,7 @@
  public:
   virtual ~WebPresentationAvailabilityObserver() = default;
 
-  virtual void AvailabilityChanged(bool) = 0;
+  virtual void AvailabilityChanged(blink::mojom::ScreenAvailability) = 0;
 
   virtual const WebVector<WebURL>& Urls() const = 0;
 };
diff --git a/third_party/WebKit/public/platform/modules/presentation/presentation.mojom b/third_party/WebKit/public/platform/modules/presentation/presentation.mojom
index a511c07..f68bc43a 100644
--- a/third_party/WebKit/public/platform/modules/presentation/presentation.mojom
+++ b/third_party/WebKit/public/platform/modules/presentation/presentation.mojom
@@ -11,6 +11,14 @@
   string id;
 };
 
+enum ScreenAvailability {
+  UNKNOWN,
+  UNAVAILABLE,
+  SOURCE_NOT_SUPPORTED,
+  DISABLED,
+  AVAILABLE
+};
+
 enum PresentationConnectionState {
   CONNECTING,
   CONNECTED,
@@ -111,7 +119,7 @@
   // This is called after a connection has been established to the presentation
   // from the frame.
   ListenForConnectionMessages(PresentationInfo presentation_info);
-};      
+};
 
 interface PresentationServiceClient {
   //////////  This API is implemented by a controlling frame.  /////////////////
@@ -126,7 +134,8 @@
   // presentation of |url| and the state changes. When the client starts to
   // listen for screen availability, this method will always be called to give
   // the current known state. It will then be called to notify of state updates.
-  OnScreenAvailabilityUpdated(url.mojom.Url url, bool available);
+  OnScreenAvailabilityUpdated(url.mojom.Url url,
+                              ScreenAvailability availability);
 
   // See PresentationService::SetDefaultPresentationURL.
   OnDefaultPresentationStarted(PresentationInfo presentation_info);
@@ -156,6 +165,4 @@
   // See PresentationService::ListenForConnectionMessages.
   OnConnectionMessagesReceived(PresentationInfo presentation_info,
                                array<PresentationConnectionMessage> messages);
-
-
 };
diff --git a/third_party/WebKit/public/web/WebFrame.h b/third_party/WebKit/public/web/WebFrame.h
index 4b95fb4..ea7abf1 100644
--- a/third_party/WebKit/public/web/WebFrame.h
+++ b/third_party/WebKit/public/web/WebFrame.h
@@ -40,14 +40,7 @@
 #include "public/platform/WebInsecureRequestPolicy.h"
 #include "public/web/WebFrameLoadType.h"
 #include "public/web/WebTreeScopeType.h"
-
-namespace v8 {
-class Context;
-class Function;
-class Value;
-template <class T>
-class Local;
-}
+#include "v8/include/v8.h"
 
 namespace blink {
 
@@ -71,12 +64,8 @@
 struct WebFrameOwnerProperties;
 struct WebPrintParams;
 struct WebRect;
-struct WebScriptSource;
 struct WebSize;
 
-template <typename T>
-class WebVector;
-
 // Frames may be rendered in process ('local') or out of process ('remote').
 // A remote frame is always cross-site; a local frame may be either same-site or
 // cross-site.
@@ -219,65 +208,8 @@
 
   // Scripting ----------------------------------------------------------
 
-  // Executes JavaScript in a new world associated with the web frame.
-  // The script gets its own global scope and its own prototypes for
-  // intrinsic JavaScript objects (String, Array, and so-on). It also
-  // gets its own wrappers for all DOM nodes and DOM constructors.
-  //
-  // worldID must be > 0 (as 0 represents the main world).
-  // worldID must be < EmbedderWorldIdLimit, high number used internally.
-  virtual void ExecuteScriptInIsolatedWorld(int world_id,
-                                            const WebScriptSource* sources,
-                                            unsigned num_sources) = 0;
-
-  // Associates an isolated world (see above for description) with a security
-  // origin. XMLHttpRequest instances used in that world will be considered
-  // to come from that origin, not the frame's.
-  virtual void SetIsolatedWorldSecurityOrigin(int world_id,
-                                              const WebSecurityOrigin&) = 0;
-
-  // Associates a content security policy with an isolated world. This policy
-  // should be used when evaluating script in the isolated world, and should
-  // also replace a protected resource's CSP when evaluating resources
-  // injected into the DOM.
-  //
-  // FIXME: Setting this simply bypasses the protected resource's CSP. It
-  //     doesn't yet restrict the isolated world to the provided policy.
-  virtual void SetIsolatedWorldContentSecurityPolicy(int world_id,
-                                                     const WebString&) = 0;
-
-  // Calls window.gc() if it is defined.
-  virtual void CollectGarbage() = 0;
-
-  // Executes script in the context of the current page and returns the value
-  // that the script evaluated to.
-  // DEPRECATED: Use WebLocalFrame::requestExecuteScriptAndReturnValue.
-  virtual v8::Local<v8::Value> ExecuteScriptAndReturnValue(
-      const WebScriptSource&) = 0;
-
-  // worldID must be > 0 (as 0 represents the main world).
-  // worldID must be < EmbedderWorldIdLimit, high number used internally.
-  // DEPRECATED: Use WebLocalFrame::requestExecuteScriptInIsolatedWorld.
-  virtual void ExecuteScriptInIsolatedWorld(
-      int world_id,
-      const WebScriptSource* sources_in,
-      unsigned num_sources,
-      WebVector<v8::Local<v8::Value>>* results) = 0;
-
-  // Call the function with the given receiver and arguments, bypassing
-  // canExecute().
-  virtual v8::Local<v8::Value> CallFunctionEvenIfScriptDisabled(
-      v8::Local<v8::Function>,
-      v8::Local<v8::Value>,
-      int argc,
-      v8::Local<v8::Value> argv[]) = 0;
-
-  // Returns the V8 context for associated with the main world and this
-  // frame. There can be many V8 contexts associated with this frame, one for
-  // each isolated world and one for the main world. If you don't know what
-  // the "main world" or an "isolated world" is, then you probably shouldn't
-  // be calling this API.
-  virtual v8::Local<v8::Context> MainWorldScriptContext() const = 0;
+  // Returns the global proxy object.
+  virtual v8::Local<v8::Object> GlobalProxy() const = 0;
 
   // Returns true if the WebFrame currently executing JavaScript has access
   // to the given WebFrame, or false otherwise.
diff --git a/third_party/WebKit/public/web/WebLocalFrame.h b/third_party/WebKit/public/web/WebLocalFrame.h
index 0196d82..7b2ecf30 100644
--- a/third_party/WebKit/public/web/WebLocalFrame.h
+++ b/third_party/WebKit/public/web/WebLocalFrame.h
@@ -14,6 +14,7 @@
 #include "public/platform/WebURLError.h"
 #include "public/platform/WebURLRequest.h"
 #include "public/platform/site_engagement.mojom-shared.h"
+#include "v8/include/v8.h"
 
 namespace base {
 class SingleThreadTaskRunner;
@@ -49,6 +50,7 @@
 struct WebFindOptions;
 struct WebFloatRect;
 struct WebPrintPresetOptions;
+struct WebScriptSource;
 struct WebSourceLocation;
 
 // Interface for interacting with in process frames. This contains methods that
@@ -296,6 +298,66 @@
   // Executes script in the context of the current page.
   virtual void ExecuteScript(const WebScriptSource&) = 0;
 
+  // Executes JavaScript in a new world associated with the web frame.
+  // The script gets its own global scope and its own prototypes for
+  // intrinsic JavaScript objects (String, Array, and so-on). It also
+  // gets its own wrappers for all DOM nodes and DOM constructors.
+  //
+  // worldID must be > 0 (as 0 represents the main world).
+  // worldID must be < EmbedderWorldIdLimit, high number used internally.
+  virtual void ExecuteScriptInIsolatedWorld(int world_id,
+                                            const WebScriptSource* sources,
+                                            unsigned num_sources) = 0;
+
+  // worldID must be > 0 (as 0 represents the main world).
+  // worldID must be < EmbedderWorldIdLimit, high number used internally.
+  // DEPRECATED: Use WebLocalFrame::requestExecuteScriptInIsolatedWorld.
+  virtual void ExecuteScriptInIsolatedWorld(
+      int world_id,
+      const WebScriptSource* sources_in,
+      unsigned num_sources,
+      WebVector<v8::Local<v8::Value>>* results) = 0;
+
+  // Associates an isolated world (see above for description) with a security
+  // origin. XMLHttpRequest instances used in that world will be considered
+  // to come from that origin, not the frame's.
+  virtual void SetIsolatedWorldSecurityOrigin(int world_id,
+                                              const WebSecurityOrigin&) = 0;
+
+  // Associates a content security policy with an isolated world. This policy
+  // should be used when evaluating script in the isolated world, and should
+  // also replace a protected resource's CSP when evaluating resources
+  // injected into the DOM.
+  //
+  // FIXME: Setting this simply bypasses the protected resource's CSP. It
+  //     doesn't yet restrict the isolated world to the provided policy.
+  virtual void SetIsolatedWorldContentSecurityPolicy(int world_id,
+                                                     const WebString&) = 0;
+
+  // Calls window.gc() if it is defined.
+  virtual void CollectGarbage() = 0;
+
+  // Executes script in the context of the current page and returns the value
+  // that the script evaluated to.
+  // DEPRECATED: Use WebLocalFrame::requestExecuteScriptAndReturnValue.
+  virtual v8::Local<v8::Value> ExecuteScriptAndReturnValue(
+      const WebScriptSource&) = 0;
+
+  // Call the function with the given receiver and arguments, bypassing
+  // canExecute().
+  virtual v8::Local<v8::Value> CallFunctionEvenIfScriptDisabled(
+      v8::Local<v8::Function>,
+      v8::Local<v8::Value>,
+      int argc,
+      v8::Local<v8::Value> argv[]) = 0;
+
+  // Returns the V8 context for associated with the main world and this
+  // frame. There can be many V8 contexts associated with this frame, one for
+  // each isolated world and one for the main world. If you don't know what
+  // the "main world" or an "isolated world" is, then you probably shouldn't
+  // be calling this API.
+  virtual v8::Local<v8::Context> MainWorldScriptContext() const = 0;
+
   // Executes script in the context of the current page and returns the value
   // that the script evaluated to with callback. Script execution can be
   // suspend.
diff --git a/third_party/WebKit/public/web/WebRemoteFrame.h b/third_party/WebKit/public/web/WebRemoteFrame.h
index 52039dc..5c23c51 100644
--- a/third_party/WebKit/public/web/WebRemoteFrame.h
+++ b/third_party/WebKit/public/web/WebRemoteFrame.h
@@ -100,8 +100,6 @@
 
   virtual void SetHasReceivedUserGesture() = 0;
 
-  virtual v8::Local<v8::Object> GlobalProxy() const = 0;
-
  protected:
   explicit WebRemoteFrame(WebTreeScopeType scope) : WebFrame(scope) {}
 
diff --git a/third_party/closure_compiler/compile2.py b/third_party/closure_compiler/compile2.py
index 224856d7..260c57e7 100755
--- a/third_party/closure_compiler/compile2.py
+++ b/third_party/closure_compiler/compile2.py
@@ -243,7 +243,7 @@
 
     self._log_debug("Args: %s" % " ".join(args))
 
-    _, stderr = self.run_jar(self._compiler_jar, args)
+    return_code, stderr = self.run_jar(self._compiler_jar, args)
 
     errors = stderr.strip().split("\n\n")
     maybe_summary = errors.pop()
@@ -262,7 +262,7 @@
         os.remove(out_file)
       if os.path.exists(self._MAP_FILE_FORMAT % out_file):
         os.remove(self._MAP_FILE_FORMAT % out_file)
-    elif checks_only:
+    elif checks_only and return_code == 0:
       # Compile succeeded but --checks_only disables --js_output_file from
       # actually writing a file. Write a file ourselves so incremental builds
       # still work.
@@ -280,7 +280,7 @@
         self._log_debug("Output: %s" % output)
 
     self._nuke_temp_files()
-    return bool(errors), stderr
+    return bool(errors) or return_code > 0, stderr
 
 
 if __name__ == "__main__":
diff --git a/tools/json_schema_compiler/idl_schema.py b/tools/json_schema_compiler/idl_schema.py
index fcad49b..7bbe842 100755
--- a/tools/json_schema_compiler/idl_schema.py
+++ b/tools/json_schema_compiler/idl_schema.py
@@ -182,12 +182,15 @@
     name = self.node.GetName()
     if self.node.GetProperty('deprecated'):
       properties['deprecated'] = self.node.GetProperty('deprecated')
-    if self.node.GetProperty('allowAmbiguousOptionalArguments'):
-      properties['allowAmbiguousOptionalArguments'] = True
-    for property_name in ('OPTIONAL', 'nodoc', 'nocompile', 'nodart',
-                          'nodefine'):
+
+    for property_name in ['allowAmbiguousOptionalArguments', 'forIOThread',
+                          'nodoc', 'nocompile', 'nodart', 'nodefine']:
       if self.node.GetProperty(property_name):
-        properties[property_name.lower()] = True
+        properties[property_name] = True
+
+    if self.node.GetProperty('OPTIONAL'):
+      properties['optional'] = True
+
     for option_name, sanitizer in [
         ('maxListeners', int),
         ('supportsFilters', lambda s: s == 'true'),
diff --git a/tools/json_schema_compiler/idl_schema_test.py b/tools/json_schema_compiler/idl_schema_test.py
index f9a4d87..4b47b44f 100755
--- a/tools/json_schema_compiler/idl_schema_test.py
+++ b/tools/json_schema_compiler/idl_schema_test.py
@@ -79,6 +79,17 @@
         'c': {'name': 'c', 'type': 'string'}},
       getType(self.idl_basics, 'MyType1')['properties'])
 
+  def testIOThreadFunc(self):
+    schema = self.idl_basics
+
+    func = getFunction(schema, 'function32')
+    self.assertTrue(func is not None)
+    self.assertTrue(func['forIOThread'])
+
+    func = getFunction(schema, 'function1')
+    self.assertTrue(func is not None)
+    self.assertTrue('forIOThread' not in func)
+
   def testMemberOrdering(self):
     self.assertEquals(
         ['x', 'y', 'z', 'a', 'b', 'c'],
diff --git a/tools/json_schema_compiler/test/idl_basics.idl b/tools/json_schema_compiler/test/idl_basics.idl
index bfa49dce..c4fb5aa 100644
--- a/tools/json_schema_compiler/test/idl_basics.idl
+++ b/tools/json_schema_compiler/test/idl_basics.idl
@@ -102,6 +102,8 @@
     static idl_other_namespace.SomeType[] function30();
 
     [nodefine] static void function31(long switch);
+
+    [forIOThread] static void function32();
   };
 
   interface Events {
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index 099d7ca..fbcbaa7 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -5636,6 +5636,7 @@
   <int value="31" label="Permission autoblocker data setting"/>
   <int value="32" label="Subresource filter setting"/>
   <int value="33" label="Subresource filter metadata"/>
+  <int value="34" label="Password protection setting"/>
 </enum>
 
 <enum name="ContentTypeParseableResult" type="int">
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index f3310ae..3616616 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -51480,8 +51480,8 @@
   </summary>
 </histogram>
 
-<histogram name="PasswordProtection.NumberOfCachedVerdictBeforeShutdown"
-    units="count">
+<histogram base="true"
+    name="PasswordProtection.NumberOfCachedVerdictBeforeShutdown" units="count">
   <owner>jialiul@chromium.org</owner>
   <owner>nparker@chromium.org</owner>
   <summary>
@@ -82504,6 +82504,17 @@
 </histogram>
 
 <histogram name="WebCore.IndexedDB.PutValueSize" units="KB">
+  <obsolete>
+    Replaced with WebCore.IndexedDB.PutValueSize2 on 6/2017
+  </obsolete>
+  <owner>dmurph@chromium.org</owner>
+  <summary>
+    The size of the IndexedDB value used in an IndexedDB object store 'put'
+    operation. Recorded for every 'put' operation.
+  </summary>
+</histogram>
+
+<histogram name="WebCore.IndexedDB.PutValueSize2" units="KB">
   <owner>dmurph@chromium.org</owner>
   <summary>
     The size of the IndexedDB value used in an IndexedDB object store 'put'
@@ -82556,6 +82567,19 @@
 
 <histogram name="WebCore.IndexedDB.Transaction.ReadOnly.SizeOnCommit"
     units="KB">
+  <obsolete>
+    Replaced with WebCore.IndexedDB.Transaction.ReadOnly.SizeOnCommit2 on 6/2017
+  </obsolete>
+  <owner>dmurph@chromium.org</owner>
+  <summary>
+    The total temporary size of an IndexedDB ReadOnly Transaction. Since this is
+    a readonly transaction, the size should only be &gt;0 when the transaction
+    is removing tombstone index keys. Recorded on transaction commit.
+  </summary>
+</histogram>
+
+<histogram name="WebCore.IndexedDB.Transaction.ReadOnly.SizeOnCommit2"
+    units="KB">
   <owner>dmurph@chromium.org</owner>
   <summary>
     The total temporary size of an IndexedDB ReadOnly Transaction. Since this is
@@ -82575,6 +82599,20 @@
 
 <histogram name="WebCore.IndexedDB.Transaction.ReadWrite.SizeOnCommit"
     units="KB">
+  <obsolete>
+    Replaced with WebCore.IndexedDB.Transaction.ReadWrite.SizeOnCommit2 on
+    6/2017
+  </obsolete>
+  <owner>dmurph@chromium.org</owner>
+  <summary>
+    The total temporary size of an IndexedDB ReadWrite Transaction. This is the
+    memory that is temporarily stored before writing to disk. Recorded on
+    transaction commit.
+  </summary>
+</histogram>
+
+<histogram name="WebCore.IndexedDB.Transaction.ReadWrite.SizeOnCommit2"
+    units="KB">
   <owner>dmurph@chromium.org</owner>
   <summary>
     The total temporary size of an IndexedDB ReadWrite Transaction. This is the
@@ -82594,6 +82632,21 @@
 
 <histogram name="WebCore.IndexedDB.Transaction.VersionChange.SizeOnCommit"
     units="KB">
+  <obsolete>
+    Replaced with WebCore.IndexedDB.Transaction.VersionChange.SizeOnCommit2 on
+    6/2017
+  </obsolete>
+  <owner>dmurph@chromium.org</owner>
+  <summary>
+    The total temporary size of an IndexedDB VersionChange Transaction. This is
+    the memory that is temporarily stored before writing to disk. Version change
+    transactions happen when creating a database or updating a current database
+    schema. Recorded on transaction commit.
+  </summary>
+</histogram>
+
+<histogram name="WebCore.IndexedDB.Transaction.VersionChange.SizeOnCommit2"
+    units="KB">
   <owner>dmurph@chromium.org</owner>
   <summary>
     The total temporary size of an IndexedDB VersionChange Transaction. This is
@@ -92765,6 +92818,8 @@
 </histogram_suffixes>
 
 <histogram_suffixes name="PasswordProtectionTrigger" separator=".">
+  <affected-histogram
+      name="PasswordProtection.NumberOfCachedVerdictBeforeShutdown"/>
   <suffix name="PasswordFieldOnFocus"
       label="Password protection triggered by password field on focus event."/>
   <suffix name="ProtectedPasswordEntry"
diff --git a/ui/accessibility/ax_tree.cc b/ui/accessibility/ax_tree.cc
index 03b6aeb8..43e635af 100644
--- a/ui/accessibility/ax_tree.cc
+++ b/ui/accessibility/ax_tree.cc
@@ -11,6 +11,7 @@
 #include "base/logging.h"
 #include "base/strings/stringprintf.h"
 #include "ui/accessibility/ax_node.h"
+#include "ui/gfx/transform.h"
 
 namespace ui {
 
@@ -164,6 +165,60 @@
     delegate_->OnTreeDataChanged(this, old_data, new_data);
 }
 
+gfx::RectF AXTree::RelativeToTreeBounds(const AXNode* node,
+                                        gfx::RectF bounds) const {
+  // If |bounds| is uninitialized, which is not the same as empty,
+  // start with the node bounds.
+  if (bounds.width() == 0 && bounds.height() == 0) {
+    bounds = node->data().location;
+
+    // If the node bounds is empty (either width or height is zero),
+    // try to compute good bounds from the children.
+    if (bounds.IsEmpty()) {
+      for (size_t i = 0; i < node->children().size(); i++) {
+        ui::AXNode* child = node->children()[i];
+        bounds.Union(GetTreeBounds(child));
+      }
+      if (bounds.width() > 0 && bounds.height() > 0)
+        return bounds;
+    }
+  } else {
+    bounds.Offset(node->data().location.x(), node->data().location.y());
+  }
+
+  while (node != nullptr) {
+    if (node->data().transform)
+      node->data().transform->TransformRect(&bounds);
+    auto* container = GetFromId(node->data().offset_container_id);
+    if (!container) {
+      if (node == root())
+        container = node->parent();
+      else
+        container = root();
+    }
+    if (!container || container == node)
+      break;
+
+    gfx::RectF container_bounds = container->data().location;
+    bounds.Offset(container_bounds.x(), container_bounds.y());
+
+    int scroll_x = 0;
+    int scroll_y = 0;
+    if (container->data().GetIntAttribute(ui::AX_ATTR_SCROLL_X, &scroll_x) &&
+        container->data().GetIntAttribute(ui::AX_ATTR_SCROLL_Y, &scroll_y)) {
+      bounds.Offset(-scroll_x, -scroll_y);
+    }
+
+    node = container;
+  }
+
+  return bounds;
+}
+
+gfx::RectF AXTree::GetTreeBounds(const AXNode* node) const {
+  return RelativeToTreeBounds(node, gfx::RectF());
+}
+
 bool AXTree::Unserialize(const AXTreeUpdate& update) {
   AXTreeUpdateState update_state;
   int32_t old_root_id = root_ ? root_->id() : 0;
diff --git a/ui/accessibility/ax_tree.h b/ui/accessibility/ax_tree.h
index 1448838..36fe655 100644
--- a/ui/accessibility/ax_tree.h
+++ b/ui/accessibility/ax_tree.h
@@ -172,6 +172,14 @@
 
   virtual void UpdateData(const AXTreeData& data);
 
+  // Convert any rectangle from the local coordinate space of one node in
+  // the tree, to bounds in the coordinate space of the tree.
+  gfx::RectF RelativeToTreeBounds(const AXNode* node,
+                                  gfx::RectF node_bounds) const;
+
+  // Get the bounds of a node in the coordinate space of the tree.
+  gfx::RectF GetTreeBounds(const AXNode* node) const;
+
   // Return a multi-line indented string representation, for logging.
   std::string ToString() const;
 
diff --git a/ui/accessibility/ax_tree_unittest.cc b/ui/accessibility/ax_tree_unittest.cc
index 3618332..c915a5f3 100644
--- a/ui/accessibility/ax_tree_unittest.cc
+++ b/ui/accessibility/ax_tree_unittest.cc
@@ -9,12 +9,14 @@
 
 #include <memory>
 
+#include "base/memory/ptr_util.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/stringprintf.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/accessibility/ax_node.h"
 #include "ui/accessibility/ax_serializable_tree.h"
 #include "ui/accessibility/ax_tree_serializer.h"
+#include "ui/gfx/transform.h"
 
 using base::DoubleToString;
 using base::IntToString;
@@ -34,6 +36,13 @@
   return str;
 }
 
+std::string GetBoundsAsString(const AXTree& tree, int32_t id) {
+  AXNode* node = tree.GetFromId(id);
+  gfx::RectF bounds = tree.GetTreeBounds(node);
+  return base::StringPrintf("(%.0f, %.0f) size (%.0f x %.0f)", bounds.x(),
+                            bounds.y(), bounds.width(), bounds.height());
+}
+
 class FakeAXTreeDelegate : public AXTreeDelegate {
  public:
   FakeAXTreeDelegate()
@@ -784,4 +793,113 @@
   tree.SetDelegate(NULL);
 }
 
+// Create a very simple tree and make sure that we can get the bounds of
+// any node.
+TEST(AXTreeTest, GetBoundsBasic) {
+  AXTreeUpdate tree_update;
+  tree_update.root_id = 1;
+  tree_update.nodes.resize(2);
+  tree_update.nodes[0].id = 1;
+  tree_update.nodes[0].location = gfx::RectF(0, 0, 800, 600);
+  tree_update.nodes[0].child_ids.push_back(2);
+  tree_update.nodes[1].id = 2;
+  tree_update.nodes[1].location = gfx::RectF(100, 10, 400, 300);
+  AXTree tree(tree_update);
+
+  EXPECT_EQ("(0, 0) size (800 x 600)", GetBoundsAsString(tree, 1));
+  EXPECT_EQ("(100, 10) size (400 x 300)", GetBoundsAsString(tree, 2));
+}
+
+// If a node doesn't specify its location but at least one child does have
+// a location, its computed bounds should be the union of all child bounds.
+TEST(AXTreeTest, EmptyNodeBoundsIsUnionOfChildren) {
+  AXTreeUpdate tree_update;
+  tree_update.root_id = 1;
+  tree_update.nodes.resize(4);
+  tree_update.nodes[0].id = 1;
+  tree_update.nodes[0].location = gfx::RectF(0, 0, 800, 600);
+  tree_update.nodes[0].child_ids.push_back(2);
+  tree_update.nodes[1].id = 2;
+  tree_update.nodes[1].location = gfx::RectF();  // Deliberately empty.
+  tree_update.nodes[1].child_ids.push_back(3);
+  tree_update.nodes[1].child_ids.push_back(4);
+  tree_update.nodes[2].id = 3;
+  tree_update.nodes[2].location = gfx::RectF(100, 10, 400, 20);
+  tree_update.nodes[3].id = 4;
+  tree_update.nodes[3].location = gfx::RectF(200, 30, 400, 20);
+
+  AXTree tree(tree_update);
+  EXPECT_EQ("(100, 10) size (500 x 40)", GetBoundsAsString(tree, 2));
+}
+
+// Test that getting the bounds of a node works when there's a transform.
+TEST(AXTreeTest, GetBoundsWithTransform) {
+  AXTreeUpdate tree_update;
+  tree_update.root_id = 1;
+  tree_update.nodes.resize(3);
+  tree_update.nodes[0].id = 1;
+  tree_update.nodes[0].location = gfx::RectF(0, 0, 400, 300);
+  tree_update.nodes[0].transform.reset(new gfx::Transform());
+  tree_update.nodes[0].transform->Scale(2.0, 2.0);
+  tree_update.nodes[0].child_ids.push_back(2);
+  tree_update.nodes[0].child_ids.push_back(3);
+  tree_update.nodes[1].id = 2;
+  tree_update.nodes[1].location = gfx::RectF(20, 10, 50, 5);
+  tree_update.nodes[2].id = 3;
+  tree_update.nodes[2].location = gfx::RectF(20, 30, 50, 5);
+  tree_update.nodes[2].transform.reset(new gfx::Transform());
+  tree_update.nodes[2].transform->Scale(2.0, 2.0);
+
+  AXTree tree(tree_update);
+  EXPECT_EQ("(0, 0) size (800 x 600)", GetBoundsAsString(tree, 1));
+  EXPECT_EQ("(40, 20) size (100 x 10)", GetBoundsAsString(tree, 2));
+  EXPECT_EQ("(80, 120) size (200 x 20)", GetBoundsAsString(tree, 3));
+}
+
+// Test that getting the bounds of a node that's inside a container
+// works correctly.
+TEST(AXTreeTest, GetBoundsWithContainerId) {
+  AXTreeUpdate tree_update;
+  tree_update.root_id = 1;
+  tree_update.nodes.resize(4);
+  tree_update.nodes[0].id = 1;
+  tree_update.nodes[0].location = gfx::RectF(0, 0, 800, 600);
+  tree_update.nodes[0].child_ids.push_back(2);
+  tree_update.nodes[1].id = 2;
+  tree_update.nodes[1].location = gfx::RectF(100, 50, 600, 500);
+  tree_update.nodes[1].child_ids.push_back(3);
+  tree_update.nodes[1].child_ids.push_back(4);
+  tree_update.nodes[2].id = 3;
+  tree_update.nodes[2].offset_container_id = 2;
+  tree_update.nodes[2].location = gfx::RectF(20, 30, 50, 5);
+  tree_update.nodes[3].id = 4;
+  tree_update.nodes[3].location = gfx::RectF(20, 30, 50, 5);
+
+  AXTree tree(tree_update);
+  EXPECT_EQ("(120, 80) size (50 x 5)", GetBoundsAsString(tree, 3));
+  EXPECT_EQ("(20, 30) size (50 x 5)", GetBoundsAsString(tree, 4));
+}
+
+// Test that getting the bounds of a node that's inside a scrolling container
+// works correctly.
+TEST(AXTreeTest, GetBoundsWithScrolling) {
+  AXTreeUpdate tree_update;
+  tree_update.root_id = 1;
+  tree_update.nodes.resize(3);
+  tree_update.nodes[0].id = 1;
+  tree_update.nodes[0].location = gfx::RectF(0, 0, 800, 600);
+  tree_update.nodes[0].child_ids.push_back(2);
+  tree_update.nodes[1].id = 2;
+  tree_update.nodes[1].location = gfx::RectF(100, 50, 600, 500);
+  tree_update.nodes[1].AddIntAttribute(ui::AX_ATTR_SCROLL_X, 5);
+  tree_update.nodes[1].AddIntAttribute(ui::AX_ATTR_SCROLL_Y, 10);
+  tree_update.nodes[1].child_ids.push_back(3);
+  tree_update.nodes[2].id = 3;
+  tree_update.nodes[2].offset_container_id = 2;
+  tree_update.nodes[2].location = gfx::RectF(20, 30, 50, 5);
+
+  AXTree tree(tree_update);
+  EXPECT_EQ("(115, 70) size (50 x 5)", GetBoundsAsString(tree, 3));
+}
+
 }  // namespace ui
diff --git a/ui/accessibility/platform/ax_snapshot_node_android_platform.cc b/ui/accessibility/platform/ax_snapshot_node_android_platform.cc
index af21bbdc..852090a 100644
--- a/ui/accessibility/platform/ax_snapshot_node_android_platform.cc
+++ b/ui/accessibility/platform/ax_snapshot_node_android_platform.cc
@@ -30,55 +30,6 @@
   return false;
 }
 
-gfx::Rect RelativeToAbsoluteBounds(const AXNode* node,
-                                   gfx::RectF bounds,
-                                   const AXTree* tree) {
-  const AXNode* current = node;
-  while (current != nullptr) {
-    if (current->data().transform)
-      current->data().transform->TransformRect(&bounds);
-    auto* container = tree->GetFromId(current->data().offset_container_id);
-    if (!container) {
-      if (current == tree->root())
-        container = current->parent();
-      else
-        container = tree->root();
-    }
-    if (!container || container == current)
-      break;
-
-    gfx::RectF container_bounds = container->data().location;
-    bounds.Offset(container_bounds.x(), container_bounds.y());
-    current = container;
-  }
-  return gfx::ToEnclosingRect(bounds);
-}
-
-void FixEmptyBounds(const AXNode* node, gfx::RectF* bounds, const AXTree* tree);
-
-gfx::Rect GetPageBoundsRect(const AXNode* node, const AXTree* tree) {
-  gfx::RectF bounds = node->data().location;
-  FixEmptyBounds(node, &bounds, tree);
-  return RelativeToAbsoluteBounds(node, bounds, tree);
-}
-
-void FixEmptyBounds(const AXNode* node,
-                    gfx::RectF* bounds,
-                    const AXTree* tree) {
-  if (bounds->width() > 0 && bounds->height() > 0)
-    return;
-  for (auto* child : node->children()) {
-    gfx::Rect child_bounds = GetPageBoundsRect(child, tree);
-    if (child_bounds.width() == 0 || child_bounds.height() == 0)
-      continue;
-    if (bounds->width() == 0 || bounds->height() == 0) {
-      *bounds = gfx::RectF(child_bounds);
-      continue;
-    }
-    bounds->Union(gfx::RectF(child_bounds));
-  }
-}
-
 bool HasOnlyTextChildren(const AXNode* node) {
   for (auto* child : node->children()) {
     if (!child->IsTextNode())
@@ -397,7 +348,7 @@
     gfx::RectF text_size_rect(
         0, 0, 1, node->data().GetFloatAttribute(ui::AX_ATTR_FONT_SIZE));
     gfx::Rect scaled_text_size_rect =
-        RelativeToAbsoluteBounds(node, text_size_rect, tree);
+        gfx::ToEnclosingRect(tree->RelativeToTreeBounds(node, text_size_rect));
     result->text_size = scaled_text_size_rect.height();
 
     const int text_style = node->data().GetIntAttribute(ui::AX_ATTR_TEXT_STYLE);
@@ -410,7 +361,8 @@
     result->underline = (text_style & ui::AX_TEXT_STYLE_UNDERLINE) != 0;
   }
 
-  const gfx::Rect& absolute_rect = GetPageBoundsRect(node, tree);
+  const gfx::Rect& absolute_rect =
+      gfx::ToEnclosingRect(tree->GetTreeBounds(node));
   gfx::Rect parent_relative_rect = absolute_rect;
   bool is_root = node->parent() == nullptr;
   if (!is_root) {
diff --git a/ui/app_list/app_list_constants.cc b/ui/app_list/app_list_constants.cc
index 4241f46..9633deea 100644
--- a/ui/app_list/app_list_constants.cc
+++ b/ui/app_list/app_list_constants.cc
@@ -91,7 +91,6 @@
 
 // The number of apps shown in the start page app grid.
 const size_t kNumStartPageTiles = 9;
-const size_t kNumStartPageTilesFullscreen = 5;
 
 // Maximum number of results to show in the launcher Search UI.
 const size_t kMaxSearchResults = 6;
diff --git a/ui/app_list/app_list_constants.h b/ui/app_list/app_list_constants.h
index a3927e9..7e6cb28 100644
--- a/ui/app_list/app_list_constants.h
+++ b/ui/app_list/app_list_constants.h
@@ -71,7 +71,6 @@
 APP_LIST_EXPORT extern const SkColor kIconColor;
 
 APP_LIST_EXPORT extern const size_t kNumStartPageTiles;
-APP_LIST_EXPORT extern const size_t kNumStartPageTilesFullscreen;
 APP_LIST_EXPORT extern const size_t kMaxSearchResults;
 
 APP_LIST_EXPORT extern const int kReorderDroppingCircleRadius;
diff --git a/ui/app_list/presenter/app_list_presenter_impl.cc b/ui/app_list/presenter/app_list_presenter_impl.cc
index c4ef3092..9fa5613f 100644
--- a/ui/app_list/presenter/app_list_presenter_impl.cc
+++ b/ui/app_list/presenter/app_list_presenter_impl.cc
@@ -59,10 +59,8 @@
     return;
 
   is_visible_ = true;
-  if (app_list_) {
+  if (app_list_)
     app_list_->OnTargetVisibilityChanged(GetTargetVisibility());
-    app_list_->OnVisibilityChanged(GetTargetVisibility(), display_id);
-  }
 
   if (view_) {
     ScheduleAnimation();
@@ -88,10 +86,9 @@
   DCHECK(view_);
 
   is_visible_ = false;
-  if (app_list_) {
+  if (app_list_)
     app_list_->OnTargetVisibilityChanged(GetTargetVisibility());
-    app_list_->OnVisibilityChanged(GetTargetVisibility(), GetDisplayId());
-  }
+
   // The dismissal may have occurred in response to the app list losing
   // activation. Otherwise, our widget is currently active. When the animation
   // completes we'll hide the widget, changing activation. If a menu is shown
diff --git a/ui/app_list/views/all_apps_tile_item_view.cc b/ui/app_list/views/all_apps_tile_item_view.cc
index fd21536..a2bfb8a 100644
--- a/ui/app_list/views/all_apps_tile_item_view.cc
+++ b/ui/app_list/views/all_apps_tile_item_view.cc
@@ -6,9 +6,7 @@
 
 #include "base/metrics/histogram_macros.h"
 #include "ui/app_list/app_list_constants.h"
-#include "ui/app_list/app_list_features.h"
 #include "ui/app_list/resources/grit/app_list_resources.h"
-#include "ui/app_list/views/app_list_view.h"
 #include "ui/app_list/views/contents_view.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/base/resource/resource_bundle.h"
@@ -40,9 +38,8 @@
 
 }  // namespace
 
-AllAppsTileItemView::AllAppsTileItemView(ContentsView* contents_view,
-                                         AppListView* app_list_view)
-    : contents_view_(contents_view), app_list_view_(app_list_view) {
+AllAppsTileItemView::AllAppsTileItemView(ContentsView* contents_view)
+    : contents_view_(contents_view) {
   SetTitle(l10n_util::GetStringUTF16(IDS_APP_LIST_ALL_APPS));
   SetHoverStyle(TileItemView::HOVER_STYLE_ANIMATE_SHADOW);
   UpdateIcon();
@@ -67,8 +64,6 @@
                             AppListModel::STATE_LAST);
 
   contents_view_->SetActiveState(AppListModel::STATE_APPS);
-  if (features::IsFullscreenAppListEnabled())
-    app_list_view_->SetState(AppListView::FULLSCREEN);
 }
 
 }  // namespace app_list
diff --git a/ui/app_list/views/all_apps_tile_item_view.h b/ui/app_list/views/all_apps_tile_item_view.h
index aa52351..a3cf94b 100644
--- a/ui/app_list/views/all_apps_tile_item_view.h
+++ b/ui/app_list/views/all_apps_tile_item_view.h
@@ -11,13 +11,12 @@
 
 namespace app_list {
 
-class AppListView;
 class ContentsView;
 
 // A tile item for the "All apps" button on the start page.
 class AllAppsTileItemView : public TileItemView {
  public:
-  AllAppsTileItemView(ContentsView* contents_view, AppListView* app_list_view);
+  explicit AllAppsTileItemView(ContentsView* contents_view);
 
   ~AllAppsTileItemView() override;
 
@@ -28,7 +27,6 @@
 
  private:
   ContentsView* contents_view_;
-  AppListView* const app_list_view_;  // Owned by the views hierarchy.
 
   DISALLOW_COPY_AND_ASSIGN(AllAppsTileItemView);
 };
diff --git a/ui/app_list/views/app_list_main_view.cc b/ui/app_list/views/app_list_main_view.cc
index eb060ac0..f913e11 100644
--- a/ui/app_list/views/app_list_main_view.cc
+++ b/ui/app_list/views/app_list_main_view.cc
@@ -44,13 +44,11 @@
 ////////////////////////////////////////////////////////////////////////////////
 // AppListMainView:
 
-AppListMainView::AppListMainView(AppListViewDelegate* delegate,
-                                 AppListView* app_list_view)
+AppListMainView::AppListMainView(AppListViewDelegate* delegate)
     : delegate_(delegate),
       model_(delegate->GetModel()),
       search_box_view_(nullptr),
-      contents_view_(nullptr),
-      app_list_view_(app_list_view) {
+      contents_view_(nullptr) {
   SetLayoutManager(
       features::IsAnswerCardEnabled()
           ? static_cast<views::LayoutManager*>(new views::FillLayout)
@@ -79,7 +77,8 @@
 
 void AppListMainView::AddContentsViews() {
   DCHECK(search_box_view_);
-  contents_view_ = new ContentsView(this, app_list_view_);
+
+  contents_view_ = new ContentsView(this);
   contents_view_->Init(model_);
   AddChildView(contents_view_);
 
diff --git a/ui/app_list/views/app_list_main_view.h b/ui/app_list/views/app_list_main_view.h
index 7d06cab3..937a5dd 100644
--- a/ui/app_list/views/app_list_main_view.h
+++ b/ui/app_list/views/app_list_main_view.h
@@ -21,7 +21,6 @@
 
 class AppListItem;
 class AppListModel;
-class AppListView;
 class AppListViewDelegate;
 class ApplicationDragAndDropHost;
 class ContentsView;
@@ -36,7 +35,7 @@
                                         public SearchBoxViewDelegate,
                                         public SearchResultListViewDelegate {
  public:
-  AppListMainView(AppListViewDelegate* delegate, AppListView* app_list_view);
+  explicit AppListMainView(AppListViewDelegate* delegate);
   ~AppListMainView() override;
 
   void Init(gfx::NativeView parent,
@@ -100,7 +99,6 @@
   // Created by AppListView. Owned by views hierarchy.
   SearchBoxView* search_box_view_;
   ContentsView* contents_view_;  // Owned by views hierarchy.
-  AppListView* const app_list_view_;  // Owned by views hierarchy.
 
   DISALLOW_COPY_AND_ASSIGN(AppListMainView);
 };
diff --git a/ui/app_list/views/app_list_main_view_unittest.cc b/ui/app_list/views/app_list_main_view_unittest.cc
index b365a44..5cfa290 100644
--- a/ui/app_list/views/app_list_main_view_unittest.cc
+++ b/ui/app_list/views/app_list_main_view_unittest.cc
@@ -82,7 +82,10 @@
     views::ViewsTestBase::SetUp();
     delegate_.reset(new AppListTestViewDelegate);
 
-    main_view_ = new AppListMainView(delegate_.get(), nullptr);
+    // In Ash, the third argument is a container aura::Window, but it is always
+    // NULL on Windows, and not needed for tests. It is only used to determine
+    // the scale factor for preloading icons.
+    main_view_ = new AppListMainView(delegate_.get());
     main_view_->SetPaintToLayer();
     main_view_->model()->SetFoldersEnabled(true);
     search_box_view_ = new SearchBoxView(main_view_, delegate_.get());
diff --git a/ui/app_list/views/app_list_view.cc b/ui/app_list/views/app_list_view.cc
index a38c609..060e7d7 100644
--- a/ui/app_list/views/app_list_view.cc
+++ b/ui/app_list/views/app_list_view.cc
@@ -53,33 +53,10 @@
 namespace {
 
 // The margin from the edge to the speech UI.
-constexpr int kSpeechUIMargin = 12;
-
-// The height/width of the shelf.
-constexpr int kShelfSize = 48;
-
-// The height of the peeking app list.
-constexpr int kPeekingAppListHeight = 320;
-
-// The fraction of app list height that the app list must be released at in
-// order to transition to the next state.
-constexpr int kAppListThresholdDenominator = 3;
-
-// The velocity the app list must be dragged in order to transition to the next
-// state, measured in DIPs/event.
-constexpr int kAppListDragVelocityThreshold = 25;
-
-// The opacity of the app list background.
-constexpr float kAppListOpacity = 0.8;
+const int kSpeechUIMargin = 12;
 
 // The vertical position for the appearing animation of the speech UI.
-constexpr float kSpeechUIAppearingPosition = 12;
-
-bool IsFullscreenAppListEnabled() {
-  // Cache this value to avoid repeated lookup.
-  static bool cached_value = features::IsFullscreenAppListEnabled();
-  return cached_value;
-}
+const float kSpeechUIAppearingPosition = 12;
 
 // This view forwards the focus to the search box widget by providing it as a
 // FocusTraversable when a focus search is provided.
@@ -197,16 +174,11 @@
       search_box_focus_host_(nullptr),
       search_box_widget_(nullptr),
       search_box_view_(nullptr),
-      app_list_state_(PEEKING),
-      display_observer_(this),
       overlay_view_(nullptr),
       animation_observer_(new HideViewAnimationObserver()) {
   CHECK(delegate);
 
   delegate_->GetSpeechUI()->AddObserver(this);
-
-  if (IsFullscreenAppListEnabled())
-    display_observer_.Add(display::Screen::GetScreen());
 }
 
 AppListView::~AppListView() {
@@ -223,20 +195,15 @@
   set_color(kContentsBackgroundColor);
   set_parent_window(parent);
 
-  if (IsFullscreenAppListEnabled())
+  if (features::IsFullscreenAppListEnabled())
     InitializeFullscreen(parent, initial_apps_page);
   else
     InitializeBubble(parent, initial_apps_page);
 
   InitChildWidgets();
   AddChildView(overlay_view_);
-
-  if (IsFullscreenAppListEnabled())
-    SetState(app_list_state_);
-
   if (delegate_)
     delegate_->ViewInitialized();
-
   UMA_HISTOGRAM_TIMES("Apps.AppListCreationTime",
                       base::Time::Now() - start_time);
 }
@@ -249,7 +216,7 @@
 
 void AppListView::MaybeSetAnchorPoint(const gfx::Point& anchor_point) {
   // if the AppListView is a bubble
-  if (!IsFullscreenAppListEnabled())
+  if (!features::IsFullscreenAppListEnabled())
     SetAnchorRect(gfx::Rect(anchor_point, gfx::Size()));
 }
 
@@ -262,9 +229,14 @@
   app_list_main_view_->ShowAppListWhenReady();
 }
 
+void AppListView::CloseAppList() {
+  app_list_main_view_->Close();
+  delegate_->Dismiss();
+}
+
 void AppListView::UpdateBounds() {
   // if the AppListView is a bubble
-  if (!IsFullscreenAppListEnabled())
+  if (!features::IsFullscreenAppListEnabled())
     SizeToContents();
 }
 
@@ -357,23 +329,14 @@
       FROM_HERE_WITH_EXPLICIT_FUNCTION(
           "440224, 441028 AppListView::InitContents"));
 
-  if (IsFullscreenAppListEnabled()) {
-    // The shield view that colors the background of the app list and makes it
-    // transparent.
-    app_list_background_shield_ = new views::View;
-    app_list_background_shield_->SetPaintToLayer(ui::LAYER_SOLID_COLOR);
-    app_list_background_shield_->layer()->SetColor(SK_ColorBLACK);
-    app_list_background_shield_->layer()->SetOpacity(kAppListOpacity);
-    AddChildView(app_list_background_shield_);
-  }
-  app_list_main_view_ = new AppListMainView(delegate_, this);
+  app_list_main_view_ = new AppListMainView(delegate_);
   AddChildView(app_list_main_view_);
   app_list_main_view_->SetPaintToLayer();
   app_list_main_view_->layer()->SetFillsBoundsOpaquely(false);
   app_list_main_view_->layer()->SetMasksToBounds(true);
   // This will be added to the |search_box_widget_| after the app list widget is
   // initialized.
-  search_box_view_ = new SearchBoxView(app_list_main_view_, delegate_, this);
+  search_box_view_ = new SearchBoxView(app_list_main_view_, delegate_);
   search_box_view_->SetPaintToLayer();
   search_box_view_->layer()->SetFillsBoundsOpaquely(false);
   search_box_view_->layer()->SetMasksToBounds(true);
@@ -440,29 +403,19 @@
 
 void AppListView::InitializeFullscreen(gfx::NativeView parent,
                                        int initial_apps_page) {
-  gfx::Rect display_work_area_bounds =
-      display::Screen::GetScreen()
-          ->GetDisplayNearestView(parent_window())
-          .work_area();
-
-  gfx::Rect app_list_overlay_view_bounds(
-      display_work_area_bounds.x(),
-      display_work_area_bounds.height() + kShelfSize - kPeekingAppListHeight,
-      display_work_area_bounds.width(),
-      display_work_area_bounds.height() + kShelfSize);
-
-  fullscreen_widget_ = new views::Widget;
+  views::Widget* widget = new views::Widget;
   views::Widget::InitParams app_list_overlay_view_params(
       views::Widget::InitParams::TYPE_WINDOW_FRAMELESS);
 
-  app_list_overlay_view_params.name = "AppList";
   app_list_overlay_view_params.parent = parent;
   app_list_overlay_view_params.delegate = this;
   app_list_overlay_view_params.opacity =
       views::Widget::InitParams::TRANSLUCENT_WINDOW;
-  app_list_overlay_view_params.bounds = app_list_overlay_view_bounds;
-  app_list_overlay_view_params.layer_type = ui::LAYER_SOLID_COLOR;
-  fullscreen_widget_->Init(app_list_overlay_view_params);
+  app_list_overlay_view_params.bounds =
+      display::Screen::GetScreen()->
+      GetDisplayNearestView(parent).work_area();
+  widget->Init(app_list_overlay_view_params);
+  widget->GetLayer()->SetBackgroundBlur(10);
 
   overlay_view_ = new AppListOverlayView(0 /* no corners */);
 }
@@ -473,8 +426,8 @@
   set_close_on_deactivate(false);
   set_shadow(views::BubbleBorder::NO_ASSETS);
 
-  // This creates the app list widget (Before this, child widgets cannot be
-  // created).
+  // This creates the app list widget. (Before this, child widgets cannot be
+  // created.)
   views::BubbleDialogDelegateView::CreateBubble(this);
 
   SetBubbleArrow(views::BubbleBorder::FLOAT);
@@ -489,79 +442,6 @@
   overlay_view_->SetBoundsRect(GetContentsBounds());
 }
 
-void AppListView::StartDrag(const gfx::Point& location) {
-  initial_drag_point_ = location;
-}
-
-void AppListView::UpdateDrag(const gfx::Point& location) {
-  // Update the bounds of the widget while maintaining the
-  // relative position of the top of the widget and the mouse/gesture.
-  // Block drags north of 0 and recalculate the initial_drag_point_.
-  int const new_y_position = location.y() - initial_drag_point_.y() +
-                             fullscreen_widget_->GetWindowBoundsInScreen().y();
-  gfx::Rect new_widget_bounds = fullscreen_widget_->GetWindowBoundsInScreen();
-  if (new_y_position < 0) {
-    new_widget_bounds.set_y(0);
-    initial_drag_point_ = location;
-  } else {
-    new_widget_bounds.set_y(new_y_position);
-  }
-  fullscreen_widget_->SetBounds(new_widget_bounds);
-}
-
-void AppListView::EndDrag(const gfx::Point& location) {
-  // Change the app list state based on where the drag ended. If fling velocity
-  // was over the threshold, snap to the next state in the direction of the
-  // fling.
-  int const new_y_position = location.y() - initial_drag_point_.y() +
-                             fullscreen_widget_->GetWindowBoundsInScreen().y();
-  if (std::abs(last_fling_velocity_) > kAppListDragVelocityThreshold) {
-    // If the user releases drag with velocity over the threshold, snap to
-    // the next state, ignoring the drag release position.
-    if (app_list_state_ == FULLSCREEN) {
-      if (last_fling_velocity_ > 0)
-        SetState(PEEKING);
-
-    } else {
-      SetState(last_fling_velocity_ > 0 ? CLOSED : FULLSCREEN);
-    }
-    last_fling_velocity_ = 0;
-  } else {
-    int display_height = display::Screen::GetScreen()
-                             ->GetDisplayNearestView(parent_window())
-                             .work_area()
-                             .height();
-    int default_peeking_y = display_height + kShelfSize - kPeekingAppListHeight;
-    // The drag release velocity was too low, so use the release point.
-    int app_list_snap_y =
-        (app_list_state_ == FULLSCREEN) ? 0 : default_peeking_y;
-    // The DIP delta that must be exceeded for the app list to snap to the next
-    // state.
-    int app_list_threshold =
-        (fullscreen_widget_->GetWindowBoundsInScreen().height() + kShelfSize) /
-        kAppListThresholdDenominator;
-    app_list_threshold -=
-        (app_list_state_ == FULLSCREEN ? 0 : kPeekingAppListHeight) /
-        kAppListThresholdDenominator;
-
-    // If the user releases +/- 1/3 of |app_list_threshold|, snap to the
-    // next state.
-    if (std::abs(app_list_snap_y - new_y_position) < app_list_threshold) {
-      // The drag was not far enough so set the app list bounds to the target
-      // bounds for the current state.
-      SetState(app_list_state_);
-    } else if ((app_list_snap_y + app_list_threshold) < new_y_position) {
-      // The drag was far enough to change states and was a downward drag, so
-      // set the app list bounds to the next state.
-      SetState(app_list_state_ == FULLSCREEN ? PEEKING : CLOSED);
-    } else {
-      // The drag was far enough to change states and was an upward drag, so
-      // set the app list bounds to the next state.
-      SetState(FULLSCREEN);
-    }
-  }
-}
-
 void AppListView::OnBeforeBubbleWidgetInit(views::Widget::InitParams* params,
                                            views::Widget* widget) const {
   if (!params->native_widget) {
@@ -595,64 +475,14 @@
   mask->addRect(gfx::RectToSkRect(GetBubbleFrameView()->GetContentsBounds()));
 }
 
-void AppListView::OnMouseEvent(ui::MouseEvent* event) {
-  if (!IsFullscreenAppListEnabled())
-    return;
-
-  switch (event->type()) {
-    case ui::ET_MOUSE_PRESSED:
-      StartDrag(event->location());
-      event->SetHandled();
-      break;
-    case ui::ET_MOUSE_DRAGGED:
-      UpdateDrag(event->location());
-      event->SetHandled();
-      break;
-    case ui::ET_MOUSE_RELEASED:
-      EndDrag(event->location());
-      event->SetHandled();
-      break;
-    default:
-      break;
-  }
-}
-
-void AppListView::OnGestureEvent(ui::GestureEvent* event) {
-  if (!IsFullscreenAppListEnabled())
-    return;
-
-  switch (event->type()) {
-    case ui::ET_GESTURE_SCROLL_BEGIN:
-      StartDrag(event->location());
-      event->SetHandled();
-      break;
-    case ui::ET_GESTURE_SCROLL_UPDATE:
-      last_fling_velocity_ = event->details().velocity_y();
-      UpdateDrag(event->location());
-      event->SetHandled();
-      break;
-    case ui::ET_GESTURE_END:
-      EndDrag(event->location());
-      event->SetHandled();
-      break;
-    default:
-      break;
-  }
-}
-
 bool AppListView::AcceleratorPressed(const ui::Accelerator& accelerator) {
   DCHECK_EQ(ui::VKEY_ESCAPE, accelerator.key_code());
 
   // If the ContentsView does not handle the back action, then this is the
   // top level, so we close the app list.
   if (!app_list_main_view_->contents_view()->Back()) {
-    if (IsFullscreenAppListEnabled()) {
-      SetState(CLOSED);
-    } else {
-      app_list_main_view_->Close();
-      delegate_->Dismiss();
-    }
     GetWidget()->Deactivate();
+    CloseAppList();
   }
 
   // Don't let DialogClientView handle the accelerator.
@@ -680,11 +510,6 @@
     speech_bounds.Inset(-speech_view_->GetInsets());
     speech_view_->SetBoundsRect(speech_bounds);
   }
-
-  if (IsFullscreenAppListEnabled()) {
-    app_list_main_view_->contents_view()->Layout();
-    app_list_background_shield_->SetBoundsRect(contents_bounds);
-  }
 }
 
 void AppListView::SchedulePaintInRect(const gfx::Rect& rect) {
@@ -693,31 +518,6 @@
     GetBubbleFrameView()->SchedulePaint();
 }
 
-void AppListView::SetState(AppListState new_state) {
-  gfx::Rect new_widget_bounds = fullscreen_widget_->GetWindowBoundsInScreen();
-  switch (new_state) {
-    case PEEKING: {
-      int display_height = display::Screen::GetScreen()
-                               ->GetDisplayNearestView(parent_window())
-                               .work_area()
-                               .bottom();
-      int default_peeking_y =
-          display_height + kShelfSize - kPeekingAppListHeight;
-      new_widget_bounds.set_y(default_peeking_y);
-      break;
-    }
-    case FULLSCREEN:
-      new_widget_bounds.set_y(0);
-      break;
-    case CLOSED:
-      app_list_main_view_->Close();
-      delegate_->Dismiss();
-      break;
-  }
-  fullscreen_widget_->SetBounds(new_widget_bounds);
-  app_list_state_ = new_state;
-}
-
 void AppListView::OnWidgetDestroying(views::Widget* widget) {
   BubbleDialogDelegateView::OnWidgetDestroying(widget);
   if (delegate_ && widget == GetWidget())
@@ -802,21 +602,4 @@
   }
 }
 
-void AppListView::OnDisplayMetricsChanged(const display::Display& display,
-                                          uint32_t changed_metrics) {
-  if (!IsFullscreenAppListEnabled())
-    return;
-
-  // Set the |fullscreen_widget_| size to fit the new display metrics.
-  gfx::Size size = display::Screen::GetScreen()
-                       ->GetDisplayNearestView(parent_window())
-                       .work_area()
-                       .size();
-  size.Enlarge(0, kShelfSize);
-  fullscreen_widget_->SetSize(size);
-
-  // Update the |fullscreen_widget_| bounds to accomodate the new work area.
-  SetState(app_list_state_);
-}
-
 }  // namespace app_list
diff --git a/ui/app_list/views/app_list_view.h b/ui/app_list/views/app_list_view.h
index 84b0ead..3b657141 100644
--- a/ui/app_list/views/app_list_view.h
+++ b/ui/app_list/views/app_list_view.h
@@ -9,18 +9,12 @@
 
 #include "base/callback.h"
 #include "base/macros.h"
-#include "base/scoped_observer.h"
 #include "build/build_config.h"
 #include "ui/app_list/app_list_export.h"
 #include "ui/app_list/speech_ui_model_observer.h"
-#include "ui/display/display_observer.h"
 #include "ui/views/bubble/bubble_dialog_delegate.h"
 #include "ui/views/widget/widget.h"
 
-namespace display {
-class Screen;
-}
-
 namespace app_list {
 class ApplicationDragAndDropHost;
 class AppListMainView;
@@ -38,28 +32,15 @@
 // AppListView is the top-level view and controller of app list UI. It creates
 // and hosts a AppsGridView and passes AppListModel to it for display.
 class APP_LIST_EXPORT AppListView : public views::BubbleDialogDelegateView,
-                                    public SpeechUIModelObserver,
-                                    public display::DisplayObserver {
+                                    public SpeechUIModelObserver {
  public:
-  enum AppListState {
-    // If set, closes |app_list_main_view_| and dismisses the delegate.
-    CLOSED = 0,
-
-    // The initial state for the app list. If set, the widget will peek over
-    // the shelf by kPeekingAppListHeight DIPs.
-    PEEKING,
-
-    // If set, the widget will be repositioned to take up the whole screen.
-    FULLSCREEN,
-  };
-
   // Does not take ownership of |delegate|.
   explicit AppListView(AppListViewDelegate* delegate);
   ~AppListView() override;
 
-  // Initializes the widget as a bubble or fullscreen view depending on if the
-  // fullscreen app list feature is set. |parent| and |initial_apps_page| are
-  // used for both the bubble and fullscreen initialization.
+  // Initializes the widget as a bubble or fullscreen view depending on the
+  // presence of a cmd line switch. parent and initial_apps_page are used for
+  // both the bubble and fullscreen initialization.
   void Initialize(gfx::NativeView parent, int initial_apps_page);
 
   void SetBubbleArrow(views::BubbleBorder::Arrow arrow);
@@ -77,6 +58,8 @@
   // timer to show the UI when a maximum allowed wait time has expired.
   void ShowWhenReady();
 
+  void CloseAppList();
+
   void UpdateBounds();
 
   // Enables/disables a semi-transparent overlay over the app list (good for
@@ -106,12 +89,6 @@
   void Layout() override;
   void SchedulePaintInRect(const gfx::Rect& rect) override;
 
-  // Changes the app list state.
-  void SetState(AppListState new_state);
-
-  bool is_fullscreen() const { return app_list_state_ == FULLSCREEN; }
-  AppListState app_list_state() const { return app_list_state_; }
-
  private:
   friend class test::AppListViewTestApi;
 
@@ -125,17 +102,6 @@
   // Initializes the widget as a bubble.
   void InitializeBubble(gfx::NativeView parent, int initial_apps_page);
 
-  // Initializes |initial_drag_point_|.
-  void StartDrag(const gfx::Point& location);
-
-  // Updates the bounds of the widget while maintaining the relative position
-  // of the top of the widget and the gesture.
-  void UpdateDrag(const gfx::Point& location);
-
-  // Handles app list state transfers. If the drag was fast enough, ignore the
-  // release position and snap to the next state.
-  void EndDrag(const gfx::Point& location);
-
   // Overridden from views::BubbleDialogDelegateView:
   void OnBeforeBubbleWidgetInit(views::Widget::InitParams* params,
                                 views::Widget* widget) const override;
@@ -146,10 +112,6 @@
   bool WidgetHasHitTestMask() const override;
   void GetWidgetHitTestMask(gfx::Path* mask) const override;
 
-  // Overridden from ui::EventHandler:
-  void OnMouseEvent(ui::MouseEvent* event) override;
-  void OnGestureEvent(ui::GestureEvent* event) override;
-
   // Overridden from views::WidgetObserver:
   void OnWidgetDestroying(views::Widget* widget) override;
   void OnWidgetVisibilityChanged(views::Widget* widget, bool visible) override;
@@ -158,31 +120,14 @@
   void OnSpeechRecognitionStateChanged(
       SpeechRecognitionState new_state) override;
 
-  // Overridden from DisplayObserver:
-  void OnDisplayMetricsChanged(const display::Display& display,
-                               uint32_t changed_metrics) override;
-
   AppListViewDelegate* delegate_;  // Weak. Owned by AppListService.
 
   AppListMainView* app_list_main_view_;
   SpeechView* speech_view_;
-  views::Widget* fullscreen_widget_ = nullptr;  // Owned by AppListView.
 
   views::View* search_box_focus_host_;  // Owned by the views hierarchy.
   views::Widget* search_box_widget_;    // Owned by the app list's widget.
   SearchBoxView* search_box_view_;      // Owned by |search_box_widget_|.
-  // Owned by the app list's widget. Null if the fullscreen app list is not
-  // enabled.
-  views::View* app_list_background_shield_ = nullptr;
-  // The gap between the initial gesture event and the top of the window.
-  gfx::Point initial_drag_point_;
-  // The velocity of the gesture event.
-  float last_fling_velocity_ = 0;
-  // The state of the app list, controlled via SetState().
-  AppListState app_list_state_;
-
-  // An observer that notifies AppListView when the display has changed.
-  ScopedObserver<display::Screen, display::DisplayObserver> display_observer_;
 
   // A semi-transparent white overlay that covers the app list while dialogs are
   // open.
diff --git a/ui/app_list/views/contents_view.cc b/ui/app_list/views/contents_view.cc
index ff96d24..9a3a73b7 100644
--- a/ui/app_list/views/contents_view.cc
+++ b/ui/app_list/views/contents_view.cc
@@ -29,15 +29,13 @@
 
 namespace app_list {
 
-ContentsView::ContentsView(AppListMainView* app_list_main_view,
-                           AppListView* app_list_view)
+ContentsView::ContentsView(AppListMainView* app_list_main_view)
     : model_(nullptr),
       apps_container_view_(nullptr),
       search_results_page_view_(nullptr),
       start_page_view_(nullptr),
       custom_page_view_(nullptr),
       app_list_main_view_(app_list_main_view),
-      app_list_view_(app_list_view),
       page_before_search_(0) {
   pagination_model_.SetTransitionDurations(kPageTransitionDurationInMs,
                                            kOverscrollPageTransitionDurationMs);
@@ -66,8 +64,7 @@
   }
 
   // Start page.
-  start_page_view_ =
-      new StartPageView(app_list_main_view_, view_delegate, app_list_view_);
+  start_page_view_ = new StartPageView(app_list_main_view_, view_delegate);
   AddLauncherPage(start_page_view_, AppListModel::STATE_START);
 
   // Search results UI.
diff --git a/ui/app_list/views/contents_view.h b/ui/app_list/views/contents_view.h
index a27300fe..62a52cd2 100644
--- a/ui/app_list/views/contents_view.h
+++ b/ui/app_list/views/contents_view.h
@@ -26,7 +26,6 @@
 
 class AppsGridView;
 class AppListPage;
-class AppListView;
 class ApplicationDragAndDropHost;
 class AppListFolderItem;
 class AppListMainView;
@@ -45,7 +44,7 @@
 class APP_LIST_EXPORT ContentsView : public views::View,
                                      public PaginationModelObserver {
  public:
-  ContentsView(AppListMainView* app_list_main_view, AppListView* app_list_view);
+  explicit ContentsView(AppListMainView* app_list_main_view);
   ~ContentsView() override;
 
   // Initialize the pages of the launcher. Should be called after
@@ -192,9 +191,6 @@
   // Parent view. Owned by the views hierarchy.
   AppListMainView* app_list_main_view_;
 
-  // Owned by the views hierarchy.
-  AppListView* const app_list_view_;
-
   // Maps State onto |view_model_| indices.
   std::map<AppListModel::State, int> state_to_view_;
 
diff --git a/ui/app_list/views/search_box_view.cc b/ui/app_list/views/search_box_view.cc
index 599f298e8..0d8aec2 100644
--- a/ui/app_list/views/search_box_view.cc
+++ b/ui/app_list/views/search_box_view.cc
@@ -10,14 +10,12 @@
 #include "base/memory/ptr_util.h"
 #include "build/build_config.h"
 #include "ui/app_list/app_list_constants.h"
-#include "ui/app_list/app_list_features.h"
 #include "ui/app_list/app_list_model.h"
 #include "ui/app_list/app_list_switches.h"
 #include "ui/app_list/app_list_view_delegate.h"
 #include "ui/app_list/resources/grit/app_list_resources.h"
 #include "ui/app_list/search_box_model.h"
 #include "ui/app_list/speech_ui_model.h"
-#include "ui/app_list/views/app_list_view.h"
 #include "ui/app_list/views/contents_view.h"
 #include "ui/app_list/views/search_box_view_delegate.h"
 #include "ui/base/ime/text_input_flags.h"
@@ -36,7 +34,6 @@
 #include "ui/views/layout/box_layout.h"
 #include "ui/views/layout/fill_layout.h"
 #include "ui/views/shadow_border.h"
-#include "ui/views/widget/widget.h"
 
 namespace app_list {
 
@@ -45,28 +42,16 @@
 const int kPadding = 16;
 const int kInnerPadding = 24;
 const int kPreferredWidth = 360;
-const int kPreferredWidthFullscreen = 544;
 const int kPreferredHeight = 48;
 
 const SkColor kHintTextColor = SkColorSetRGB(0xA0, 0xA0, 0xA0);
 
 const int kBackgroundBorderCornerRadius = 2;
-const int kBackgroundBorderCornerRadiusFullscreen = 20;
-
-bool IsFullscreenAppListEnabled() {
-  // Cache this value to avoid repeated lookup.
-  static bool cached_value = features::IsFullscreenAppListEnabled();
-  return cached_value;
-}
 
 // A background that paints a solid white rounded rect with a thin grey border.
 class SearchBoxBackground : public views::Background {
  public:
-  SearchBoxBackground()
-      : background_border_corner_radius_(
-            IsFullscreenAppListEnabled()
-                ? kBackgroundBorderCornerRadiusFullscreen
-                : kBackgroundBorderCornerRadius) {}
+  SearchBoxBackground() {}
   ~SearchBoxBackground() override {}
 
  private:
@@ -77,11 +62,9 @@
     cc::PaintFlags flags;
     flags.setAntiAlias(true);
     flags.setColor(kSearchBoxBackground);
-    canvas->DrawRoundRect(bounds, background_border_corner_radius_, flags);
+    canvas->DrawRoundRect(bounds, kBackgroundBorderCornerRadius, flags);
   }
 
-  const int background_border_corner_radius_;
-
   DISALLOW_COPY_AND_ASSIGN(SearchBoxBackground);
 };
 
@@ -131,8 +114,7 @@
 };
 
 SearchBoxView::SearchBoxView(SearchBoxViewDelegate* delegate,
-                             AppListViewDelegate* view_delegate,
-                             AppListView* app_list_view)
+                             AppListViewDelegate* view_delegate)
     : delegate_(delegate),
       view_delegate_(view_delegate),
       model_(NULL),
@@ -141,13 +123,9 @@
       speech_button_(NULL),
       search_box_(new views::Textfield),
       contents_view_(NULL),
-      app_list_view_(app_list_view),
       focused_view_(FOCUS_SEARCH_BOX) {
   SetLayoutManager(new views::FillLayout);
-  SetPreferredSize(gfx::Size(IsFullscreenAppListEnabled()
-                                 ? kPreferredWidthFullscreen
-                                 : kPreferredWidth,
-                             kPreferredHeight));
+  SetPreferredSize(gfx::Size(kPreferredWidth, kPreferredHeight));
   AddChildView(content_container_);
 
   SetShadow(GetShadowForZHeight(2));
@@ -345,11 +323,6 @@
   UpdateModel();
   view_delegate_->AutoLaunchCanceled();
   NotifyQueryChanged();
-
-  if (IsFullscreenAppListEnabled() && !app_list_view_->is_fullscreen()) {
-    // If the app list is in the peeking state, switch it to fullscreen.
-    app_list_view_->SetState(AppListView::FULLSCREEN);
-  }
 }
 
 bool SearchBoxView::HandleKeyEvent(views::Textfield* sender,
diff --git a/ui/app_list/views/search_box_view.h b/ui/app_list/views/search_box_view.h
index db647e9..8b5790bc 100644
--- a/ui/app_list/views/search_box_view.h
+++ b/ui/app_list/views/search_box_view.h
@@ -31,7 +31,6 @@
 };
 
 class AppListModel;
-class AppListView;
 class AppListViewDelegate;
 class SearchBoxModel;
 class SearchBoxViewDelegate;
@@ -48,8 +47,7 @@
                                       public SpeechUIModelObserver {
  public:
   SearchBoxView(SearchBoxViewDelegate* delegate,
-                AppListViewDelegate* view_delegate,
-                AppListView* app_list_view = nullptr);
+                AppListViewDelegate* view_delegate);
   ~SearchBoxView() override;
 
   void ModelChanged();
@@ -120,7 +118,6 @@
   SearchBoxImageButton* speech_button_;  // Owned by views hierarchy.
   views::Textfield* search_box_;  // Owned by views hierarchy.
   views::View* contents_view_;  // Owned by views hierarchy.
-  app_list::AppListView* app_list_view_;  // Owned by views hierarchy.
 
   SearchBoxFocus focused_view_;  // Which element has TAB'd focus.
 
diff --git a/ui/app_list/views/start_page_view.cc b/ui/app_list/views/start_page_view.cc
index c9166f3..27dce46 100644
--- a/ui/app_list/views/start_page_view.cc
+++ b/ui/app_list/views/start_page_view.cc
@@ -13,10 +13,8 @@
 #include "base/strings/utf_string_conversions.h"
 #include "ui/accessibility/ax_node_data.h"
 #include "ui/app_list/app_list_constants.h"
-#include "ui/app_list/app_list_features.h"
 #include "ui/app_list/app_list_item.h"
 #include "ui/app_list/app_list_model.h"
-#include "ui/app_list/app_list_switches.h"
 #include "ui/app_list/app_list_view_delegate.h"
 #include "ui/app_list/search_result.h"
 #include "ui/app_list/views/all_apps_tile_item_view.h"
@@ -54,7 +52,6 @@
 constexpr int kTileSpacing = 7;
 constexpr int kNumStartPageTilesCols = 5;
 constexpr int kTilesHorizontalMarginLeft = 145;
-constexpr int kCenterColumnOfStartPageAppGrid = 3;
 
 constexpr int kLauncherPageBackgroundWidth = 400;
 
@@ -140,10 +137,7 @@
   SetBackground(views::CreateSolidBackground(kLabelBackgroundColor));
   all_apps_button_->SetHoverStyle(TileItemView::HOVER_STYLE_ANIMATE_SHADOW);
   all_apps_button_->SetParentBackgroundColor(kLabelBackgroundColor);
-
-  CreateAppsGrid(features::IsFullscreenAppListEnabled()
-                     ? kNumStartPageTilesFullscreen
-                     : kNumStartPageTiles);
+  CreateAppsGrid(kNumStartPageTiles);
 }
 
 StartPageView::StartPageTilesContainer::~StartPageTilesContainer() {
@@ -177,10 +171,7 @@
       delete search_result_tile_views_[i];
     search_result_tile_views_.clear();
     RemoveChildView(all_apps_button_);
-
-    CreateAppsGrid(features::IsFullscreenAppListEnabled()
-                       ? kNumStartPageTilesFullscreen
-                       : std::min(kNumStartPageTiles, display_results.size()));
+    CreateAppsGrid(std::min(kNumStartPageTiles, display_results.size()));
   }
 
   // Update the tile item results.
@@ -251,19 +242,10 @@
     search_result_tile_views_.emplace_back(tile_item);
   }
 
+  // Also add a special "all apps" button to the end of the container.
   all_apps_button_->UpdateIcon();
-  if (features::IsFullscreenAppListEnabled()) {
-    // Also add a special "all apps" button to the middle of the next row of the
-    // container.
+  if (i % kNumStartPageTilesCols == 0)
     tiles_layout_manager->StartRow(0, 0);
-    tiles_layout_manager->SkipColumns(kCenterColumnOfStartPageAppGrid);
-  } else {
-    // Also add a special "all apps" button to the end of the next row of the
-    // container.
-    if (i % kNumStartPageTilesCols == 0)
-      tiles_layout_manager->StartRow(0, 0);
-  }
-
   tiles_layout_manager->AddView(all_apps_button_);
   AddChildView(all_apps_button_);
 }
@@ -271,8 +253,7 @@
 ////////////////////////////////////////////////////////////////////////////////
 // StartPageView implementation:
 StartPageView::StartPageView(AppListMainView* app_list_main_view,
-                             AppListViewDelegate* view_delegate,
-                             AppListView* app_list_view)
+                             AppListViewDelegate* view_delegate)
     : app_list_main_view_(app_list_main_view),
       view_delegate_(view_delegate),
       search_box_spacer_view_(new View()),
@@ -281,8 +262,7 @@
           view_delegate_->GetModel()->custom_launcher_page_name())),
       tiles_container_(new StartPageTilesContainer(
           app_list_main_view->contents_view(),
-          new AllAppsTileItemView(app_list_main_view_->contents_view(),
-                                  app_list_view),
+          new AllAppsTileItemView(app_list_main_view_->contents_view()),
           view_delegate)) {
   search_box_spacer_view_->SetPreferredSize(gfx::Size(
       kStartPageSearchBoxWidth,
@@ -294,6 +274,7 @@
 
   // The view containing the start page tiles.
   AddChildView(tiles_container_);
+
   AddChildView(custom_launcher_page_background_);
 
   tiles_container_->SetResults(view_delegate_->GetModel()->results());
@@ -313,16 +294,11 @@
       views::BoxLayout::CROSS_AXIS_ALIGNMENT_CENTER);
   instant_container_->SetLayoutManager(instant_layout_manager);
 
-  // Create the view for the Google Doodle if the fullscreen launcher is not
-  // enabled.
-  if (!features::IsFullscreenAppListEnabled()) {
-    views::View* web_view = view_delegate_->CreateStartPageWebView(
-        gfx::Size(kWebViewWidth, kWebViewHeight));
-
-    if (web_view) {
-      web_view->SetFocusBehavior(FocusBehavior::NEVER);
-      instant_container_->AddChildView(web_view);
-    }
+  views::View* web_view = view_delegate_->CreateStartPageWebView(
+      gfx::Size(kWebViewWidth, kWebViewHeight));
+  if (web_view) {
+    web_view->SetFocusBehavior(FocusBehavior::NEVER);
+    instant_container_->AddChildView(web_view);
   }
 
   instant_container_->AddChildView(search_box_spacer_view_);
diff --git a/ui/app_list/views/start_page_view.h b/ui/app_list/views/start_page_view.h
index ef68aa0d..28f9c2e 100644
--- a/ui/app_list/views/start_page_view.h
+++ b/ui/app_list/views/start_page_view.h
@@ -16,7 +16,6 @@
 namespace app_list {
 
 class AppListMainView;
-class AppListView;
 class AppListViewDelegate;
 class CustomLauncherPageBackgroundView;
 class SearchResultTileItemView;
@@ -26,8 +25,7 @@
 class APP_LIST_EXPORT StartPageView : public AppListPage {
  public:
   StartPageView(AppListMainView* app_list_main_view,
-                AppListViewDelegate* view_delegate,
-                AppListView* app_list_view);
+                AppListViewDelegate* view_delegate);
   ~StartPageView() override;
 
   void Reset();
@@ -52,11 +50,11 @@
   void OnGestureEvent(ui::GestureEvent* event) override;
   void OnScrollEvent(ui::ScrollEvent* event) override;
 
+
  private:
   class StartPageTilesContainer;
 
   void InitInstantContainer();
-
   void MaybeOpenCustomLauncherPage();
 
   void SetCustomLauncherPageSelected(bool selected);