diff --git a/DEPS b/DEPS
index cc5f825..776f9a86 100644
--- a/DEPS
+++ b/DEPS
@@ -48,7 +48,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling swarming_client
   # and whatever else without interference from each other.
-  'swarming_revision': 'a941a089ff1000403078b74cb628eb430f07d271',
+  'swarming_revision': '5c4eed8883548ba78c886ef26986b81b1be723a4',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling ANGLE
   # and whatever else without interference from each other.
@@ -64,7 +64,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling PDFium
   # and whatever else without interference from each other.
-  'pdfium_revision': '1bbcb35e4e5593998837c832eabf16a91a695387',
+  'pdfium_revision': 'fb9c11b49ee4fe6c18703d661dcaee498085c4c5',
   # 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': '239f08ec63f401050f448ae3ca4cba48606682cf',
+  'catapult_revision': '95d73385835dd1347cacacd1e25efa48938ee90a',
   # 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/ash/BUILD.gn b/ash/BUILD.gn
index 1641768..1d7c3e6a 100644
--- a/ash/BUILD.gn
+++ b/ash/BUILD.gn
@@ -1172,6 +1172,8 @@
     "laser/laser_pointer_points_unittest.cc",
     "laser/laser_segment_utils_unittest.cc",
     "login/lock_screen_controller_unittest.cc",
+    "login/mock_lock_screen_client.cc",
+    "login/mock_lock_screen_client.h",
     "metrics/desktop_task_switch_metric_recorder_unittest.cc",
     "metrics/pointer_metrics_recorder_unittest.cc",
     "metrics/task_switch_metrics_recorder_unittest.cc",
diff --git a/ash/accelerators/accelerator_controller.cc b/ash/accelerators/accelerator_controller.cc
index 0e592a0..9ca596b 100644
--- a/ash/accelerators/accelerator_controller.cc
+++ b/ash/accelerators/accelerator_controller.cc
@@ -45,7 +45,6 @@
 #include "ash/wm/window_state.h"
 #include "ash/wm/window_util.h"
 #include "ash/wm/wm_event.h"
-#include "ash/wm_window.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/metrics/user_metrics.h"
 #include "base/strings/string_split.h"
@@ -424,10 +423,10 @@
 }
 
 bool CanHandleWindowSnap() {
-  WmWindow* active_window = WmWindow::Get(wm::GetActiveWindow());
+  aura::Window* active_window = wm::GetActiveWindow();
   if (!active_window)
     return false;
-  wm::WindowState* window_state = active_window->GetWindowState();
+  wm::WindowState* window_state = wm::GetWindowState(active_window);
   // Disable window snapping shortcut key for full screen window due to
   // http://crbug.com/135487.
   return (window_state && window_state->IsUserPositionable() &&
@@ -443,9 +442,9 @@
   const wm::WMEvent event(action == WINDOW_CYCLE_SNAP_LEFT
                               ? wm::WM_EVENT_CYCLE_SNAP_LEFT
                               : wm::WM_EVENT_CYCLE_SNAP_RIGHT);
-  WmWindow* active_window = WmWindow::Get(wm::GetActiveWindow());
+  aura::Window* active_window = wm::GetActiveWindow();
   DCHECK(active_window);
-  active_window->GetWindowState()->OnWMEvent(&event);
+  wm::GetWindowState(active_window)->OnWMEvent(&event);
 }
 
 void HandleWindowMinimize() {
diff --git a/ash/accelerators/accelerator_delegate.cc b/ash/accelerators/accelerator_delegate.cc
index d2c252fb..762ed102 100644
--- a/ash/accelerators/accelerator_delegate.cc
+++ b/ash/accelerators/accelerator_delegate.cc
@@ -5,7 +5,6 @@
 #include "ash/accelerators/accelerator_delegate.h"
 
 #include "ash/accelerators/accelerator_router.h"
-#include "ash/wm_window.h"
 #include "ui/aura/window.h"
 #include "ui/events/event.h"
 
@@ -19,8 +18,7 @@
     const ui::KeyEvent& key_event,
     const ui::Accelerator& accelerator) {
   return router_->ProcessAccelerator(
-      WmWindow::Get(static_cast<aura::Window*>(key_event.target())), key_event,
-      accelerator);
+      static_cast<aura::Window*>(key_event.target()), key_event, accelerator);
 }
 
 }  // namespace ash
diff --git a/ash/accelerators/accelerator_router.cc b/ash/accelerators/accelerator_router.cc
index 553bd24..20e67d7 100644
--- a/ash/accelerators/accelerator_router.cc
+++ b/ash/accelerators/accelerator_router.cc
@@ -8,11 +8,12 @@
 #include "ash/shell.h"
 #include "ash/shell_port.h"
 #include "ash/wm/window_state.h"
-#include "ash/wm_window.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/stl_util.h"
+#include "ui/aura/window.h"
 #include "ui/base/accelerators/accelerator.h"
 #include "ui/events/event.h"
+#include "ui/wm/core/window_util.h"
 
 namespace ash {
 
@@ -43,7 +44,7 @@
 
 AcceleratorRouter::~AcceleratorRouter() {}
 
-bool AcceleratorRouter::ProcessAccelerator(WmWindow* target,
+bool AcceleratorRouter::ProcessAccelerator(aura::Window* target,
                                            const ui::KeyEvent& key_event,
                                            const ui::Accelerator& accelerator) {
   // Callers should never supply null.
@@ -85,16 +86,16 @@
   }
 }
 
-bool AcceleratorRouter::CanConsumeSystemKeys(WmWindow* target,
+bool AcceleratorRouter::CanConsumeSystemKeys(aura::Window* target,
                                              const ui::KeyEvent& event) {
   // Uses the top level window so if the target is a web contents window the
   // containing parent window will be checked for the property.
-  WmWindow* top_level = target->GetToplevelWindowForFocus();
-  return top_level && top_level->GetWindowState()->can_consume_system_keys();
+  aura::Window* top_level = ::wm::GetToplevelWindow(target);
+  return top_level && wm::GetWindowState(top_level)->can_consume_system_keys();
 }
 
 bool AcceleratorRouter::ShouldProcessAcceleratorNow(
-    WmWindow* target,
+    aura::Window* target,
     const ui::KeyEvent& event,
     const ui::Accelerator& accelerator) {
   // Callers should never supply null.
@@ -104,7 +105,7 @@
   if (accelerator.IsCmdDown())
     return true;
 
-  if (base::ContainsValue(ShellPort::Get()->GetAllRootWindows(), target))
+  if (base::ContainsValue(Shell::GetAllRootWindows(), target))
     return true;
 
   AcceleratorController* accelerator_controller =
@@ -116,8 +117,8 @@
 
   // A full screen window has a right to handle all key events including the
   // reserved ones.
-  WmWindow* top_level = target->GetToplevelWindowForFocus();
-  if (top_level && top_level->GetWindowState()->IsFullscreen()) {
+  aura::Window* top_level = ::wm::GetToplevelWindow(target);
+  if (top_level && wm::GetWindowState(top_level)->IsFullscreen()) {
     // On ChromeOS, fullscreen windows are either browser or apps, which
     // send key events to a web content first, then will process keys
     // if the web content didn't consume them.
diff --git a/ash/accelerators/accelerator_router.h b/ash/accelerators/accelerator_router.h
index a849d5a..9734811c 100644
--- a/ash/accelerators/accelerator_router.h
+++ b/ash/accelerators/accelerator_router.h
@@ -9,6 +9,10 @@
 #include "base/macros.h"
 #include "base/time/time.h"
 
+namespace aura {
+class Window;
+}
+
 namespace ui {
 class Accelerator;
 class KeyEvent;
@@ -16,8 +20,6 @@
 
 namespace ash {
 
-class WmWindow;
-
 // AcceleratorRouter does a minimal amount of processing before routing the
 // accelerator to the AcceleratorController. AcceleratorRouter may also decide
 // not to process certain accelerators.
@@ -28,17 +30,17 @@
 
   // Returns true if event should be consumed. |target| is the target of the
   // event.
-  bool ProcessAccelerator(WmWindow* target,
+  bool ProcessAccelerator(aura::Window* target,
                           const ui::KeyEvent& event,
                           const ui::Accelerator& accelerator);
 
  private:
   // Returns true if the window should be allowed a chance to handle
   // system keys.
-  bool CanConsumeSystemKeys(WmWindow* target, const ui::KeyEvent& event);
+  bool CanConsumeSystemKeys(aura::Window* target, const ui::KeyEvent& event);
 
   // Returns true if the |accelerator| should be processed now.
-  bool ShouldProcessAcceleratorNow(WmWindow* target,
+  bool ShouldProcessAcceleratorNow(aura::Window* target,
                                    const ui::KeyEvent& event,
                                    const ui::Accelerator& accelerator);
 
diff --git a/ash/aura/shell_port_classic.cc b/ash/aura/shell_port_classic.cc
index e1b66fa..d27eb274 100644
--- a/ash/aura/shell_port_classic.cc
+++ b/ash/aura/shell_port_classic.cc
@@ -112,11 +112,11 @@
          display::DisplayManager::UNIFIED;
 }
 
-void ShellPortClassic::SetDisplayWorkAreaInsets(WmWindow* window,
+void ShellPortClassic::SetDisplayWorkAreaInsets(aura::Window* window,
                                                 const gfx::Insets& insets) {
   Shell::Get()
       ->window_tree_host_manager()
-      ->UpdateWorkAreaOfDisplayNearestWindow(window->aura_window(), insets);
+      ->UpdateWorkAreaOfDisplayNearestWindow(window, insets);
 }
 
 std::unique_ptr<display::TouchTransformSetter>
@@ -191,7 +191,7 @@
 }
 
 std::unique_ptr<WorkspaceEventHandler>
-ShellPortClassic::CreateWorkspaceEventHandler(WmWindow* workspace_window) {
+ShellPortClassic::CreateWorkspaceEventHandler(aura::Window* workspace_window) {
   return base::MakeUnique<WorkspaceEventHandlerAura>(workspace_window);
 }
 
diff --git a/ash/aura/shell_port_classic.h b/ash/aura/shell_port_classic.h
index f2e02a15..2143ee8d 100644
--- a/ash/aura/shell_port_classic.h
+++ b/ash/aura/shell_port_classic.h
@@ -43,7 +43,7 @@
   display::Display GetFirstDisplay() const override;
   bool IsInUnifiedMode() const override;
   bool IsInUnifiedModeIgnoreMirroring() const override;
-  void SetDisplayWorkAreaInsets(WmWindow* window,
+  void SetDisplayWorkAreaInsets(aura::Window* window,
                                 const gfx::Insets& insets) override;
   std::unique_ptr<display::TouchTransformSetter> CreateTouchTransformDelegate()
       override;
@@ -65,7 +65,7 @@
   std::unique_ptr<wm::TabletModeEventHandler> CreateTabletModeEventHandler()
       override;
   std::unique_ptr<WorkspaceEventHandler> CreateWorkspaceEventHandler(
-      WmWindow* workspace_window) override;
+      aura::Window* workspace_window) override;
   std::unique_ptr<ScopedDisableInternalMouseAndKeyboard>
   CreateScopedDisableInternalMouseAndKeyboard() override;
   std::unique_ptr<ImmersiveFullscreenController>
diff --git a/ash/login/lock_screen_controller_unittest.cc b/ash/login/lock_screen_controller_unittest.cc
index cc3f8d4..3e42c9e 100644
--- a/ash/login/lock_screen_controller_unittest.cc
+++ b/ash/login/lock_screen_controller_unittest.cc
@@ -4,58 +4,38 @@
 
 #include "ash/login/lock_screen_controller.h"
 
+#include "ash/login/mock_lock_screen_client.h"
 #include "ash/shell.h"
 #include "ash/test/ash_test_base.h"
 #include "base/run_loop.h"
-#include "chromeos/cryptohome/system_salt_getter.h"
+
+using ::testing::_;
 
 namespace ash {
 
 namespace {
-
-class TestLockScreenClient : public mojom::LockScreenClient {
- public:
-  TestLockScreenClient() : binding_(this) {}
-  ~TestLockScreenClient() override = default;
-
-  mojom::LockScreenClientPtr CreateInterfacePtrAndBind() {
-    return binding_.CreateInterfacePtrAndBind();
-  }
-
-  // mojom::LockScreenClient:
-  void AuthenticateUser(const AccountId& account_id,
-                        const std::string& password,
-                        bool authenticated_by_pin) override {
-    ++autentication_requests_count_;
-  }
-
-  int authentication_requests_count() const {
-    return autentication_requests_count_;
-  }
-
- private:
-  mojo::Binding<ash::mojom::LockScreenClient> binding_;
-  int autentication_requests_count_ = 0;
-
-  DISALLOW_COPY_AND_ASSIGN(TestLockScreenClient);
-};
-
 using LockScreenControllerTest = test::AshTestBase;
-
 }  // namespace
 
 TEST_F(LockScreenControllerTest, RequestAuthentication) {
-  LockScreenController* lock_screen_controller =
-      Shell::Get()->lock_screen_controller();
-  TestLockScreenClient lock_screen_client;
-  lock_screen_controller->SetClient(
-      lock_screen_client.CreateInterfacePtrAndBind());
-  EXPECT_EQ(0, lock_screen_client.authentication_requests_count());
+  LockScreenController* controller = Shell::Get()->lock_screen_controller();
+  std::unique_ptr<MockLockScreenClient> client = BindMockLockScreenClient();
 
   AccountId id = AccountId::FromUserEmail("user1@test.com");
-  lock_screen_controller->AuthenticateUser(id, std::string(), false);
+
+  // We hardcode the hashed password. This is fine because the password hash
+  // algorithm should never accidently change; if it does we will need to
+  // have cryptohome migration code and one failing test isn't a problem.
+  std::string password = "password";
+  std::string hashed_password = "40c7b00f3bccc7675ec5b732de4bfbe4";
+  EXPECT_NE(password, hashed_password);
+
+  // Verify AuthenticateUser mojo call is run with the same account id, a
+  // (hashed) password, and the correct PIN state.
+  EXPECT_CALL(*client, AuthenticateUser(id, hashed_password, false));
+  controller->AuthenticateUser(id, password, false);
+
   base::RunLoop().RunUntilIdle();
-  EXPECT_EQ(1, lock_screen_client.authentication_requests_count());
 }
 
 }  // namespace ash
diff --git a/ash/login/mock_lock_screen_client.cc b/ash/login/mock_lock_screen_client.cc
new file mode 100644
index 0000000..364f90c
--- /dev/null
+++ b/ash/login/mock_lock_screen_client.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/login/mock_lock_screen_client.h"
+
+#include "ash/login/lock_screen_controller.h"
+#include "ash/shell.h"
+
+namespace ash {
+
+MockLockScreenClient::MockLockScreenClient() : binding_(this) {}
+
+MockLockScreenClient::~MockLockScreenClient() = default;
+
+mojom::LockScreenClientPtr MockLockScreenClient::CreateInterfacePtrAndBind() {
+  return binding_.CreateInterfacePtrAndBind();
+}
+
+std::unique_ptr<MockLockScreenClient> BindMockLockScreenClient() {
+  LockScreenController* lock_screen_controller =
+      Shell::Get()->lock_screen_controller();
+  auto lock_screen_client = base::MakeUnique<MockLockScreenClient>();
+  lock_screen_controller->SetClient(
+      lock_screen_client->CreateInterfacePtrAndBind());
+  return lock_screen_client;
+}
+
+}  // namespace ash
diff --git a/ash/login/mock_lock_screen_client.h b/ash/login/mock_lock_screen_client.h
new file mode 100644
index 0000000..52874b35
--- /dev/null
+++ b/ash/login/mock_lock_screen_client.h
@@ -0,0 +1,38 @@
+// 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_LOGIN_MOCK_LOCK_SCREEN_CLIENT_H_
+#define ASH_LOGIN_MOCK_LOCK_SCREEN_CLIENT_H_
+
+#include "ash/public/interfaces/lock_screen.mojom.h"
+#include "mojo/public/cpp/bindings/binding_set.h"
+#include "testing/gmock/include/gmock/gmock.h"
+
+namespace ash {
+
+class MockLockScreenClient : public mojom::LockScreenClient {
+ public:
+  MockLockScreenClient();
+  ~MockLockScreenClient() override;
+
+  mojom::LockScreenClientPtr CreateInterfacePtrAndBind();
+
+  // mojom::LockScreenClient:
+  MOCK_METHOD3(AuthenticateUser,
+               void(const AccountId& account_id,
+                    const std::string& password,
+                    bool authenticated_by_pin));
+
+ private:
+  mojo::Binding<ash::mojom::LockScreenClient> binding_;
+
+  DISALLOW_COPY_AND_ASSIGN(MockLockScreenClient);
+};
+
+// Helper method to bind a lock screen client so it receives all mojo calls.
+std::unique_ptr<MockLockScreenClient> BindMockLockScreenClient();
+
+}  // namespace ash
+
+#endif  // ASH_LOGIN_MOCK_LOCK_SCREEN_CLIENT_H_
\ No newline at end of file
diff --git a/ash/mus/BUILD.gn b/ash/mus/BUILD.gn
index 1409ea7..96714f3ee 100644
--- a/ash/mus/BUILD.gn
+++ b/ash/mus/BUILD.gn
@@ -32,6 +32,8 @@
     "context_menu_mus.h",
     "disconnected_app_handler.cc",
     "disconnected_app_handler.h",
+    "display_synchronizer.cc",
+    "display_synchronizer.h",
     "drag_window_resizer.cc",
     "drag_window_resizer.h",
     "frame/detached_title_area_renderer.cc",
diff --git a/ash/mus/accelerators/accelerator_controller_registrar.cc b/ash/mus/accelerators/accelerator_controller_registrar.cc
index 6f0c197..bff6040 100644
--- a/ash/mus/accelerators/accelerator_controller_registrar.cc
+++ b/ash/mus/accelerators/accelerator_controller_registrar.cc
@@ -14,7 +14,6 @@
 #include "ash/shell.h"
 #include "ash/wm/window_cycle_controller.h"
 #include "ash/wm/window_util.h"
-#include "ash/wm_window.h"
 #include "base/logging.h"
 #include "services/ui/common/accelerator_util.h"
 #include "services/ui/public/cpp/property_type_converters.h"
@@ -96,8 +95,8 @@
     if (!target_window)
       target_window = Shell::GetRootWindowForNewWindows();
     DCHECK(target_window);
-    if (router_->ProcessAccelerator(WmWindow::Get(target_window),
-                                    *(event.AsKeyEvent()), accelerator)) {
+    if (router_->ProcessAccelerator(target_window, *(event.AsKeyEvent()),
+                                    accelerator)) {
       return ui::mojom::EventResult::HANDLED;
     }
     if (accelerator_controller->IsActionForAcceleratorEnabled(accelerator)) {
diff --git a/ash/mus/bridge/shell_port_mash.cc b/ash/mus/bridge/shell_port_mash.cc
index 61c9646..cdca83b5 100644
--- a/ash/mus/bridge/shell_port_mash.cc
+++ b/ash/mus/bridge/shell_port_mash.cc
@@ -20,6 +20,7 @@
 #include "ash/mus/ash_window_tree_host_mus.h"
 #include "ash/mus/bridge/immersive_handler_factory_mus.h"
 #include "ash/mus/bridge/workspace_event_handler_mus.h"
+#include "ash/mus/display_synchronizer.h"
 #include "ash/mus/drag_window_resizer.h"
 #include "ash/mus/keyboard_ui_mus.h"
 #include "ash/mus/screen_mus.h"
@@ -161,6 +162,8 @@
 }
 
 void ShellPortMash::Shutdown() {
+  display_synchronizer_.reset();
+
   if (added_display_observer_)
     Shell::Get()->window_tree_host_manager()->RemoveObserver(this);
 
@@ -258,15 +261,15 @@
   return false;
 }
 
-void ShellPortMash::SetDisplayWorkAreaInsets(WmWindow* window,
+void ShellPortMash::SetDisplayWorkAreaInsets(aura::Window* window,
                                              const gfx::Insets& insets) {
   if (GetAshConfig() == Config::MUS) {
     Shell::Get()
         ->window_tree_host_manager()
-        ->UpdateWorkAreaOfDisplayNearestWindow(window->aura_window(), insets);
+        ->UpdateWorkAreaOfDisplayNearestWindow(window, insets);
     return;
   }
-  window_manager_->screen()->SetWorkAreaInsets(window->aura_window(), insets);
+  window_manager_->screen()->SetWorkAreaInsets(window, insets);
 }
 
 std::unique_ptr<display::TouchTransformSetter>
@@ -398,12 +401,11 @@
 }
 
 std::unique_ptr<WorkspaceEventHandler>
-ShellPortMash::CreateWorkspaceEventHandler(WmWindow* workspace_window) {
+ShellPortMash::CreateWorkspaceEventHandler(aura::Window* workspace_window) {
   if (GetAshConfig() == Config::MUS)
     return base::MakeUnique<WorkspaceEventHandlerAura>(workspace_window);
 
-  return base::MakeUnique<WorkspaceEventHandlerMus>(
-      WmWindow::GetAuraWindow(workspace_window));
+  return base::MakeUnique<WorkspaceEventHandlerMus>(workspace_window);
 }
 
 std::unique_ptr<ImmersiveFullscreenController>
@@ -525,8 +527,7 @@
 
 std::unique_ptr<AshWindowTreeHost> ShellPortMash::CreateAshWindowTreeHost(
     const AshWindowTreeHostInitParams& init_params) {
-  // TODO(sky): make this work for mash too.
-  if (GetAshConfig() != Config::MUS)
+  if (!Shell::ShouldEnableSimplifiedDisplayManagement())
     return nullptr;
 
   std::unique_ptr<aura::DisplayInitParams> display_params =
@@ -544,7 +545,6 @@
     display_params->display =
         base::MakeUnique<display::Display>(mirrored_display);
   }
-  // TODO: wire update is_primary_display correctly.
   display_params->is_primary_display = true;
   aura::WindowTreeHostMusInitParams aura_init_params =
       window_manager_->window_manager_client()->CreateInitParamsForNewDisplay();
@@ -576,8 +576,10 @@
 }
 
 void ShellPortMash::InitHosts(const ShellInitParams& init_params) {
-  if (GetAshConfig() == Config::MUS) {
+  if (Shell::ShouldEnableSimplifiedDisplayManagement()) {
     Shell::Get()->window_tree_host_manager()->InitHosts();
+    display_synchronizer_ = base::MakeUnique<DisplaySynchronizer>(
+        window_manager_->window_manager_client());
   } else {
     window_manager_->CreatePrimaryRootWindowController(
         base::WrapUnique(init_params.primary_window_tree_host));
diff --git a/ash/mus/bridge/shell_port_mash.h b/ash/mus/bridge/shell_port_mash.h
index 2624227..be47cb2 100644
--- a/ash/mus/bridge/shell_port_mash.h
+++ b/ash/mus/bridge/shell_port_mash.h
@@ -25,6 +25,7 @@
 namespace ash {
 
 class AcceleratorControllerDelegateAura;
+class DisplaySynchronizer;
 class PointerWatcherAdapter;
 class RootWindowController;
 
@@ -70,7 +71,7 @@
   display::Display GetFirstDisplay() const override;
   bool IsInUnifiedMode() const override;
   bool IsInUnifiedModeIgnoreMirroring() const override;
-  void SetDisplayWorkAreaInsets(WmWindow* window,
+  void SetDisplayWorkAreaInsets(aura::Window* window,
                                 const gfx::Insets& insets) override;
   std::unique_ptr<display::TouchTransformSetter> CreateTouchTransformDelegate()
       override;
@@ -92,7 +93,7 @@
   std::unique_ptr<wm::TabletModeEventHandler> CreateTabletModeEventHandler()
       override;
   std::unique_ptr<WorkspaceEventHandler> CreateWorkspaceEventHandler(
-      WmWindow* workspace_window) override;
+      aura::Window* workspace_window) override;
   std::unique_ptr<ScopedDisableInternalMouseAndKeyboard>
   CreateScopedDisableInternalMouseAndKeyboard() override;
   std::unique_ptr<ImmersiveFullscreenController>
@@ -160,6 +161,8 @@
 
   std::unique_ptr<SessionStateDelegate> session_state_delegate_;
 
+  std::unique_ptr<DisplaySynchronizer> display_synchronizer_;
+
   bool added_display_observer_ = false;
   base::ObserverList<WmDisplayObserver> display_observers_;
 
diff --git a/ash/mus/display_synchronizer.cc b/ash/mus/display_synchronizer.cc
new file mode 100644
index 0000000..f948e2f
--- /dev/null
+++ b/ash/mus/display_synchronizer.cc
@@ -0,0 +1,57 @@
+// 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/mus/display_synchronizer.h"
+
+#include "ash/shell.h"
+#include "ui/aura/mus/window_manager_delegate.h"
+#include "ui/display/manager/display_manager.h"
+#include "ui/display/manager/managed_display_info.h"
+
+namespace ash {
+
+DisplaySynchronizer::DisplaySynchronizer(
+    aura::WindowManagerClient* window_manager_client)
+    : window_manager_client_(window_manager_client) {
+  Shell::Get()->window_tree_host_manager()->AddObserver(this);
+  SendDisplayConfigurationToServer();
+}
+
+DisplaySynchronizer::~DisplaySynchronizer() {
+  Shell::Get()->window_tree_host_manager()->RemoveObserver(this);
+}
+
+void DisplaySynchronizer::SendDisplayConfigurationToServer() {
+  display::DisplayManager* display_manager = Shell::Get()->display_manager();
+  const size_t display_count = display_manager->GetNumDisplays();
+  if (display_count == 0)
+    return;
+
+  std::vector<display::Display> displays;
+  std::vector<ui::mojom::WmViewportMetricsPtr> metrics;
+  for (size_t i = 0; i < display_count; ++i) {
+    displays.push_back(display_manager->GetDisplayAt(i));
+    ui::mojom::WmViewportMetricsPtr viewport_metrics =
+        ui::mojom::WmViewportMetrics::New();
+    const display::ManagedDisplayInfo& display_info =
+        display_manager->GetDisplayInfo(displays.back().id());
+    viewport_metrics->bounds_in_pixels = display_info.bounds_in_native();
+    viewport_metrics->device_scale_factor = display_info.device_scale_factor();
+    viewport_metrics->ui_scale_factor = display_info.configured_ui_scale();
+    metrics.push_back(std::move(viewport_metrics));
+  }
+  window_manager_client_->SetDisplayConfiguration(
+      displays, std::move(metrics),
+      WindowTreeHostManager::GetPrimaryDisplayId());
+}
+
+void DisplaySynchronizer::OnDisplaysInitialized() {
+  SendDisplayConfigurationToServer();
+}
+
+void DisplaySynchronizer::OnDisplayConfigurationChanged() {
+  SendDisplayConfigurationToServer();
+}
+
+}  // namespace ash
diff --git a/ash/mus/display_synchronizer.h b/ash/mus/display_synchronizer.h
new file mode 100644
index 0000000..791fffe3
--- /dev/null
+++ b/ash/mus/display_synchronizer.h
@@ -0,0 +1,40 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef ASH_MUS_DISPLAY_SYNCHRONIZER_H_
+#define ASH_MUS_DISPLAY_SYNCHRONIZER_H_
+
+#include "ash/display/window_tree_host_manager.h"
+#include "base/macros.h"
+
+namespace aura {
+class WindowManagerClient;
+}
+
+namespace ash {
+
+// DisplaySynchronizer keeps the display state in mus in sync with ash's display
+// state. As ash controls the overall display state this synchronization is one
+// way (from ash to mus).
+class DisplaySynchronizer : public ash::WindowTreeHostManager::Observer {
+ public:
+  explicit DisplaySynchronizer(
+      aura::WindowManagerClient* window_manager_client);
+  ~DisplaySynchronizer() override;
+
+ private:
+  void SendDisplayConfigurationToServer();
+
+  // WindowTreeHostManager::Observer:
+  void OnDisplaysInitialized() override;
+  void OnDisplayConfigurationChanged() override;
+
+  aura::WindowManagerClient* window_manager_client_;
+
+  DISALLOW_COPY_AND_ASSIGN(DisplaySynchronizer);
+};
+
+}  // namespace ash
+
+#endif  // ASH_MUS_DISPLAY_SYNCHRONIZER_H_
diff --git a/ash/mus/shell_delegate_mus.cc b/ash/mus/shell_delegate_mus.cc
index 9a4fb29..b068bc9 100644
--- a/ash/mus/shell_delegate_mus.cc
+++ b/ash/mus/shell_delegate_mus.cc
@@ -71,7 +71,7 @@
   return false;
 }
 
-bool ShellDelegateMus::CanShowWindowForUser(WmWindow* window) const {
+bool ShellDelegateMus::CanShowWindowForUser(aura::Window* window) const {
   NOTIMPLEMENTED();
   return true;
 }
diff --git a/ash/mus/shell_delegate_mus.h b/ash/mus/shell_delegate_mus.h
index c5cadea..514db36 100644
--- a/ash/mus/shell_delegate_mus.h
+++ b/ash/mus/shell_delegate_mus.h
@@ -26,7 +26,7 @@
   bool IsIncognitoAllowed() const override;
   bool IsMultiProfilesEnabled() const override;
   bool IsRunningInForcedAppMode() const override;
-  bool CanShowWindowForUser(WmWindow* window) const override;
+  bool CanShowWindowForUser(aura::Window* window) const override;
   bool IsForceMaximizeOnFirstRun() const override;
   void PreInit() override;
   void PreShutdown() override;
diff --git a/ash/mus/window_manager.cc b/ash/mus/window_manager.cc
index b9ea19f..08d115ca 100644
--- a/ash/mus/window_manager.cc
+++ b/ash/mus/window_manager.cc
@@ -126,30 +126,22 @@
   DCHECK_EQ(nullptr, ash::Shell::window_tree_client());
   ash::Shell::set_window_tree_client(window_tree_client_.get());
 
-  // TODO(sky): remove and use MUS code.
+  // TODO(sky): remove and use MUS code. This should really be
+  // ShouldEnableSimplifiedDisplayManagement(), but as ShellPort hasn't been
+  // created yet it can't be used here.
   if (config_ == Config::MASH) {
     // |connector_| is null in some tests.
     if (connector_)
       connector_->BindInterface(ui::mojom::kServiceName, &display_controller_);
     screen_ = base::MakeUnique<ScreenMus>(display_controller_.get());
     display::Screen::SetScreenInstance(screen_.get());
+    InstallFrameDecorationValues();
   }
 
   pointer_watcher_event_router_ =
       base::MakeUnique<views::PointerWatcherEventRouter>(
           window_tree_client_.get());
 
-  ui::mojom::FrameDecorationValuesPtr frame_decoration_values =
-      ui::mojom::FrameDecorationValues::New();
-  const gfx::Insets client_area_insets =
-      NonClientFrameController::GetPreferredClientAreaInsets();
-  frame_decoration_values->normal_client_area_insets = client_area_insets;
-  frame_decoration_values->maximized_client_area_insets = client_area_insets;
-  frame_decoration_values->max_title_bar_button_width =
-      NonClientFrameController::GetMaxTitleBarButtonWidth();
-  window_manager_client_->SetFrameDecorationValues(
-      std::move(frame_decoration_values));
-
   // Notify PointerWatcherEventRouter and CaptureSynchronizer that the capture
   // client has been set.
   aura::client::CaptureClient* capture_client = wm_state_->capture_controller();
@@ -261,6 +253,19 @@
   root_window_controllers_.insert(std::move(root_window_controller));
 }
 
+void WindowManager::InstallFrameDecorationValues() {
+  ui::mojom::FrameDecorationValuesPtr frame_decoration_values =
+      ui::mojom::FrameDecorationValues::New();
+  const gfx::Insets client_area_insets =
+      NonClientFrameController::GetPreferredClientAreaInsets();
+  frame_decoration_values->normal_client_area_insets = client_area_insets;
+  frame_decoration_values->maximized_client_area_insets = client_area_insets;
+  frame_decoration_values->max_title_bar_button_width =
+      NonClientFrameController::GetMaxTitleBarButtonWidth();
+  window_manager_client_->SetFrameDecorationValues(
+      std::move(frame_decoration_values));
+}
+
 void WindowManager::DestroyRootWindowController(
     RootWindowController* root_window_controller,
     bool in_shutdown) {
@@ -341,6 +346,7 @@
   CreateShell(nullptr);
   if (show_primary_host_on_connect_)
     Shell::GetPrimaryRootWindow()->GetHost()->Show();
+  InstallFrameDecorationValues();
 }
 
 void WindowManager::OnWmSetBounds(aura::Window* window,
diff --git a/ash/mus/window_manager.h b/ash/mus/window_manager.h
index daf1592..35a7f89b 100644
--- a/ash/mus/window_manager.h
+++ b/ash/mus/window_manager.h
@@ -138,6 +138,9 @@
       const display::Display& display,
       ash::RootWindowController::RootWindowType root_window_type);
 
+  // Sets the frame decoration values on the server.
+  void InstallFrameDecorationValues();
+
   // Deletes the specified RootWindowController. Called when a display is
   // removed. |in_shutdown| is true if called from Shutdown().
   void DestroyRootWindowController(RootWindowController* root_window_controller,
diff --git a/ash/shelf/shelf_layout_manager.cc b/ash/shelf/shelf_layout_manager.cc
index c9bfdf3..70450454 100644
--- a/ash/shelf/shelf_layout_manager.cc
+++ b/ash/shelf/shelf_layout_manager.cc
@@ -25,7 +25,6 @@
 #include "ash/wm/mru_window_tracker.h"
 #include "ash/wm/screen_pinning_controller.h"
 #include "ash/wm/window_state.h"
-#include "ash/wm_window.h"
 #include "base/auto_reset.h"
 #include "base/command_line.h"
 #include "base/i18n/rtl.h"
@@ -452,8 +451,8 @@
   // window.
   if (Shell::Get()->session_controller()->IsUserSessionBlocked() &&
       keyboard_is_about_to_hide) {
-    WmWindow* window = WmWindow::Get(shelf_widget_->GetNativeWindow());
-    ShellPort::Get()->SetDisplayWorkAreaInsets(window, gfx::Insets());
+    ShellPort::Get()->SetDisplayWorkAreaInsets(shelf_widget_->GetNativeWindow(),
+                                               gfx::Insets());
   }
 }
 
@@ -642,8 +641,8 @@
       // if keyboard is not shown.
       if (!state_.IsAddingSecondaryUser() || !keyboard_bounds_.IsEmpty())
         insets = target_bounds.work_area_insets;
-      WmWindow* shelf_window = WmWindow::Get(shelf_widget_->GetNativeWindow());
-      ShellPort::Get()->SetDisplayWorkAreaInsets(shelf_window, insets);
+      ShellPort::Get()->SetDisplayWorkAreaInsets(
+          shelf_widget_->GetNativeWindow(), insets);
     }
   }
 
diff --git a/ash/shelf/shelf_tooltip_manager.cc b/ash/shelf/shelf_tooltip_manager.cc
index abddb3d2..6176fede 100644
--- a/ash/shelf/shelf_tooltip_manager.cc
+++ b/ash/shelf/shelf_tooltip_manager.cc
@@ -10,7 +10,7 @@
 #include "ash/shelf/shelf_view.h"
 #include "ash/shell_port.h"
 #include "ash/system/tray/tray_constants.h"
-#include "ash/wm_window.h"
+#include "ash/wm/window_util.h"
 #include "base/bind.h"
 #include "base/strings/string16.h"
 #include "base/threading/thread_task_runner_handle.h"
@@ -23,6 +23,7 @@
 #include "ui/views/controls/label.h"
 #include "ui/views/layout/fill_layout.h"
 #include "ui/views/widget/widget.h"
+#include "ui/wm/core/window_animations.h"
 
 namespace ash {
 namespace {
@@ -108,8 +109,7 @@
   void OnBeforeBubbleWidgetInit(views::Widget::InitParams* params,
                                 views::Widget* bubble_widget) const override {
     // Place the bubble in the same display as the anchor.
-    WmWindow::Get(anchor_widget()->GetNativeWindow())
-        ->GetRootWindowController()
+    RootWindowController::ForWindow(anchor_widget()->GetNativeWindow())
         ->ConfigureWidgetInitParamsForContainer(
             bubble_widget, kShellWindowId_SettingBubbleContainer, params);
   }
@@ -132,16 +132,16 @@
 ShelfTooltipManager::~ShelfTooltipManager() {
   ShellPort::Get()->RemovePointerWatcher(this);
   shelf_view_->shelf()->RemoveObserver(this);
-  WmWindow* window = nullptr;
+  aura::Window* window = nullptr;
   if (shelf_view_->GetWidget())
-    window = WmWindow::Get(shelf_view_->GetWidget()->GetNativeWindow());
+    window = shelf_view_->GetWidget()->GetNativeWindow();
   if (window)
-    window->RemoveLimitedPreTargetHandler(this);
+    wm::RemoveLimitedPreTargetHandlerForWindow(this, window);
 }
 
 void ShelfTooltipManager::Init() {
-  WmWindow* window = WmWindow::Get(shelf_view_->GetWidget()->GetNativeWindow());
-  window->AddLimitedPreTargetHandler(this);
+  wm::AddLimitedPreTargetHandlerForWindow(
+      this, shelf_view_->GetWidget()->GetNativeWindow());
 }
 
 void ShelfTooltipManager::Close() {
@@ -163,8 +163,8 @@
   timer_.Stop();
   if (bubble_) {
     // Cancel the hiding animation to hide the old bubble immediately.
-    WmWindow::Get(bubble_->GetWidget()->GetNativeWindow())
-        ->SetVisibilityAnimationTransition(::wm::ANIMATE_NONE);
+    ::wm::SetWindowVisibilityAnimationTransition(
+        bubble_->GetWidget()->GetNativeWindow(), ::wm::ANIMATE_NONE);
     Close();
   }
 
@@ -187,10 +187,10 @@
 
   base::string16 text = shelf_view_->GetTitleForView(view);
   bubble_ = new ShelfTooltipBubble(view, arrow, text);
-  WmWindow* window = WmWindow::Get(bubble_->GetWidget()->GetNativeWindow());
-  window->SetVisibilityAnimationType(
-      ::wm::WINDOW_VISIBILITY_ANIMATION_TYPE_VERTICAL);
-  window->SetVisibilityAnimationTransition(::wm::ANIMATE_HIDE);
+  aura::Window* window = bubble_->GetWidget()->GetNativeWindow();
+  ::wm::SetWindowVisibilityAnimationType(
+      window, ::wm::WINDOW_VISIBILITY_ANIMATION_TYPE_VERTICAL);
+  ::wm::SetWindowVisibilityAnimationTransition(window, ::wm::ANIMATE_HIDE);
   bubble_->GetWidget()->Show();
 }
 
diff --git a/ash/shelf/shelf_window_watcher.cc b/ash/shelf/shelf_window_watcher.cc
index ec2fde2..c1112f4 100644
--- a/ash/shelf/shelf_window_watcher.cc
+++ b/ash/shelf/shelf_window_watcher.cc
@@ -197,9 +197,9 @@
     item.id.launch_id = base::IntToString(id++);
   }
 
-  model_->SetShelfItemDelegate(item.id,
-                               base::MakeUnique<ShelfWindowWatcherItemDelegate>(
-                                   item.id, WmWindow::Get(window)));
+  model_->SetShelfItemDelegate(
+      item.id,
+      base::MakeUnique<ShelfWindowWatcherItemDelegate>(item.id, window));
   // Panels are inserted on the left so as not to push all existing panels over.
   model_->AddAt(item.type == TYPE_APP_PANEL ? 0 : model_->item_count(), item);
 }
diff --git a/ash/shelf/shelf_window_watcher_item_delegate.cc b/ash/shelf/shelf_window_watcher_item_delegate.cc
index 001b25f..c10440b 100644
--- a/ash/shelf/shelf_window_watcher_item_delegate.cc
+++ b/ash/shelf/shelf_window_watcher_item_delegate.cc
@@ -12,9 +12,10 @@
 #include "ash/shell.h"
 #include "ash/wm/window_state.h"
 #include "ash/wm/window_util.h"
-#include "ash/wm_window.h"
+#include "ui/aura/client/aura_constants.h"
 #include "ui/aura/window.h"
 #include "ui/events/event_constants.h"
+#include "ui/wm/core/window_animations.h"
 
 namespace ash {
 
@@ -30,7 +31,7 @@
 
 ShelfWindowWatcherItemDelegate::ShelfWindowWatcherItemDelegate(
     const ShelfID& id,
-    WmWindow* window)
+    aura::Window* window)
     : ShelfItemDelegate(id), window_(window) {
   DCHECK(!id.IsNull());
   DCHECK(window_);
@@ -45,24 +46,24 @@
     ItemSelectedCallback callback) {
   // Move panels attached on another display to the current display.
   if (GetShelfItemType(shelf_id()) == TYPE_APP_PANEL &&
-      window_->aura_window()->GetProperty(kPanelAttachedKey) &&
-      wm::MoveWindowToDisplay(window_->aura_window(), display_id)) {
-    window_->Activate();
+      window_->GetProperty(kPanelAttachedKey) &&
+      wm::MoveWindowToDisplay(window_, display_id)) {
+    wm::ActivateWindow(window_);
     std::move(callback).Run(SHELF_ACTION_WINDOW_ACTIVATED, base::nullopt);
     return;
   }
 
-  if (window_->IsActive()) {
+  if (wm::IsActiveWindow(window_)) {
     if (event && event->type() == ui::ET_KEY_RELEASED) {
-      window_->Animate(::wm::WINDOW_ANIMATION_TYPE_BOUNCE);
+      ::wm::AnimateWindow(window_, ::wm::WINDOW_ANIMATION_TYPE_BOUNCE);
       std::move(callback).Run(SHELF_ACTION_NONE, base::nullopt);
       return;
     }
-    window_->Minimize();
+    window_->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MINIMIZED);
     std::move(callback).Run(SHELF_ACTION_WINDOW_MINIMIZED, base::nullopt);
     return;
   }
-  window_->Activate();
+  wm::ActivateWindow(window_);
   std::move(callback).Run(SHELF_ACTION_WINDOW_ACTIVATED, base::nullopt);
 }
 
@@ -70,7 +71,7 @@
                                                     int32_t event_flags) {}
 
 void ShelfWindowWatcherItemDelegate::Close() {
-  window_->CloseWidget();
+  wm::CloseWidgetForWindow(window_);
 }
 
 }  // namespace ash
diff --git a/ash/shelf/shelf_window_watcher_item_delegate.h b/ash/shelf/shelf_window_watcher_item_delegate.h
index 64e8af5..00bbd6a 100644
--- a/ash/shelf/shelf_window_watcher_item_delegate.h
+++ b/ash/shelf/shelf_window_watcher_item_delegate.h
@@ -8,15 +8,17 @@
 #include "ash/public/cpp/shelf_item_delegate.h"
 #include "base/macros.h"
 
-namespace ash {
+namespace aura {
+class Window;
+}
 
-class WmWindow;
+namespace ash {
 
 // ShelfItemDelegate for the items created by ShelfWindowWatcher, for example:
 // The Chrome OS settings window, task manager window, and panel windows.
 class ShelfWindowWatcherItemDelegate : public ShelfItemDelegate {
  public:
-  ShelfWindowWatcherItemDelegate(const ShelfID& id, WmWindow* window);
+  ShelfWindowWatcherItemDelegate(const ShelfID& id, aura::Window* window);
   ~ShelfWindowWatcherItemDelegate() override;
 
  private:
@@ -29,7 +31,7 @@
   void Close() override;
 
   // The window associated with this item. Not owned.
-  WmWindow* window_;
+  aura::Window* window_;
 
   DISALLOW_COPY_AND_ASSIGN(ShelfWindowWatcherItemDelegate);
 };
diff --git a/ash/shell.cc b/ash/shell.cc
index 338f3d47c9..3ddcef2 100644
--- a/ash/shell.cc
+++ b/ash/shell.cc
@@ -339,7 +339,7 @@
 
 void Shell::SetDisplayWorkAreaInsets(Window* contains,
                                      const gfx::Insets& insets) {
-  shell_port_->SetDisplayWorkAreaInsets(WmWindow::Get(contains), insets);
+  shell_port_->SetDisplayWorkAreaInsets(contains, insets);
 }
 
 void Shell::OnCastingSessionStartedOrStopped(bool started) {
diff --git a/ash/shell/shell_delegate_impl.cc b/ash/shell/shell_delegate_impl.cc
index 1a4a4eb..e9112ba 100644
--- a/ash/shell/shell_delegate_impl.cc
+++ b/ash/shell/shell_delegate_impl.cc
@@ -102,7 +102,7 @@
   return false;
 }
 
-bool ShellDelegateImpl::CanShowWindowForUser(WmWindow* window) const {
+bool ShellDelegateImpl::CanShowWindowForUser(aura::Window* window) const {
   return true;
 }
 
diff --git a/ash/shell/shell_delegate_impl.h b/ash/shell/shell_delegate_impl.h
index dbc15971..f768cea 100644
--- a/ash/shell/shell_delegate_impl.h
+++ b/ash/shell/shell_delegate_impl.h
@@ -28,7 +28,7 @@
   bool IsIncognitoAllowed() const override;
   bool IsMultiProfilesEnabled() const override;
   bool IsRunningInForcedAppMode() const override;
-  bool CanShowWindowForUser(WmWindow* window) const override;
+  bool CanShowWindowForUser(aura::Window* window) const override;
   bool IsForceMaximizeOnFirstRun() const override;
   void PreInit() override;
   void PreShutdown() override;
diff --git a/ash/shell_delegate.h b/ash/shell_delegate.h
index 218e00b..3022a58 100644
--- a/ash/shell_delegate.h
+++ b/ash/shell_delegate.h
@@ -15,6 +15,10 @@
 class GURL;
 class PrefService;
 
+namespace aura {
+class Window;
+}
+
 namespace gfx {
 class Image;
 }
@@ -41,7 +45,6 @@
 struct ShelfItem;
 class SystemTrayDelegate;
 class WallpaperDelegate;
-class WmWindow;
 
 // Delegate of the Shell.
 class ASH_EXPORT ShellDelegate {
@@ -64,7 +67,7 @@
 
   // Returns true if |window| can be shown for the delegate's concept of current
   // user.
-  virtual bool CanShowWindowForUser(WmWindow* window) const = 0;
+  virtual bool CanShowWindowForUser(aura::Window* window) const = 0;
 
   // Returns true if the first window shown on first run should be
   // unconditionally maximized, overriding the heuristic that normally chooses
diff --git a/ash/shell_port.h b/ash/shell_port.h
index 8f56b7f5..1c3376a 100644
--- a/ash/shell_port.h
+++ b/ash/shell_port.h
@@ -112,7 +112,7 @@
   bool IsForceMaximizeOnFirstRun();
 
   // Sets work area insets of the display containing |window|, pings observers.
-  virtual void SetDisplayWorkAreaInsets(WmWindow* window,
+  virtual void SetDisplayWorkAreaInsets(aura::Window* window,
                                         const gfx::Insets& insets) = 0;
 
   // Returns true if a system-modal dialog window is currently open.
@@ -171,7 +171,7 @@
   CreateTabletModeEventHandler() = 0;
 
   virtual std::unique_ptr<WorkspaceEventHandler> CreateWorkspaceEventHandler(
-      WmWindow* workspace_window) = 0;
+      aura::Window* workspace_window) = 0;
 
   virtual std::unique_ptr<ScopedDisableInternalMouseAndKeyboard>
   CreateScopedDisableInternalMouseAndKeyboard() = 0;
diff --git a/ash/system/bluetooth/tray_bluetooth.cc b/ash/system/bluetooth/tray_bluetooth.cc
index cf6a5f9..58cd1aa 100644
--- a/ash/system/bluetooth/tray_bluetooth.cc
+++ b/ash/system/bluetooth/tray_bluetooth.cc
@@ -331,26 +331,13 @@
       HoverHighlightView* container =
           AddScrollListItem(icon, device.display_name);
       if (device.connected)
-        SetupConnectedItem(container);
+        SetupConnectedScrollListItem(container);
       else if (device.connecting)
-        SetupConnectingItem(container);
+        SetupConnectingScrollListItem(container);
       device_map_[container] = device.address;
     }
   }
 
-  void SetupConnectedItem(HoverHighlightView* container) {
-    container->SetSubText(l10n_util::GetStringUTF16(
-        IDS_ASH_STATUS_TRAY_NETWORK_STATUS_CONNECTED));
-    TrayPopupItemStyle style(TrayPopupItemStyle::FontStyle::CAPTION);
-    style.set_color_style(TrayPopupItemStyle::ColorStyle::CONNECTED);
-    style.SetupLabel(container->sub_text_label());
-  }
-
-  void SetupConnectingItem(HoverHighlightView* container) {
-    container->SetSubText(l10n_util::GetStringUTF16(
-        IDS_ASH_STATUS_TRAY_NETWORK_STATUS_CONNECTING));
-  }
-
   // Returns true if the device with |device_id| is found in |device_list|.
   bool FoundDevice(const std::string& device_id,
                    const BluetoothDeviceList& device_list) {
@@ -368,7 +355,7 @@
     if (FoundDevice(device_id, paired_not_connected_devices_)) {
       HoverHighlightView* container =
           static_cast<HoverHighlightView*>(item_container);
-      SetupConnectingItem(container);
+      SetupConnectingScrollListItem(container);
       scroll_content()->SizeToPreferredSize();
       scroller()->Layout();
     }
diff --git a/ash/system/network/network_list.cc b/ash/system/network/network_list.cc
index 7c4c3f9..b462bbd 100644
--- a/ash/system/network/network_list.cc
+++ b/ash/system/network/network_list.cc
@@ -82,29 +82,6 @@
       network->guid(), network->profile_path(), nullptr /* onc_source */);
 }
 
-// TODO(varkha|mohsen): Consolidate with a similar method in
-// BluetoothDetailedView (see https://crbug.com/686924).
-void SetupConnectedItem(HoverHighlightView* container,
-                        const base::string16& text,
-                        const gfx::ImageSkia& image) {
-  container->AddIconAndLabels(
-      image, text,
-      l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_NETWORK_STATUS_CONNECTED));
-  TrayPopupItemStyle style(TrayPopupItemStyle::FontStyle::CAPTION);
-  style.set_color_style(TrayPopupItemStyle::ColorStyle::CONNECTED);
-  style.SetupLabel(container->sub_text_label());
-}
-
-// TODO(varkha|mohsen): Consolidate with a similar method in
-// BluetoothDetailedView (see https://crbug.com/686924).
-void SetupConnectingItem(HoverHighlightView* container,
-                         const base::string16& text,
-                         const gfx::ImageSkia& image) {
-  container->AddIconAndLabels(
-      image, text,
-      l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_NETWORK_STATUS_CONNECTING));
-}
-
 }  // namespace
 
 // A header row for sections in network detailed view which contains a title and
@@ -612,35 +589,18 @@
   return new_guids;
 }
 
-HoverHighlightView* NetworkListView::CreateViewForNetwork(
-    const NetworkInfo& info) {
-  HoverHighlightView* container = new HoverHighlightView(this);
-  if (info.connected)
-    SetupConnectedItem(container, info.label, info.image);
-  else if (info.connecting)
-    SetupConnectingItem(container, info.label, info.image);
-  else
-    container->AddIconAndLabel(info.image, info.label);
-  container->SetTooltipText(info.tooltip);
-  views::View* controlled_icon = CreateControlledByExtensionView(info);
-  if (controlled_icon)
-    container->AddRightView(controlled_icon);
-  return container;
-}
-
 void NetworkListView::UpdateViewForNetwork(HoverHighlightView* view,
                                            const NetworkInfo& info) {
-  DCHECK(!view->is_populated());
+  view->Reset();
+  view->AddIconAndLabel(info.image, info.label);
   if (info.connected)
-    SetupConnectedItem(view, info.label, info.image);
+    SetupConnectedScrollListItem(view);
   else if (info.connecting)
-    SetupConnectingItem(view, info.label, info.image);
-  else
-    view->AddIconAndLabel(info.image, info.label);
-  views::View* controlled_icon = CreateControlledByExtensionView(info);
+    SetupConnectingScrollListItem(view);
   view->SetTooltipText(info.tooltip);
+  views::View* controlled_icon = CreateControlledByExtensionView(info);
   if (controlled_icon)
-    view->AddChildView(controlled_icon);
+    view->AddRightView(controlled_icon);
 }
 
 views::View* NetworkListView::CreateControlledByExtensionView(
@@ -681,15 +641,12 @@
   HoverHighlightView* network_view = nullptr;
   NetworkGuidMap::const_iterator found = network_guid_map_.find(info->guid);
   if (found == network_guid_map_.end()) {
-    network_view = CreateViewForNetwork(*info);
+    network_view = new HoverHighlightView(this);
+    UpdateViewForNetwork(network_view, *info);
   } else {
     network_view = found->second;
-    if (NeedUpdateViewForNetwork(*info)) {
-      network_view->Reset();
+    if (NeedUpdateViewForNetwork(*info))
       UpdateViewForNetwork(network_view, *info);
-      network_view->Layout();
-      network_view->SchedulePaint();
-    }
   }
   PlaceViewAtIndex(network_view, index);
   if (info->disable)
@@ -702,6 +659,7 @@
   if (view->parent() != scroll_content()) {
     scroll_content()->AddChildViewAt(view, index);
   } else {
+    // No re-order and re-layout is necessary if |view| is already at |index|.
     if (scroll_content()->child_at(index) == view)
       return;
     scroll_content()->ReorderChildView(view, index);
diff --git a/ash/system/network/network_list.h b/ash/system/network/network_list.h
index ba6beed..98436f3 100644
--- a/ash/system/network/network_list.h
+++ b/ash/system/network/network_list.h
@@ -71,11 +71,7 @@
   // being used.
   TriView* CreateConnectionWarning();
 
-  // Creates and returns a View with the information in |info|.
-  HoverHighlightView* CreateViewForNetwork(const NetworkInfo& info);
-
-  // Updates |view| with the information in |info|. Note that |view| is
-  // guaranteed to be a View returned from |CreateViewForNetwork()|.
+  // Updates |view| with the information in |info|.
   void UpdateViewForNetwork(HoverHighlightView* view, const NetworkInfo& info);
 
   // Creates the view of an extra icon appearing next to the network name
diff --git a/ash/system/network/vpn_list_view.cc b/ash/system/network/vpn_list_view.cc
index 5714e65..1a1625e 100644
--- a/ash/system/network/vpn_list_view.cc
+++ b/ash/system/network/vpn_list_view.cc
@@ -120,7 +120,7 @@
 class VPNListNetworkEntry : public HoverHighlightView,
                             public network_icon::AnimationObserver {
  public:
-  VPNListNetworkEntry(ViewClickListener* listener,
+  VPNListNetworkEntry(VPNListView* vpn_list_view,
                       const chromeos::NetworkState* network);
   ~VPNListNetworkEntry() override;
 
@@ -132,11 +132,8 @@
 
  private:
   void UpdateFromNetworkState(const chromeos::NetworkState* network);
-  void SetupConnectedItem(const base::string16& text,
-                          const gfx::ImageSkia& image);
-  void SetupConnectingItem(const base::string16& text,
-                           const gfx::ImageSkia& image);
 
+  VPNListView* const owner_;
   const std::string guid_;
 
   views::LabelButton* disconnect_button_ = nullptr;
@@ -144,9 +141,9 @@
   DISALLOW_COPY_AND_ASSIGN(VPNListNetworkEntry);
 };
 
-VPNListNetworkEntry::VPNListNetworkEntry(ViewClickListener* listener,
+VPNListNetworkEntry::VPNListNetworkEntry(VPNListView* owner,
                                          const chromeos::NetworkState* network)
-    : HoverHighlightView(listener), guid_(network->guid()) {
+    : HoverHighlightView(owner), owner_(owner), guid_(network->guid()) {
   UpdateFromNetworkState(network);
 }
 
@@ -189,46 +186,24 @@
       network_icon::GetImageForNetwork(network, network_icon::ICON_TYPE_LIST);
   base::string16 label = network_icon::GetLabelForNetwork(
       network, network_icon::ICON_TYPE_MENU_LIST);
-  if (network->IsConnectedState())
-    SetupConnectedItem(label, image);
-  else if (network->IsConnectingState())
-    SetupConnectingItem(label, image);
-  else
-    AddIconAndLabel(image, label);
-
+  AddIconAndLabel(image, label);
   if (network->IsConnectedState()) {
+    owner_->SetupConnectedScrollListItem(this);
     disconnect_button_ = TrayPopupUtils::CreateTrayPopupButton(
         this, l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_VPN_DISCONNECT));
-    tri_view()->AddView(TriView::Container::END, disconnect_button_);
-    tri_view()->SetContainerVisible(TriView::Container::END, true);
+    AddRightView(disconnect_button_);
     tri_view()->SetContainerBorder(
         TriView::Container::END,
-        views::CreateEmptyBorder(0, 0, 0, kTrayPopupButtonEndMargin));
+        views::CreateEmptyBorder(
+            0, kTrayPopupButtonEndMargin - kTrayPopupLabelHorizontalPadding, 0,
+            kTrayPopupButtonEndMargin));
+  } else if (network->IsConnectingState()) {
+    owner_->SetupConnectingScrollListItem(this);
   }
+
   Layout();
 }
 
-// TODO(varkha|mohsen): Consolidate with a similar method in
-// BluetoothDetailedView. See https://crbug.com/686924.
-void VPNListNetworkEntry::SetupConnectedItem(const base::string16& text,
-                                             const gfx::ImageSkia& image) {
-  AddIconAndLabels(
-      image, text,
-      l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_NETWORK_STATUS_CONNECTED));
-  TrayPopupItemStyle style(TrayPopupItemStyle::FontStyle::CAPTION);
-  style.set_color_style(TrayPopupItemStyle::ColorStyle::CONNECTED);
-  style.SetupLabel(sub_text_label());
-}
-
-// TODO(varkha|mohsen): Consolidate with a similar method in
-// BluetoothDetailedView. See https://crbug.com/686924.
-void VPNListNetworkEntry::SetupConnectingItem(const base::string16& text,
-                                              const gfx::ImageSkia& image) {
-  AddIconAndLabels(
-      image, text,
-      l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_NETWORK_STATUS_CONNECTING));
-}
-
 }  // namespace
 
 VPNListView::VPNListView(SystemTrayItem* owner, LoginStatus login)
diff --git a/ash/system/network/vpn_list_view.h b/ash/system/network/vpn_list_view.h
index 8b3a2b86..ef52389 100644
--- a/ash/system/network/vpn_list_view.h
+++ b/ash/system/network/vpn_list_view.h
@@ -43,6 +43,10 @@
   VPNListView(SystemTrayItem* owner, LoginStatus login);
   ~VPNListView() override;
 
+  // Make following functions publicly accessible for VPNListNetworkEntry.
+  using NetworkStateListDetailedView::SetupConnectedScrollListItem;
+  using NetworkStateListDetailedView::SetupConnectingScrollListItem;
+
   // NetworkStateListDetailedView:
   void UpdateNetworkList() override;
   bool IsNetworkEntry(views::View* view, std::string* guid) const override;
diff --git a/ash/system/tray/tray_details_view.cc b/ash/system/tray/tray_details_view.cc
index b0e6690d..f334fc5 100644
--- a/ash/system/tray/tray_details_view.cc
+++ b/ash/system/tray/tray_details_view.cc
@@ -338,6 +338,23 @@
   return AddScrollListCheckableItem(gfx::kNoneIcon, text, checked);
 }
 
+void TrayDetailsView::SetupConnectedScrollListItem(HoverHighlightView* view) {
+  DCHECK(view->is_populated());
+
+  view->SetSubText(
+      l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_NETWORK_STATUS_CONNECTED));
+  TrayPopupItemStyle style(TrayPopupItemStyle::FontStyle::CAPTION);
+  style.set_color_style(TrayPopupItemStyle::ColorStyle::CONNECTED);
+  style.SetupLabel(view->sub_text_label());
+}
+
+void TrayDetailsView::SetupConnectingScrollListItem(HoverHighlightView* view) {
+  DCHECK(view->is_populated());
+
+  view->SetSubText(
+      l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_NETWORK_STATUS_CONNECTING));
+}
+
 TriView* TrayDetailsView::AddScrollListSubHeader(const gfx::VectorIcon& icon,
                                                  int text_id) {
   TriView* header = TrayPopupUtils::CreateSubHeaderRowView(!icon.is_empty());
diff --git a/ash/system/tray/tray_details_view.h b/ash/system/tray/tray_details_view.h
index 1822067..3761ba7 100644
--- a/ash/system/tray/tray_details_view.h
+++ b/ash/system/tray/tray_details_view.h
@@ -85,6 +85,12 @@
   HoverHighlightView* AddScrollListCheckableItem(const base::string16& text,
                                                  bool checked);
 
+  // Adds connected sub label to the |view| with appropriate style.
+  void SetupConnectedScrollListItem(HoverHighlightView* view);
+
+  // Adds connecting sub label to the |view| with appropriate style.
+  void SetupConnectingScrollListItem(HoverHighlightView* view);
+
   // Adds a sticky sub header to |scroll_content_| containing |icon| and a text
   // represented by |text_id| resource id.
   TriView* AddScrollListSubHeader(const gfx::VectorIcon& icon, int text_id);
diff --git a/ash/test/test_shell_delegate.cc b/ash/test/test_shell_delegate.cc
index 204b3a3d..329897d4 100644
--- a/ash/test/test_shell_delegate.cc
+++ b/ash/test/test_shell_delegate.cc
@@ -79,7 +79,7 @@
   return false;
 }
 
-bool TestShellDelegate::CanShowWindowForUser(WmWindow* window) const {
+bool TestShellDelegate::CanShowWindowForUser(aura::Window* window) const {
   return true;
 }
 
diff --git a/ash/test/test_shell_delegate.h b/ash/test/test_shell_delegate.h
index ec26985..23cadccbc 100644
--- a/ash/test/test_shell_delegate.h
+++ b/ash/test/test_shell_delegate.h
@@ -41,7 +41,7 @@
   bool IsIncognitoAllowed() const override;
   bool IsMultiProfilesEnabled() const override;
   bool IsRunningInForcedAppMode() const override;
-  bool CanShowWindowForUser(WmWindow* window) const override;
+  bool CanShowWindowForUser(aura::Window* window) const override;
   bool IsForceMaximizeOnFirstRun() const override;
   void PreInit() override;
   void PreShutdown() override;
diff --git a/ash/wm/focus_rules.cc b/ash/wm/focus_rules.cc
index 519bc0b..b1ca005 100644
--- a/ash/wm/focus_rules.cc
+++ b/ash/wm/focus_rules.cc
@@ -8,7 +8,6 @@
 #include "ash/shell.h"
 #include "ash/shell_delegate.h"
 #include "ash/wm/window_state.h"
-#include "ash/wm_window.h"
 #include "ui/aura/window.h"
 
 namespace ash {
@@ -38,8 +37,7 @@
   DCHECK(window);
   // If the |window| doesn't belong to the current active user and also doesn't
   // show for the current active user, then it should not be activated.
-  if (!Shell::Get()->shell_delegate()->CanShowWindowForUser(
-          WmWindow::Get(window)))
+  if (!Shell::Get()->shell_delegate()->CanShowWindowForUser(window))
     return false;
 
   if (window->IsVisible())
diff --git a/ash/wm/overview/cleanup_animation_observer_unittest.cc b/ash/wm/overview/cleanup_animation_observer_unittest.cc
index 1cfc762..b0542b5c 100644
--- a/ash/wm/overview/cleanup_animation_observer_unittest.cc
+++ b/ash/wm/overview/cleanup_animation_observer_unittest.cc
@@ -8,7 +8,7 @@
 
 #include "ash/test/ash_test_base.h"
 #include "ash/wm/overview/window_selector_delegate.h"
-#include "ash/wm_window.h"
+#include "ui/aura/window.h"
 #include "ui/compositor/layer_animation_observer.h"
 #include "ui/compositor/scoped_animation_duration_scale_mode.h"
 #include "ui/compositor/scoped_layer_animation_settings.h"
@@ -124,10 +124,10 @@
   TestWindowSelectorDelegate delegate;
   std::unique_ptr<views::Widget> widget(
       CreateWindowWidget(gfx::Rect(0, 0, 40, 40)));
-  WmWindow* widget_window = WmWindow::Get(widget->GetNativeWindow());
+  aura::Window* widget_window = widget->GetNativeWindow();
   {
     ui::ScopedLayerAnimationSettings animation_settings(
-        widget_window->GetLayer()->GetAnimator());
+        widget_window->layer()->GetAnimator());
     animation_settings.SetTransitionDuration(
         base::TimeDelta::FromMilliseconds(1000));
     animation_settings.SetPreemptionStrategy(
@@ -155,14 +155,14 @@
   TestWindowSelectorDelegate delegate;
   std::unique_ptr<views::Widget> widget(
       CreateWindowWidget(gfx::Rect(0, 0, 40, 40)));
-  WmWindow* widget_window = WmWindow::Get(widget->GetNativeWindow());
+  aura::Window* widget_window = widget->GetNativeWindow();
   {
     // Normal animations for tests have ZERO_DURATION, make sure we are actually
     // animating the movement.
     ui::ScopedAnimationDurationScaleMode animation_scale_mode(
         ui::ScopedAnimationDurationScaleMode::NON_ZERO_DURATION);
     ui::ScopedLayerAnimationSettings animation_settings(
-        widget_window->GetLayer()->GetAnimator());
+        widget_window->layer()->GetAnimator());
     animation_settings.SetTransitionDuration(
         base::TimeDelta::FromMilliseconds(1000));
     animation_settings.SetPreemptionStrategy(
diff --git a/ash/wm/overview/scoped_transform_overview_window.cc b/ash/wm/overview/scoped_transform_overview_window.cc
index 5186465..933c8b4 100644
--- a/ash/wm/overview/scoped_transform_overview_window.cc
+++ b/ash/wm/overview/scoped_transform_overview_window.cc
@@ -13,7 +13,6 @@
 #include "ash/wm/window_mirror_view.h"
 #include "ash/wm/window_state.h"
 #include "ash/wm/window_util.h"
-#include "ash/wm_window.h"
 #include "base/macros.h"
 #include "base/memory/ptr_util.h"
 #include "base/single_thread_task_runner.h"
@@ -434,9 +433,9 @@
 }
 
 void ScopedTransformOverviewWindow::CloseWidget() {
-  WmWindow* parent_window = WmWindow::Get(GetTransientRoot(window_));
+  aura::Window* parent_window = GetTransientRoot(window_);
   if (parent_window)
-    parent_window->CloseWidget();
+    wm::CloseWidgetForWindow(parent_window);
 }
 
 // static
diff --git a/ash/wm/overview/window_grid.cc b/ash/wm/overview/window_grid.cc
index a8bdebf..e1448593 100644
--- a/ash/wm/overview/window_grid.cc
+++ b/ash/wm/overview/window_grid.cc
@@ -22,7 +22,6 @@
 #include "ash/wm/overview/window_selector_delegate.h"
 #include "ash/wm/overview/window_selector_item.h"
 #include "ash/wm/window_state.h"
-#include "ash/wm_window.h"
 #include "base/command_line.h"
 #include "base/i18n/string_search.h"
 #include "base/memory/ptr_util.h"
diff --git a/ash/wm/overview/window_selector.cc b/ash/wm/overview/window_selector.cc
index 6ca3edb..78a978d 100644
--- a/ash/wm/overview/window_selector.cc
+++ b/ash/wm/overview/window_selector.cc
@@ -27,7 +27,6 @@
 #include "ash/wm/switchable_windows.h"
 #include "ash/wm/window_state.h"
 #include "ash/wm/window_util.h"
-#include "ash/wm_window.h"
 #include "base/auto_reset.h"
 #include "base/command_line.h"
 #include "base/metrics/histogram_macros.h"
diff --git a/ash/wm/overview/window_selector_controller.cc b/ash/wm/overview/window_selector_controller.cc
index 80efd79..19b12afa 100644
--- a/ash/wm/overview/window_selector_controller.cc
+++ b/ash/wm/overview/window_selector_controller.cc
@@ -13,7 +13,6 @@
 #include "ash/wm/overview/window_selector.h"
 #include "ash/wm/screen_pinning_controller.h"
 #include "ash/wm/window_state.h"
-#include "ash/wm_window.h"
 #include "base/metrics/histogram_macros.h"
 
 namespace ash {
diff --git a/ash/wm/overview/window_selector_item.cc b/ash/wm/overview/window_selector_item.cc
index 60eadcd..46b9696 100644
--- a/ash/wm/overview/window_selector_item.cc
+++ b/ash/wm/overview/window_selector_item.cc
@@ -20,7 +20,6 @@
 #include "ash/wm/overview/window_selector.h"
 #include "ash/wm/overview/window_selector_controller.h"
 #include "ash/wm/window_state.h"
-#include "ash/wm_window.h"
 #include "base/auto_reset.h"
 #include "base/strings/string_util.h"
 #include "base/strings/utf_string_conversions.h"
diff --git a/ash/wm/overview/window_selector_unittest.cc b/ash/wm/overview/window_selector_unittest.cc
index 0bec0e49..c69caf24 100644
--- a/ash/wm/overview/window_selector_unittest.cc
+++ b/ash/wm/overview/window_selector_unittest.cc
@@ -27,7 +27,6 @@
 #include "ash/wm/window_util.h"
 #include "ash/wm/wm_event.h"
 #include "ash/wm/workspace/workspace_window_resizer.h"
-#include "ash/wm_window.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/test/user_action_tester.h"
@@ -136,10 +135,9 @@
     params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
     widget->Init(params);
     widget->Show();
-    WmWindow* window = WmWindow::Get(widget->GetNativeWindow());
-    window->aura_window()->SetProperty(aura::client::kTopViewInset,
-                                       kHeaderHeight);
-    ParentWindowInPrimaryRootWindow(widget->GetNativeWindow());
+    aura::Window* window = widget->GetNativeWindow();
+    window->SetProperty(aura::client::kTopViewInset, kHeaderHeight);
+    ParentWindowInPrimaryRootWindow(window);
     return widget;
   }
 
@@ -834,9 +832,8 @@
   params.parent = window1->parent();
   widget->Init(params);
   widget->Show();
-  WmWindow* window = WmWindow::Get(widget->GetNativeWindow());
-  window->aura_window()->SetProperty(aura::client::kTopViewInset,
-                                     kHeaderHeight);
+  aura::Window* window = widget->GetNativeWindow();
+  window->SetProperty(aura::client::kTopViewInset, kHeaderHeight);
 
   ASSERT_EQ(root_windows[1], window1->GetRootWindow());
 
diff --git a/ash/wm/panels/panel_layout_manager_unittest.cc b/ash/wm/panels/panel_layout_manager_unittest.cc
index 25c10fa7..30b2b1b0 100644
--- a/ash/wm/panels/panel_layout_manager_unittest.cc
+++ b/ash/wm/panels/panel_layout_manager_unittest.cc
@@ -22,7 +22,6 @@
 #include "ash/wm/mru_window_tracker.h"
 #include "ash/wm/window_state.h"
 #include "ash/wm/window_util.h"
-#include "ash/wm_window.h"
 #include "base/command_line.h"
 #include "base/compiler_specific.h"
 #include "base/i18n/rtl.h"
diff --git a/ash/wm/tablet_mode/tablet_mode_event_handler.cc b/ash/wm/tablet_mode/tablet_mode_event_handler.cc
index 9267e15..73aead1 100644
--- a/ash/wm/tablet_mode/tablet_mode_event_handler.cc
+++ b/ash/wm/tablet_mode/tablet_mode_event_handler.cc
@@ -9,7 +9,6 @@
 #include "ash/wm/window_state.h"
 #include "ash/wm/window_util.h"
 #include "ash/wm/wm_event.h"
-#include "ash/wm_window.h"
 #include "ui/events/event.h"
 
 namespace ash {
@@ -38,18 +37,18 @@
   }
 
   // Find the active window (from the primary screen) to un-fullscreen.
-  WmWindow* window = WmWindow::Get(GetActiveWindow());
+  aura::Window* window = GetActiveWindow();
   if (!window)
     return false;
 
-  WindowState* window_state = window->GetWindowState();
+  WindowState* window_state = GetWindowState(window);
   if (!window_state->IsFullscreen() || window_state->in_immersive_fullscreen())
     return false;
 
   // Test that the touch happened in the top or bottom lines.
   int y = event.y();
   if (y >= kLeaveFullScreenAreaHeightInPixel &&
-      y < (window->GetBounds().height() - kLeaveFullScreenAreaHeightInPixel)) {
+      y < (window->bounds().height() - kLeaveFullScreenAreaHeightInPixel)) {
     return false;
   }
 
@@ -58,7 +57,7 @@
     return false;
 
   WMEvent toggle_fullscreen(WM_EVENT_TOGGLE_FULLSCREEN);
-  window->GetWindowState()->OnWMEvent(&toggle_fullscreen);
+  GetWindowState(window)->OnWMEvent(&toggle_fullscreen);
   return true;
 }
 
diff --git a/ash/wm/tablet_mode/tablet_mode_window_manager_unittest.cc b/ash/wm/tablet_mode/tablet_mode_window_manager_unittest.cc
index 4413dc5..e59ffbb 100644
--- a/ash/wm/tablet_mode/tablet_mode_window_manager_unittest.cc
+++ b/ash/wm/tablet_mode/tablet_mode_window_manager_unittest.cc
@@ -24,7 +24,6 @@
 #include "ash/wm/window_state_observer.h"
 #include "ash/wm/window_util.h"
 #include "ash/wm/wm_event.h"
-#include "ash/wm_window.h"
 #include "base/command_line.h"
 #include "base/strings/stringprintf.h"
 #include "base/strings/utf_string_conversions.h"
diff --git a/ash/wm/tablet_mode/tablet_mode_window_state.cc b/ash/wm/tablet_mode/tablet_mode_window_state.cc
index d3ba65f8..ab0e7bc 100644
--- a/ash/wm/tablet_mode/tablet_mode_window_state.cc
+++ b/ash/wm/tablet_mode/tablet_mode_window_state.cc
@@ -15,7 +15,6 @@
 #include "ash/wm/window_properties.h"
 #include "ash/wm/window_state_util.h"
 #include "ash/wm/wm_event.h"
-#include "ash/wm_window.h"
 #include "ui/aura/window.h"
 #include "ui/aura/window_delegate.h"
 #include "ui/compositor/layer.h"
diff --git a/ash/wm/window_util.cc b/ash/wm/window_util.cc
index 0ef0d81..6c033a6 100644
--- a/ash/wm/window_util.cc
+++ b/ash/wm/window_util.cc
@@ -7,16 +7,21 @@
 #include <vector>
 
 #include "ash/ash_constants.h"
+#include "ash/public/cpp/config.h"
 #include "ash/root_window_controller.h"
 #include "ash/shelf/shelf.h"
 #include "ash/shell.h"
 #include "ash/shell_port.h"
+#include "ash/wm/widget_finder.h"
 #include "ash/wm/window_properties.h"
 #include "ash/wm/window_state.h"
 #include "ash/wm/wm_event.h"
 #include "ui/aura/client/aura_constants.h"
 #include "ui/aura/client/capture_client.h"
 #include "ui/aura/client/focus_client.h"
+#include "ui/aura/mus/window_manager_delegate.h"
+#include "ui/aura/mus/window_port_mus.h"
+#include "ui/aura/mus/window_tree_client.h"
 #include "ui/aura/window.h"
 #include "ui/aura/window_delegate.h"
 #include "ui/aura/window_event_dispatcher.h"
@@ -140,5 +145,35 @@
              : HTNOWHERE;
 }
 
+void CloseWidgetForWindow(aura::Window* window) {
+  if (Shell::GetAshConfig() == Config::MASH &&
+      window->GetProperty(kWidgetCreationTypeKey) ==
+          WidgetCreationType::FOR_CLIENT) {
+    // NOTE: in the FOR_CLIENT case there is not necessarily a widget associated
+    // with the window. Mash only creates widgets for top level windows if mash
+    // renders the non-client frame.
+    DCHECK(Shell::window_manager_client());
+    Shell::window_manager_client()->RequestClose(window);
+    return;
+  }
+  views::Widget* widget = GetInternalWidgetForWindow(window);
+  DCHECK(widget);
+  widget->Close();
+}
+
+void AddLimitedPreTargetHandlerForWindow(ui::EventHandler* handler,
+                                         aura::Window* window) {
+  // In mus AddPreTargetHandler() only works for windows created by this client.
+  DCHECK(Shell::GetAshConfig() != Config::MASH ||
+         Shell::window_tree_client()->WasCreatedByThisClient(
+             aura::WindowMus::Get(window)));
+  window->AddPreTargetHandler(handler);
+}
+
+void RemoveLimitedPreTargetHandlerForWindow(ui::EventHandler* handler,
+                                            aura::Window* window) {
+  window->RemovePreTargetHandler(handler);
+}
+
 }  // namespace wm
 }  // namespace ash
diff --git a/ash/wm/window_util.h b/ash/wm/window_util.h
index d1b95504..1697eebc 100644
--- a/ash/wm/window_util.h
+++ b/ash/wm/window_util.h
@@ -21,6 +21,7 @@
 
 namespace ui {
 class Event;
+class EventHandler;
 }
 
 namespace ash {
@@ -76,6 +77,23 @@
 ASH_EXPORT int GetNonClientComponent(aura::Window* window,
                                      const gfx::Point& location);
 
+// Requests the |window| to close and destroy itself. This is intended to
+// forward to an associated widget.
+ASH_EXPORT void CloseWidgetForWindow(aura::Window* window);
+
+// Adds or removes a handler to receive events targeted at this window, before
+// this window handles the events itself; the handler does not receive events
+// from embedded windows. This only supports windows with internal widgets;
+// see ash::GetInternalWidgetForWindow(). Ownership of the handler is not
+// transferred.
+//
+// Also note that the target of these events is always an aura::Window.
+ASH_EXPORT void AddLimitedPreTargetHandlerForWindow(ui::EventHandler* handler,
+                                                    aura::Window* window);
+ASH_EXPORT void RemoveLimitedPreTargetHandlerForWindow(
+    ui::EventHandler* handler,
+    aura::Window* window);
+
 }  // namespace wm
 }  // namespace ash
 
diff --git a/ash/wm/workspace/backdrop_controller.cc b/ash/wm/workspace/backdrop_controller.cc
index b423977..b73113f0 100644
--- a/ash/wm/workspace/backdrop_controller.cc
+++ b/ash/wm/workspace/backdrop_controller.cc
@@ -12,7 +12,6 @@
 #include "ash/system/tray/system_tray_notifier.h"
 #include "ash/wm/window_util.h"
 #include "ash/wm/workspace/backdrop_delegate.h"
-#include "ash/wm_window.h"
 #include "base/auto_reset.h"
 #include "chromeos/audio/chromeos_sounds.h"
 #include "ui/aura/client/aura_constants.h"
diff --git a/ash/wm/workspace/multi_window_resize_controller_unittest.cc b/ash/wm/workspace/multi_window_resize_controller_unittest.cc
index 5d96e5e2..c79504c 100644
--- a/ash/wm/workspace/multi_window_resize_controller_unittest.cc
+++ b/ash/wm/workspace/multi_window_resize_controller_unittest.cc
@@ -13,7 +13,6 @@
 #include "ash/test/workspace_event_handler_test_helper.h"
 #include "ash/wm/window_util.h"
 #include "ash/wm/workspace_controller.h"
-#include "ash/wm_window.h"
 #include "base/stl_util.h"
 #include "ui/aura/test/test_window_delegate.h"
 #include "ui/aura/window.h"
diff --git a/ash/wm/workspace/workspace_event_handler_aura.cc b/ash/wm/workspace/workspace_event_handler_aura.cc
index a0113099..55c663a7 100644
--- a/ash/wm/workspace/workspace_event_handler_aura.cc
+++ b/ash/wm/workspace/workspace_event_handler_aura.cc
@@ -4,19 +4,20 @@
 
 #include "ash/wm/workspace/workspace_event_handler_aura.h"
 
-#include "ash/wm_window.h"
+#include "ash/wm/window_util.h"
 #include "ui/aura/window.h"
 #include "ui/events/event.h"
 
 namespace ash {
 
-WorkspaceEventHandlerAura::WorkspaceEventHandlerAura(WmWindow* workspace_window)
+WorkspaceEventHandlerAura::WorkspaceEventHandlerAura(
+    aura::Window* workspace_window)
     : workspace_window_(workspace_window) {
-  workspace_window_->AddLimitedPreTargetHandler(this);
+  wm::AddLimitedPreTargetHandlerForWindow(this, workspace_window_);
 }
 
 WorkspaceEventHandlerAura::~WorkspaceEventHandlerAura() {
-  workspace_window_->RemoveLimitedPreTargetHandler(this);
+  wm::RemoveLimitedPreTargetHandlerForWindow(this, workspace_window_);
 }
 
 void WorkspaceEventHandlerAura::OnMouseEvent(ui::MouseEvent* event) {
diff --git a/ash/wm/workspace/workspace_event_handler_aura.h b/ash/wm/workspace/workspace_event_handler_aura.h
index 86f163d..6215870 100644
--- a/ash/wm/workspace/workspace_event_handler_aura.h
+++ b/ash/wm/workspace/workspace_event_handler_aura.h
@@ -10,14 +10,16 @@
 #include "base/macros.h"
 #include "ui/events/event_handler.h"
 
-namespace ash {
+namespace aura {
+class Window;
+}
 
-class WmWindow;
+namespace ash {
 
 class ASH_EXPORT WorkspaceEventHandlerAura : public ui::EventHandler,
                                              public WorkspaceEventHandler {
  public:
-  explicit WorkspaceEventHandlerAura(WmWindow* workspace_window);
+  explicit WorkspaceEventHandlerAura(aura::Window* workspace_window);
   ~WorkspaceEventHandlerAura() override;
 
   // ui::EventHandler:
@@ -25,7 +27,7 @@
   void OnGestureEvent(ui::GestureEvent* event) override;
 
  private:
-  WmWindow* workspace_window_;
+  aura::Window* workspace_window_;
 
   DISALLOW_COPY_AND_ASSIGN(WorkspaceEventHandlerAura);
 };
diff --git a/ash/wm/workspace/workspace_layout_manager.cc b/ash/wm/workspace/workspace_layout_manager.cc
index 940ce02..fc67e50 100644
--- a/ash/wm/workspace/workspace_layout_manager.cc
+++ b/ash/wm/workspace/workspace_layout_manager.cc
@@ -23,7 +23,6 @@
 #include "ash/wm/wm_event.h"
 #include "ash/wm/workspace/backdrop_controller.h"
 #include "ash/wm/workspace/backdrop_delegate.h"
-#include "ash/wm_window.h"
 #include "base/command_line.h"
 #include "ui/aura/client/aura_constants.h"
 #include "ui/base/ui_base_switches.h"
diff --git a/ash/wm/workspace/workspace_layout_manager_keyboard_unittest.cc b/ash/wm/workspace/workspace_layout_manager_keyboard_unittest.cc
index b604c6d..dcdaaac 100644
--- a/ash/wm/workspace/workspace_layout_manager_keyboard_unittest.cc
+++ b/ash/wm/workspace/workspace_layout_manager_keyboard_unittest.cc
@@ -22,7 +22,6 @@
 #include "ash/wm/window_util.h"
 #include "ash/wm/wm_event.h"
 #include "ash/wm/workspace/workspace_window_resizer.h"
-#include "ash/wm_window.h"
 #include "base/command_line.h"
 #include "ui/base/ui_base_switches.h"
 #include "ui/base/ui_base_types.h"
@@ -64,14 +63,13 @@
     restore_work_area_insets_ =
         display::Screen::GetScreen()->GetPrimaryDisplay().GetWorkAreaInsets();
     ShellPort::Get()->SetDisplayWorkAreaInsets(
-        WmWindow::Get(Shell::GetPrimaryRootWindow()),
+        Shell::GetPrimaryRootWindow(),
         gfx::Insets(0, 0, keyboard_bounds_.height(), 0));
   }
 
   void HideKeyboard() {
-    ShellPort::Get()->SetDisplayWorkAreaInsets(
-        WmWindow::Get(Shell::GetPrimaryRootWindow()),
-        restore_work_area_insets_);
+    ShellPort::Get()->SetDisplayWorkAreaInsets(Shell::GetPrimaryRootWindow(),
+                                               restore_work_area_insets_);
     layout_manager_->OnKeyboardBoundsChanging(gfx::Rect());
   }
 
diff --git a/ash/wm/workspace/workspace_layout_manager_unittest.cc b/ash/wm/workspace/workspace_layout_manager_unittest.cc
index 6cde773..e237be4 100644
--- a/ash/wm/workspace/workspace_layout_manager_unittest.cc
+++ b/ash/wm/workspace/workspace_layout_manager_unittest.cc
@@ -35,7 +35,6 @@
 #include "ash/wm/wm_event.h"
 #include "ash/wm/workspace/backdrop_delegate.h"
 #include "ash/wm/workspace/workspace_window_resizer.h"
-#include "ash/wm_window.h"
 #include "base/command_line.h"
 #include "base/run_loop.h"
 #include "chromeos/audio/chromeos_sounds.h"
@@ -425,10 +424,10 @@
       nullptr, aura::client::WINDOW_TYPE_NORMAL));
   window->Init(ui::LAYER_TEXTURED);
   wm::GetWindowState(window.get())->Maximize();
-  WmWindow* default_container =
-      Shell::GetPrimaryRootWindowController()->GetWmContainer(
+  aura::Window* default_container =
+      Shell::GetPrimaryRootWindowController()->GetContainer(
           kShellWindowId_DefaultContainer);
-  default_container->aura_window()->AddChild(window.get());
+  default_container->AddChild(window.get());
   window->Show();
   gfx::Rect work_area(
       display::Screen::GetScreen()->GetPrimaryDisplay().work_area());
@@ -577,8 +576,7 @@
       CreateTestWindow(gfx::Rect(10, 20, 100, 200)));
   wm::WindowState* window_state = wm::GetWindowState(window.get());
   gfx::Insets insets(0, 0, 50, 0);
-  ShellPort::Get()->SetDisplayWorkAreaInsets(WmWindow::Get(window.get()),
-                                             insets);
+  ShellPort::Get()->SetDisplayWorkAreaInsets(window.get(), insets);
   const wm::WMEvent snap_left(wm::WM_EVENT_SNAP_LEFT);
   window_state->OnWMEvent(&snap_left);
   EXPECT_EQ(wm::WINDOW_STATE_TYPE_LEFT_SNAPPED, window_state->GetStateType());
@@ -594,12 +592,11 @@
   // The following two SetDisplayWorkAreaInsets calls simulate the case of
   // crbug.com/673803 that work area first becomes fullscreen and then returns
   // to the original state.
-  ShellPort::Get()->SetDisplayWorkAreaInsets(WmWindow::Get(window.get()),
+  ShellPort::Get()->SetDisplayWorkAreaInsets(window.get(),
                                              gfx::Insets(0, 0, 0, 0));
   ui::LayerAnimator* animator = window->layer()->GetAnimator();
   EXPECT_TRUE(animator->is_animating());
-  ShellPort::Get()->SetDisplayWorkAreaInsets(WmWindow::Get(window.get()),
-                                             insets);
+  ShellPort::Get()->SetDisplayWorkAreaInsets(window.get(), insets);
   animator->StopAnimating();
   EXPECT_FALSE(animator->is_animating());
   EXPECT_EQ(expected_bounds.ToString(), window->bounds().ToString());
@@ -623,7 +620,7 @@
   window2->Show();
 
   gfx::Rect expected_bounds = window2->bounds();
-  ShellPort::Get()->SetDisplayWorkAreaInsets(WmWindow::Get(window.get()),
+  ShellPort::Get()->SetDisplayWorkAreaInsets(window.get(),
                                              gfx::Insets(50, 0, 0, 0));
   EXPECT_EQ(expected_bounds.ToString(), window2->bounds().ToString());
 }
@@ -1405,14 +1402,13 @@
     restore_work_area_insets_ =
         display::Screen::GetScreen()->GetPrimaryDisplay().GetWorkAreaInsets();
     ShellPort::Get()->SetDisplayWorkAreaInsets(
-        WmWindow::Get(Shell::GetPrimaryRootWindow()),
+        Shell::GetPrimaryRootWindow(),
         gfx::Insets(0, 0, keyboard_bounds_.height(), 0));
   }
 
   void HideKeyboard() {
-    ShellPort::Get()->SetDisplayWorkAreaInsets(
-        WmWindow::Get(Shell::GetPrimaryRootWindow()),
-        restore_work_area_insets_);
+    ShellPort::Get()->SetDisplayWorkAreaInsets(Shell::GetPrimaryRootWindow(),
+                                               restore_work_area_insets_);
     layout_manager_->OnKeyboardBoundsChanging(gfx::Rect());
   }
 
diff --git a/ash/wm/workspace/workspace_window_resizer_unittest.cc b/ash/wm/workspace/workspace_window_resizer_unittest.cc
index 57c4fdb..c3cc31a3 100644
--- a/ash/wm/workspace/workspace_window_resizer_unittest.cc
+++ b/ash/wm/workspace/workspace_window_resizer_unittest.cc
@@ -17,7 +17,6 @@
 #include "ash/wm/wm_event.h"
 #include "ash/wm/workspace/phantom_window_controller.h"
 #include "ash/wm/workspace_controller.h"
-#include "ash/wm_window.h"
 #include "base/command_line.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/stringprintf.h"
diff --git a/ash/wm/workspace_controller.cc b/ash/wm/workspace_controller.cc
index 4687dfc..8017164f 100644
--- a/ash/wm/workspace_controller.cc
+++ b/ash/wm/workspace_controller.cc
@@ -17,10 +17,10 @@
 #include "ash/wm/workspace/backdrop_delegate.h"
 #include "ash/wm/workspace/workspace_event_handler.h"
 #include "ash/wm/workspace/workspace_layout_manager.h"
-#include "ash/wm_window.h"
 #include "ui/aura/window.h"
 #include "ui/compositor/layer.h"
 #include "ui/compositor/scoped_layer_animation_settings.h"
+#include "ui/wm/core/window_animations.h"
 
 namespace ash {
 namespace {
@@ -36,8 +36,7 @@
 
 WorkspaceController::WorkspaceController(aura::Window* viewport)
     : viewport_(viewport),
-      event_handler_(ShellPort::Get()->CreateWorkspaceEventHandler(
-          WmWindow::Get(viewport))),
+      event_handler_(ShellPort::Get()->CreateWorkspaceEventHandler(viewport)),
       layout_manager_(new WorkspaceLayoutManager(viewport)) {
   viewport_->AddObserver(this);
   ::wm::SetWindowVisibilityAnimationTransition(viewport_, ::wm::ANIMATE_NONE);
diff --git a/ash/wm_window.cc b/ash/wm_window.cc
index 59e78410..256a1ab 100644
--- a/ash/wm_window.cc
+++ b/ash/wm_window.cc
@@ -447,22 +447,6 @@
   window_->Show();
 }
 
-void WmWindow::CloseWidget() {
-  if (Shell::GetAshConfig() == Config::MASH &&
-      aura_window()->GetProperty(kWidgetCreationTypeKey) ==
-          WidgetCreationType::FOR_CLIENT) {
-    // NOTE: in the FOR_CLIENT case there is not necessarily a widget associated
-    // with the window. Mash only creates widgets for top level windows if mash
-    // renders the non-client frame.
-    DCHECK(Shell::window_manager_client());
-    Shell::window_manager_client()->RequestClose(aura_window());
-    return;
-  }
-  views::Widget* widget = GetInternalWidgetForWindow(window_);
-  DCHECK(widget);
-  widget->Close();
-}
-
 void WmWindow::SetFocused() {
   aura::client::GetFocusClient(window_)->FocusWindow(window_);
 }
@@ -565,18 +549,6 @@
   }
 }
 
-void WmWindow::AddLimitedPreTargetHandler(ui::EventHandler* handler) {
-  // In mus AddPreTargetHandler() only works for windows created by this client.
-  DCHECK(Shell::GetAshConfig() != Config::MASH ||
-         Shell::window_tree_client()->WasCreatedByThisClient(
-             aura::WindowMus::Get(window_)));
-  window_->AddPreTargetHandler(handler);
-}
-
-void WmWindow::RemoveLimitedPreTargetHandler(ui::EventHandler* handler) {
-  window_->RemovePreTargetHandler(handler);
-}
-
 WmWindow::WmWindow(aura::Window* window) : window_(window) {
   window_->SetProperty(kWmWindowKey, this);
 }
diff --git a/ash/wm_window.h b/ash/wm_window.h
index e81d8566..2e6595a 100644
--- a/ash/wm_window.h
+++ b/ash/wm_window.h
@@ -31,7 +31,6 @@
 }
 
 namespace ui {
-class EventHandler;
 class Layer;
 }
 
@@ -249,10 +248,6 @@
   void Hide();
   void Show();
 
-  // Requests the window to close and destroy itself. This is intended to
-  // forward to an associated widget.
-  void CloseWidget();
-
   void SetFocused();
   bool IsFocused() const;
 
@@ -290,16 +285,6 @@
   void AddTransientWindowObserver(WmTransientWindowObserver* observer);
   void RemoveTransientWindowObserver(WmTransientWindowObserver* observer);
 
-  // Adds or removes a handler to receive events targeted at this window, before
-  // this window handles the events itself; the handler does not recieve events
-  // from embedded windows. This only supports windows with internal widgets;
-  // see ash::GetInternalWidgetForWindow(). Ownership of the handler is not
-  // transferred.
-  //
-  // Also note that the target of these events is always an aura::Window.
-  void AddLimitedPreTargetHandler(ui::EventHandler* handler);
-  void RemoveLimitedPreTargetHandler(ui::EventHandler* handler);
-
  private:
   explicit WmWindow(aura::Window* window);
 
diff --git a/base/allocator/partition_allocator/partition_alloc_unittest.cc b/base/allocator/partition_allocator/partition_alloc_unittest.cc
index 0507c0b4..77c3823 100644
--- a/base/allocator/partition_allocator/partition_alloc_unittest.cc
+++ b/base/allocator/partition_allocator/partition_alloc_unittest.cc
@@ -20,62 +20,34 @@
 #include <sys/resource.h>
 #include <sys/time.h>
 
-#ifndef MAP_ANONYMOUS
-#define MAP_ANONYMOUS MAP_ANON
-#endif
 #endif  // defined(OS_POSIX)
 
+#if !defined(MEMORY_TOOL_REPLACES_ALLOCATOR)
+
 namespace {
+
 template <typename T>
 std::unique_ptr<T[]> WrapArrayUnique(T* ptr) {
   return std::unique_ptr<T[]>(ptr);
 }
-}  // namespace
-
-#if !defined(MEMORY_TOOL_REPLACES_ALLOCATOR)
-
-namespace base {
-
-namespace {
 
 const size_t kTestMaxAllocation = 4096;
-SizeSpecificPartitionAllocator<kTestMaxAllocation> allocator;
-PartitionAllocatorGeneric generic_allocator;
 
-const size_t kTestAllocSize = 16;
-#if !DCHECK_IS_ON()
-const size_t kPointerOffset = 0;
-const size_t kExtraAllocSize = 0;
-#else
-const size_t kPointerOffset = kCookieSize;
-const size_t kExtraAllocSize = kCookieSize * 2;
-#endif
-const size_t kRealAllocSize = kTestAllocSize + kExtraAllocSize;
-const size_t kTestBucketIndex = kRealAllocSize >> kBucketShift;
-
-const char* type_name = nullptr;
-
-void TestSetup() {
-  // Zero the allocator structs to clear out traces
-  // from previous test.
-  memset(&allocator, 0, sizeof(allocator));
-  memset(&generic_allocator, 0, sizeof(generic_allocator));
-
-  allocator.init();
-  generic_allocator.init();
+bool IsLargeMemoryDevice() {
+  return base::SysInfo::AmountOfPhysicalMemory() >= 2LL * 1024 * 1024 * 1024;
 }
 
-#if !defined(ARCH_CPU_64_BITS) || defined(OS_POSIX)
 bool SetAddressSpaceLimit() {
-#if !defined(ARCH_CPU_64_BITS)
+#if !defined(ARCH_CPU_64_BITS) || !defined(OS_POSIX)
   // 32 bits => address space is limited already.
   return true;
 #elif defined(OS_POSIX) && !defined(OS_MACOSX)
-  // Mac will accept RLIMIT_AS changes but it is not enforced.
-  // See https://crbug.com/435269 and rdar://17576114.
-  // Note: this number must be not less than 6 GB, because with
-  // sanitizer_coverage_flags=edge, it reserves > 5 GB of address
-  // space, see https://crbug.com/674665.
+  // macOS will accept, but not enforce, |RLIMIT_AS| changes. See
+  // https://crbug.com/435269 and rdar://17576114.
+  //
+  // Note: This number must be not less than 6 GB, because with
+  // sanitizer_coverage_flags=edge, it reserves > 5 GB of address space. See
+  // https://crbug.com/674665.
   const size_t kAddressSpaceLimit = static_cast<size_t>(6144) * 1024 * 1024;
   struct rlimit limit;
   if (getrlimit(RLIMIT_AS, &limit) != 0)
@@ -92,7 +64,7 @@
 }
 
 bool ClearAddressSpaceLimit() {
-#if !defined(ARCH_CPU_64_BITS)
+#if !defined(ARCH_CPU_64_BITS) || !defined(OS_POSIX)
   return true;
 #elif defined(OS_POSIX)
   struct rlimit limit;
@@ -106,36 +78,159 @@
   return false;
 #endif
 }
-#endif
 
-PartitionPage* GetFullPage(size_t size) {
-  size_t real_size = size + kExtraAllocSize;
-  size_t bucket_index = real_size >> kBucketShift;
-  PartitionBucket* bucket = &allocator.root()->buckets()[bucket_index];
-  size_t num_slots =
-      (bucket->num_system_pages_per_slot_span * kSystemPageSize) / real_size;
-  void* first = 0;
-  void* last = 0;
-  size_t i;
-  for (i = 0; i < num_slots; ++i) {
-    void* ptr = PartitionAlloc(allocator.root(), size, type_name);
-    EXPECT_TRUE(ptr);
-    if (!i)
-      first = PartitionCookieFreePointerAdjust(ptr);
-    else if (i == num_slots - 1)
-      last = PartitionCookieFreePointerAdjust(ptr);
+}  // namespace
+
+namespace base {
+
+const size_t kTestAllocSize = 16;
+#if !DCHECK_IS_ON()
+const size_t kPointerOffset = 0;
+const size_t kExtraAllocSize = 0;
+#else
+const size_t kPointerOffset = kCookieSize;
+const size_t kExtraAllocSize = kCookieSize * 2;
+#endif
+const size_t kRealAllocSize = kTestAllocSize + kExtraAllocSize;
+const size_t kTestBucketIndex = kRealAllocSize >> kBucketShift;
+
+const char* type_name = nullptr;
+
+class PartitionAllocTest : public testing::Test {
+ protected:
+  PartitionAllocTest() {}
+
+  ~PartitionAllocTest() override {}
+
+  void SetUp() override {
+    // TODO(crbug.com/722911): These calls to |memset| should perhaps not be
+    // necessary.
+    memset(&allocator, 0, sizeof(allocator));
+    memset(&generic_allocator, 0, sizeof(generic_allocator));
+    allocator.init();
+    generic_allocator.init();
   }
-  EXPECT_EQ(PartitionPointerToPage(first), PartitionPointerToPage(last));
-  if (bucket->num_system_pages_per_slot_span == kNumSystemPagesPerPartitionPage)
-    EXPECT_EQ(reinterpret_cast<size_t>(first) & kPartitionPageBaseMask,
-              reinterpret_cast<size_t>(last) & kPartitionPageBaseMask);
-  EXPECT_EQ(num_slots, static_cast<size_t>(
-                           bucket->active_pages_head->num_allocated_slots));
-  EXPECT_EQ(0, bucket->active_pages_head->freelist_head);
-  EXPECT_TRUE(bucket->active_pages_head);
-  EXPECT_TRUE(bucket->active_pages_head != &PartitionRootGeneric::gSeedPage);
-  return bucket->active_pages_head;
-}
+
+  PartitionPage* GetFullPage(size_t size) {
+    size_t real_size = size + kExtraAllocSize;
+    size_t bucket_index = real_size >> kBucketShift;
+    PartitionBucket* bucket = &allocator.root()->buckets()[bucket_index];
+    size_t num_slots =
+        (bucket->num_system_pages_per_slot_span * kSystemPageSize) / real_size;
+    void* first = 0;
+    void* last = 0;
+    size_t i;
+    for (i = 0; i < num_slots; ++i) {
+      void* ptr = PartitionAlloc(allocator.root(), size, type_name);
+      EXPECT_TRUE(ptr);
+      if (!i)
+        first = PartitionCookieFreePointerAdjust(ptr);
+      else if (i == num_slots - 1)
+        last = PartitionCookieFreePointerAdjust(ptr);
+    }
+    EXPECT_EQ(PartitionPointerToPage(first), PartitionPointerToPage(last));
+    if (bucket->num_system_pages_per_slot_span ==
+        kNumSystemPagesPerPartitionPage)
+      EXPECT_EQ(reinterpret_cast<size_t>(first) & kPartitionPageBaseMask,
+                reinterpret_cast<size_t>(last) & kPartitionPageBaseMask);
+    EXPECT_EQ(num_slots, static_cast<size_t>(
+                             bucket->active_pages_head->num_allocated_slots));
+    EXPECT_EQ(0, bucket->active_pages_head->freelist_head);
+    EXPECT_TRUE(bucket->active_pages_head);
+    EXPECT_TRUE(bucket->active_pages_head != &PartitionRootGeneric::gSeedPage);
+    return bucket->active_pages_head;
+  }
+
+  void CycleFreeCache(size_t size) {
+    size_t real_size = size + kExtraAllocSize;
+    size_t bucket_index = real_size >> kBucketShift;
+    PartitionBucket* bucket = &allocator.root()->buckets()[bucket_index];
+    DCHECK(!bucket->active_pages_head->num_allocated_slots);
+
+    for (size_t i = 0; i < kMaxFreeableSpans; ++i) {
+      void* ptr = PartitionAlloc(allocator.root(), size, type_name);
+      EXPECT_EQ(1, bucket->active_pages_head->num_allocated_slots);
+      PartitionFree(ptr);
+      EXPECT_EQ(0, bucket->active_pages_head->num_allocated_slots);
+      EXPECT_NE(-1, bucket->active_pages_head->empty_cache_index);
+    }
+  }
+
+  void CycleGenericFreeCache(size_t size) {
+    for (size_t i = 0; i < kMaxFreeableSpans; ++i) {
+      void* ptr =
+          PartitionAllocGeneric(generic_allocator.root(), size, type_name);
+      PartitionPage* page =
+          PartitionPointerToPage(PartitionCookieFreePointerAdjust(ptr));
+      PartitionBucket* bucket = page->bucket;
+      EXPECT_EQ(1, bucket->active_pages_head->num_allocated_slots);
+      PartitionFreeGeneric(generic_allocator.root(), ptr);
+      EXPECT_EQ(0, bucket->active_pages_head->num_allocated_slots);
+      EXPECT_NE(-1, bucket->active_pages_head->empty_cache_index);
+    }
+  }
+
+  void DoReturnNullTest(size_t allocSize) {
+    // TODO(crbug.com/678782): Where necessary and possible, disable the
+    // platform's OOM-killing behavior. OOM-killing makes this test flaky on
+    // low-memory devices.
+    if (!IsLargeMemoryDevice()) {
+      LOG(WARNING)
+          << "Skipping test on this device because of crbug.com/678782";
+      return;
+    }
+
+    EXPECT_TRUE(SetAddressSpaceLimit());
+
+    // Work out the number of allocations for 6 GB of memory.
+    const int numAllocations = (6 * 1024 * 1024) / (allocSize / 1024);
+
+    void** ptrs = reinterpret_cast<void**>(PartitionAllocGeneric(
+        generic_allocator.root(), numAllocations * sizeof(void*), type_name));
+    int i;
+
+    for (i = 0; i < numAllocations; ++i) {
+      ptrs[i] = PartitionAllocGenericFlags(generic_allocator.root(),
+                                           PartitionAllocReturnNull, allocSize,
+                                           type_name);
+      if (!i)
+        EXPECT_TRUE(ptrs[0]);
+      if (!ptrs[i]) {
+        ptrs[i] = PartitionAllocGenericFlags(generic_allocator.root(),
+                                             PartitionAllocReturnNull,
+                                             allocSize, type_name);
+        EXPECT_FALSE(ptrs[i]);
+        break;
+      }
+    }
+
+    // We shouldn't succeed in allocating all 6 GB of memory. If we do, then
+    // we're not actually testing anything here.
+    EXPECT_LT(i, numAllocations);
+
+    // Free, reallocate and free again each block we allocated. We do this to
+    // check that freeing memory also works correctly after a failed allocation.
+    for (--i; i >= 0; --i) {
+      PartitionFreeGeneric(generic_allocator.root(), ptrs[i]);
+      ptrs[i] = PartitionAllocGenericFlags(generic_allocator.root(),
+                                           PartitionAllocReturnNull, allocSize,
+                                           type_name);
+      EXPECT_TRUE(ptrs[i]);
+      PartitionFreeGeneric(generic_allocator.root(), ptrs[i]);
+    }
+
+    PartitionFreeGeneric(generic_allocator.root(), ptrs);
+
+    EXPECT_TRUE(ClearAddressSpaceLimit());
+  }
+
+  SizeSpecificPartitionAllocator<kTestMaxAllocation> allocator;
+  PartitionAllocatorGeneric generic_allocator;
+};
+
+class PartitionAllocDeathTest : public PartitionAllocTest {};
+
+namespace {
 
 void FreeFullPage(PartitionPage* page) {
   size_t size = page->bucket->slot_size;
@@ -150,35 +245,6 @@
   }
 }
 
-void CycleFreeCache(size_t size) {
-  size_t real_size = size + kExtraAllocSize;
-  size_t bucket_index = real_size >> kBucketShift;
-  PartitionBucket* bucket = &allocator.root()->buckets()[bucket_index];
-  DCHECK(!bucket->active_pages_head->num_allocated_slots);
-
-  for (size_t i = 0; i < kMaxFreeableSpans; ++i) {
-    void* ptr = PartitionAlloc(allocator.root(), size, type_name);
-    EXPECT_EQ(1, bucket->active_pages_head->num_allocated_slots);
-    PartitionFree(ptr);
-    EXPECT_EQ(0, bucket->active_pages_head->num_allocated_slots);
-    EXPECT_NE(-1, bucket->active_pages_head->empty_cache_index);
-  }
-}
-
-void CycleGenericFreeCache(size_t size) {
-  for (size_t i = 0; i < kMaxFreeableSpans; ++i) {
-    void* ptr =
-        PartitionAllocGeneric(generic_allocator.root(), size, type_name);
-    PartitionPage* page =
-        PartitionPointerToPage(PartitionCookieFreePointerAdjust(ptr));
-    PartitionBucket* bucket = page->bucket;
-    EXPECT_EQ(1, bucket->active_pages_head->num_allocated_slots);
-    PartitionFreeGeneric(generic_allocator.root(), ptr);
-    EXPECT_EQ(0, bucket->active_pages_head->num_allocated_slots);
-    EXPECT_NE(-1, bucket->active_pages_head->empty_cache_index);
-  }
-}
-
 void CheckPageInCore(void* ptr, bool inCore) {
 #if defined(OS_LINUX)
   unsigned char ret;
@@ -187,10 +253,6 @@
 #endif
 }
 
-bool IsLargeMemoryDevice() {
-  return base::SysInfo::AmountOfPhysicalMemory() >= 2LL * 1024 * 1024 * 1024;
-}
-
 class MockPartitionStatsDumper : public PartitionStatsDumper {
  public:
   MockPartitionStatsDumper()
@@ -245,8 +307,7 @@
 }  // anonymous namespace
 
 // Check that the most basic of allocate / free pairs work.
-TEST(PartitionAllocTest, Basic) {
-  TestSetup();
+TEST_F(PartitionAllocTest, Basic) {
   PartitionBucket* bucket = &allocator.root()->buckets()[kTestBucketIndex];
   PartitionPage* seedPage = &PartitionRootGeneric::gSeedPage;
 
@@ -271,9 +332,7 @@
 }
 
 // Test multiple allocations, and freelist handling.
-TEST(PartitionAllocTest, MultiAlloc) {
-  TestSetup();
-
+TEST_F(PartitionAllocTest, MultiAlloc) {
   char* ptr1 = reinterpret_cast<char*>(
       PartitionAlloc(allocator.root(), kTestAllocSize, type_name));
   char* ptr2 = reinterpret_cast<char*>(
@@ -309,8 +368,7 @@
 }
 
 // Test a bucket with multiple pages.
-TEST(PartitionAllocTest, MultiPages) {
-  TestSetup();
+TEST_F(PartitionAllocTest, MultiPages) {
   PartitionBucket* bucket = &allocator.root()->buckets()[kTestBucketIndex];
 
   PartitionPage* page = GetFullPage(kTestAllocSize);
@@ -351,8 +409,7 @@
 }
 
 // Test some finer aspects of internal page transitions.
-TEST(PartitionAllocTest, PageTransitions) {
-  TestSetup();
+TEST_F(PartitionAllocTest, PageTransitions) {
   PartitionBucket* bucket = &allocator.root()->buckets()[kTestBucketIndex];
 
   PartitionPage* page1 = GetFullPage(kTestAllocSize);
@@ -412,8 +469,7 @@
 
 // Test some corner cases relating to page transitions in the internal
 // free page list metadata bucket.
-TEST(PartitionAllocTest, FreePageListPageTransitions) {
-  TestSetup();
+TEST_F(PartitionAllocTest, FreePageListPageTransitions) {
   PartitionBucket* bucket = &allocator.root()->buckets()[kTestBucketIndex];
 
   size_t numToFillFreeListPage =
@@ -455,8 +511,7 @@
 
 // Test a large series of allocations that cross more than one underlying
 // 64KB super page allocation.
-TEST(PartitionAllocTest, MultiPageAllocs) {
-  TestSetup();
+TEST_F(PartitionAllocTest, MultiPageAllocs) {
   // This is guaranteed to cross a super page boundary because the first
   // partition page "slot" will be taken up by a guard page.
   size_t numPagesNeeded = kNumPartitionPagesPerSuperPage;
@@ -491,9 +546,7 @@
 
 // Test the generic allocation functions that can handle arbitrary sizes and
 // reallocing etc.
-TEST(PartitionAllocTest, GenericAlloc) {
-  TestSetup();
-
+TEST_F(PartitionAllocTest, GenericAlloc) {
   void* ptr = PartitionAllocGeneric(generic_allocator.root(), 1, type_name);
   EXPECT_TRUE(ptr);
   PartitionFreeGeneric(generic_allocator.root(), ptr);
@@ -585,9 +638,7 @@
 
 // Test the generic allocation functions can handle some specific sizes of
 // interest.
-TEST(PartitionAllocTest, GenericAllocSizes) {
-  TestSetup();
-
+TEST_F(PartitionAllocTest, GenericAllocSizes) {
   void* ptr = PartitionAllocGeneric(generic_allocator.root(), 0, type_name);
   EXPECT_TRUE(ptr);
   PartitionFreeGeneric(generic_allocator.root(), ptr);
@@ -681,9 +732,7 @@
 }
 
 // Test that we can fetch the real allocated size after an allocation.
-TEST(PartitionAllocTest, GenericAllocGetSize) {
-  TestSetup();
-
+TEST_F(PartitionAllocTest, GenericAllocGetSize) {
   void* ptr;
   size_t requestedSize, actualSize, predictedSize;
 
@@ -752,9 +801,7 @@
 }
 
 // Test the realloc() contract.
-TEST(PartitionAllocTest, Realloc) {
-  TestSetup();
-
+TEST_F(PartitionAllocTest, Realloc) {
   // realloc(0, size) should be equivalent to malloc().
   void* ptr = PartitionReallocGeneric(generic_allocator.root(), 0,
                                       kTestAllocSize, type_name);
@@ -824,9 +871,7 @@
 }
 
 // Tests the handing out of freelists for partial pages.
-TEST(PartitionAllocTest, PartialPageFreelists) {
-  TestSetup();
-
+TEST_F(PartitionAllocTest, PartialPageFreelists) {
   size_t big_size = allocator.root()->max_allocation - kExtraAllocSize;
   EXPECT_EQ(kSystemPageSize - kAllocationGranularity,
             big_size + kExtraAllocSize);
@@ -980,8 +1025,7 @@
 }
 
 // Test some of the fragmentation-resistant properties of the allocator.
-TEST(PartitionAllocTest, PageRefilling) {
-  TestSetup();
+TEST_F(PartitionAllocTest, PageRefilling) {
   PartitionBucket* bucket = &allocator.root()->buckets()[kTestBucketIndex];
 
   // Grab two full pages and a non-full page.
@@ -1015,9 +1059,7 @@
 }
 
 // Basic tests to ensure that allocations work for partial page buckets.
-TEST(PartitionAllocTest, PartialPages) {
-  TestSetup();
-
+TEST_F(PartitionAllocTest, PartialPages) {
   // Find a size that is backed by a partial partition page.
   size_t size = sizeof(void*);
   PartitionBucket* bucket = 0;
@@ -1037,8 +1079,7 @@
 }
 
 // Test correct handling if our mapping collides with another.
-TEST(PartitionAllocTest, MappingCollision) {
-  TestSetup();
+TEST_F(PartitionAllocTest, MappingCollision) {
   // The -2 is because the first and last partition pages in a super page are
   // guard pages.
   size_t numPartitionPagesNeeded = kNumPartitionPagesPerSuperPage - 2;
@@ -1119,9 +1160,7 @@
 }
 
 // Tests that pages in the free page cache do get freed as appropriate.
-TEST(PartitionAllocTest, FreeCache) {
-  TestSetup();
-
+TEST_F(PartitionAllocTest, FreeCache) {
   EXPECT_EQ(0U, allocator.root()->total_size_of_committed_pages);
 
   size_t big_size = allocator.root()->max_allocation - kExtraAllocSize;
@@ -1172,9 +1211,7 @@
 }
 
 // Tests for a bug we had with losing references to free pages.
-TEST(PartitionAllocTest, LostFreePagesBug) {
-  TestSetup();
-
+TEST_F(PartitionAllocTest, LostFreePagesBug) {
   size_t size = kPartitionPageSize - kExtraAllocSize;
 
   void* ptr = PartitionAllocGeneric(generic_allocator.root(), size, type_name);
@@ -1236,60 +1273,6 @@
 
 #if !defined(ARCH_CPU_64_BITS) || defined(OS_POSIX)
 
-static void DoReturnNullTest(size_t allocSize) {
-  // TODO(crbug.com/678782): Where necessary and possible, disable the
-  // platform's OOM-killing behavior. OOM-killing makes this test flaky on
-  // low-memory devices.
-  if (!IsLargeMemoryDevice()) {
-    LOG(WARNING) << "Skipping test on this device because of crbug.com/678782";
-    return;
-  }
-
-  TestSetup();
-
-  EXPECT_TRUE(SetAddressSpaceLimit());
-
-  // Work out the number of allocations for 6 GB of memory.
-  const int numAllocations = (6 * 1024 * 1024) / (allocSize / 1024);
-
-  void** ptrs = reinterpret_cast<void**>(PartitionAllocGeneric(
-      generic_allocator.root(), numAllocations * sizeof(void*), type_name));
-  int i;
-
-  for (i = 0; i < numAllocations; ++i) {
-    ptrs[i] = PartitionAllocGenericFlags(generic_allocator.root(),
-                                         PartitionAllocReturnNull, allocSize,
-                                         type_name);
-    if (!i)
-      EXPECT_TRUE(ptrs[0]);
-    if (!ptrs[i]) {
-      ptrs[i] = PartitionAllocGenericFlags(generic_allocator.root(),
-                                           PartitionAllocReturnNull, allocSize,
-                                           type_name);
-      EXPECT_FALSE(ptrs[i]);
-      break;
-    }
-  }
-
-  // We shouldn't succeed in allocating all 6 GB of memory. If we do, then
-  // we're not actually testing anything here.
-  EXPECT_LT(i, numAllocations);
-
-  // Free, reallocate and free again each block we allocated. We do this to
-  // check that freeing memory also works correctly after a failed allocation.
-  for (--i; i >= 0; --i) {
-    PartitionFreeGeneric(generic_allocator.root(), ptrs[i]);
-    ptrs[i] = PartitionAllocGenericFlags(generic_allocator.root(),
-                                         PartitionAllocReturnNull, allocSize,
-                                         type_name);
-    EXPECT_TRUE(ptrs[i]);
-    PartitionFreeGeneric(generic_allocator.root(), ptrs[i]);
-  }
-
-  PartitionFreeGeneric(generic_allocator.root(), ptrs);
-
-  EXPECT_TRUE(ClearAddressSpaceLimit());
-}
 
 // Unit tests that check if an allocation fails in "return null" mode,
 // repeating it doesn't crash, and still returns null. The tests need to
@@ -1311,7 +1294,7 @@
 #else
 #define MAYBE_RepeatedReturnNullDirect RepeatedReturnNullDirect
 #endif
-TEST(PartitionAllocTest, MAYBE_RepeatedReturnNullDirect) {
+TEST_F(PartitionAllocTest, MAYBE_RepeatedReturnNullDirect) {
   // A direct-mapped allocation size.
   DoReturnNullTest(32 * 1024 * 1024);
 }
@@ -1325,7 +1308,7 @@
 #else
 #define MAYBE_RepeatedReturnNull RepeatedReturnNull
 #endif
-TEST(PartitionAllocTest, MAYBE_RepeatedReturnNull) {
+TEST_F(PartitionAllocTest, MAYBE_RepeatedReturnNull) {
   // A single-slot but non-direct-mapped allocation size.
   DoReturnNullTest(512 * 1024);
 }
@@ -1338,8 +1321,7 @@
 // Make sure that malloc(-1) dies.
 // In the past, we had an integer overflow that would alias malloc(-1) to
 // malloc(0), which is not good.
-TEST(PartitionAllocDeathTest, LargeAllocs) {
-  TestSetup();
+TEST_F(PartitionAllocDeathTest, LargeAllocs) {
   // Largest alloc.
   EXPECT_DEATH(PartitionAllocGeneric(generic_allocator.root(),
                                      static_cast<size_t>(-1), type_name),
@@ -1352,9 +1334,7 @@
 }
 
 // Check that our immediate double-free detection works.
-TEST(PartitionAllocDeathTest, ImmediateDoubleFree) {
-  TestSetup();
-
+TEST_F(PartitionAllocDeathTest, ImmediateDoubleFree) {
   void* ptr = PartitionAllocGeneric(generic_allocator.root(), kTestAllocSize,
                                     type_name);
   EXPECT_TRUE(ptr);
@@ -1364,9 +1344,7 @@
 }
 
 // Check that our refcount-based double-free detection works.
-TEST(PartitionAllocDeathTest, RefcountDoubleFree) {
-  TestSetup();
-
+TEST_F(PartitionAllocDeathTest, RefcountDoubleFree) {
   void* ptr = PartitionAllocGeneric(generic_allocator.root(), kTestAllocSize,
                                     type_name);
   EXPECT_TRUE(ptr);
@@ -1382,9 +1360,7 @@
 }
 
 // Check that guard pages are present where expected.
-TEST(PartitionAllocDeathTest, GuardPages) {
-  TestSetup();
-
+TEST_F(PartitionAllocDeathTest, GuardPages) {
 // PartitionAlloc adds kPartitionPageSize to the requested size
 // (for metadata), and then rounds that size to kPageAllocationGranularity.
 // To be able to reliably write one past a direct allocation, choose a size
@@ -1418,9 +1394,7 @@
 
 // Check that a bad free() is caught where the free() refers to an unused
 // partition page of a large allocation.
-TEST(PartitionAllocDeathTest, FreeWrongPartitionPage) {
-  TestSetup();
-
+TEST_F(PartitionAllocDeathTest, FreeWrongPartitionPage) {
   // This large size will result in a direct mapped allocation with guard
   // pages at either end.
   void* ptr = PartitionAllocGeneric(generic_allocator.root(),
@@ -1435,25 +1409,23 @@
 
 #endif  // !defined(OS_ANDROID) && !defined(OS_IOS)
 
-// Tests that PartitionDumpStatsGeneric and PartitionDumpStats runs without
-// crashing and returns non zero values when memory is allocated.
-TEST(PartitionAllocTest, DumpMemoryStats) {
-  TestSetup();
+// Tests that |PartitionDumpStatsGeneric| and |PartitionDumpStats| run without
+// crashing and return non-zero values when memory is allocated.
+TEST_F(PartitionAllocTest, DumpMemoryStats) {
   {
     void* ptr = PartitionAlloc(allocator.root(), kTestAllocSize, type_name);
     MockPartitionStatsDumper mockStatsDumper;
     PartitionDumpStats(allocator.root(), "mock_allocator",
                        false /* detailed dump */, &mockStatsDumper);
     EXPECT_TRUE(mockStatsDumper.IsMemoryAllocationRecorded());
-
     PartitionFree(ptr);
   }
 
   // This series of tests checks the active -> empty -> decommitted states.
   {
-    void* genericPtr = PartitionAllocGeneric(generic_allocator.root(),
-                                             2048 - kExtraAllocSize, type_name);
     {
+      void* ptr = PartitionAllocGeneric(generic_allocator.root(),
+                                        2048 - kExtraAllocSize, type_name);
       MockPartitionStatsDumper dumper;
       PartitionDumpStatsGeneric(generic_allocator.root(),
                                 "mock_generic_allocator",
@@ -1472,10 +1444,9 @@
       EXPECT_EQ(1u, stats->num_active_pages);
       EXPECT_EQ(0u, stats->num_empty_pages);
       EXPECT_EQ(0u, stats->num_decommitted_pages);
+      PartitionFreeGeneric(generic_allocator.root(), ptr);
     }
 
-    PartitionFreeGeneric(generic_allocator.root(), genericPtr);
-
     {
       MockPartitionStatsDumper dumper;
       PartitionDumpStatsGeneric(generic_allocator.root(),
@@ -1497,6 +1468,9 @@
       EXPECT_EQ(0u, stats->num_decommitted_pages);
     }
 
+    // TODO(crbug.com/722911): Commenting this out causes this test to fail when
+    // run singly (--gtest_filter=PartitionAllocTest.DumpMemoryStats), but not
+    // when run with the others (--gtest_filter=PartitionAllocTest.*).
     CycleGenericFreeCache(kTestAllocSize);
 
     {
@@ -1612,8 +1586,8 @@
     PartitionFreeGeneric(generic_allocator.root(), ptr2);
     PartitionFreeGeneric(generic_allocator.root(), ptr);
 
-    // Whilst we're here, allocate again and free with different ordering
-    // to give a workout to our linked list code.
+    // Whilst we're here, allocate again and free with different ordering to
+    // give a workout to our linked list code.
     ptr = PartitionAllocGeneric(generic_allocator.root(), size_smaller,
                                 type_name);
     ptr2 =
@@ -1710,9 +1684,7 @@
 }
 
 // Tests the API to purge freeable memory.
-TEST(PartitionAllocTest, Purge) {
-  TestSetup();
-
+TEST_F(PartitionAllocTest, Purge) {
   char* ptr = reinterpret_cast<char*>(PartitionAllocGeneric(
       generic_allocator.root(), 2048 - kExtraAllocSize, type_name));
   PartitionFreeGeneric(generic_allocator.root(), ptr);
@@ -1762,9 +1734,7 @@
 // Tests that we prefer to allocate into a non-empty partition page over an
 // empty one. This is an important aspect of minimizing memory usage for some
 // allocation sizes, particularly larger ones.
-TEST(PartitionAllocTest, PreferActiveOverEmpty) {
-  TestSetup();
-
+TEST_F(PartitionAllocTest, PreferActiveOverEmpty) {
   size_t size = (kSystemPageSize * 2) - kExtraAllocSize;
   // Allocate 3 full slot spans worth of 8192-byte allocations.
   // Each slot span for this size is 16384 bytes, or 1 partition page and 2
@@ -1813,9 +1783,7 @@
 }
 
 // Tests the API to purge discardable memory.
-TEST(PartitionAllocTest, PurgeDiscardable) {
-  TestSetup();
-
+TEST_F(PartitionAllocTest, PurgeDiscardable) {
   // Free the second of two 4096 byte allocations and then purge.
   {
     void* ptr1 = PartitionAllocGeneric(
@@ -2088,9 +2056,7 @@
   }
 }
 
-TEST(PartitionAllocTest, ReallocMovesCookies) {
-  TestSetup();
-
+TEST_F(PartitionAllocTest, ReallocMovesCookies) {
   // Resize so as to be sure to hit a "resize in place" case, and ensure that
   // use of the entire result is compatible with the debug mode's cookies, even
   // when the bucket size is large enough to span more than one partition page
diff --git a/build/config/BUILD.gn b/build/config/BUILD.gn
index aff0e3d..882f183 100644
--- a/build/config/BUILD.gn
+++ b/build/config/BUILD.gn
@@ -264,13 +264,17 @@
 }
 
 # Dependencies that all executables and shared libraries should have.
-# All targets that currently depend on //build/config/sanitizers:deps
-# are being made to depend on this target instead
-# (https://crbug.com/723069).
 group("exe_and_shlib_deps") {
-  public_deps = [
-    "//build/config/sanitizers:deps",
-  ]
+  public_deps = []
+  if (using_sanitizer) {
+    public_deps += [ "//build/config/sanitizers:deps" ]
+  }
+  if (use_custom_libcxx) {
+    public_deps += [ "//buildtools/third_party/libc++:libcxx_proxy" ]
+  }
+  if (use_afl) {
+    public_deps += [ "//third_party/afl" ]
+  }
 }
 
 # Executable configs -----------------------------------------------------------
diff --git a/build/config/sanitizers/BUILD.gn b/build/config/sanitizers/BUILD.gn
index f6b88c7..67d9568c 100644
--- a/build/config/sanitizers/BUILD.gn
+++ b/build/config/sanitizers/BUILD.gn
@@ -18,9 +18,6 @@
 # "//build/config:exe_and_shlib_deps" to pull in this target.
 group("deps") {
   visibility = [ "//build/config:exe_and_shlib_deps" ]
-  public_deps = [
-    ":deps_no_options",
-  ]
   if (using_sanitizer) {
     public_configs = [
       ":sanitizer_options_link_helper",
@@ -33,32 +30,15 @@
     deps = [
       ":options_sources",
     ]
-  }
-  if (use_afl) {
-    deps += [ "//third_party/afl" ]
-  }
-}
-
-group("deps_no_options") {
-  if (using_sanitizer) {
-    public_configs = [
-      # Even when a target removes default_sanitizer_flags, it may be depending
-      # on a library that did not remove default_sanitizer_flags. Thus, we need
-      # to add the ldflags here as well as in default_sanitizer_flags.
-      ":default_sanitizer_ldflags",
-    ]
-    deps = []
-    public_deps = []
-
-    data = [
-      "//tools/valgrind/asan/",
-    ]
     if (is_win) {
       exe = ".exe"
     } else {
       exe = ""
     }
-    data += [ "$clang_base_path/bin/llvm-symbolizer${exe}" ]
+    data = [
+      "//tools/valgrind/asan/",
+      "$clang_base_path/bin/llvm-symbolizer${exe}",
+    ]
     if (is_linux) {
       # llvm-symbolizer needs this.
       data += [ "$clang_base_path/lib/libstdc++.so.6" ]
@@ -68,10 +48,6 @@
         use_locally_built_instrumented_libraries) {
       deps += [ "//third_party/instrumented_libraries:deps" ]
     }
-    if (use_custom_libcxx) {
-      public_deps += [ "//buildtools/third_party/libc++:libcxx_proxy" ]
-      data += [ "$root_out_dir/libc++.so" ]
-    }
 
     # ASAN is supported on iOS but the runtime library depends on the compiler
     # used (Chromium version of clang versus Xcode version of clang). Only copy
@@ -82,7 +58,9 @@
       ]
     }
     if (is_mac || (is_ios && !use_xcode_clang)) {
-      public_deps += [ ":asan_runtime_bundle_data" ]
+      public_deps = [
+        ":asan_runtime_bundle_data",
+      ]
     }
   }
 }
@@ -275,7 +253,6 @@
 
 config("common_sanitizer_flags") {
   cflags = []
-  cflags_cc = []
 
   # Sanitizers need line table info for stack traces. They don't need type info
   # or variable info, so we can leave that out to speed up the build (unless
@@ -303,11 +280,14 @@
       cflags += [ "/Oy-" ]
     }
   }
+}
 
+# TODO(thomasanderson): Move this out of build/config/sanitizers.
+config("libcxx_flags") {
   if (use_custom_libcxx) {
     prefix = "//buildtools/third_party"
     include = "trunk/include"
-    cflags_cc += [
+    cflags_cc = [
       "-nostdinc++",
       "-isystem" + rebase_path("$prefix/libc++/$include", root_build_dir),
       "-isystem" + rebase_path("$prefix/libc++abi/$include", root_build_dir),
@@ -558,6 +538,7 @@
 
 all_sanitizer_configs = [
   ":common_sanitizer_flags",
+  ":libcxx_flags",
   ":coverage_flags",
   ":default_sanitizer_ldflags",
   ":asan_flags",
diff --git a/build/sanitizers/sanitizer_options.cc b/build/sanitizers/sanitizer_options.cc
index 1f425011..e7e42798 100644
--- a/build/sanitizers/sanitizer_options.cc
+++ b/build/sanitizers/sanitizer_options.cc
@@ -82,24 +82,16 @@
     "check_printf=1 use_sigaltstack=1 "
     "strip_path_prefix=/../../ fast_unwind_on_fatal=1 "
     "detect_stack_use_after_return=1 detect_odr_violation=0 ";
-static const char kNaClDefaultOptions[] = "handle_segv=0";
-static const char kNaClFlag[] = "--type=nacl-loader";
 #endif  // OS_LINUX
 
 #if defined(OS_LINUX) || defined(OS_MACOSX)
+// Allow NaCl to override the default asan options.
+extern const char* kAsanDefaultOptionsNaCl;
+__attribute__((weak)) const char* kAsanDefaultOptionsNaCl = nullptr;
+
 SANITIZER_HOOK_ATTRIBUTE const char *__asan_default_options() {
-#if defined(OS_MACOSX)
-  char*** argvp = _NSGetArgv();
-  int* argcp = _NSGetArgc();
-  if (!argvp || !argcp) return kAsanDefaultOptions;
-  char** argv = *argvp;
-  int argc = *argcp;
-  for (int i = 0; i < argc; ++i) {
-    if (strcmp(argv[i], kNaClFlag) == 0) {
-      return kNaClDefaultOptions;
-    }
-  }
-#endif
+  if (kAsanDefaultOptionsNaCl)
+    return kAsanDefaultOptionsNaCl;
   return kAsanDefaultOptions;
 }
 
diff --git a/build/vs_toolchain.py b/build/vs_toolchain.py
index bb8f96c..31af518 100755
--- a/build/vs_toolchain.py
+++ b/build/vs_toolchain.py
@@ -342,8 +342,8 @@
     # Update 3 final with patches with 10.0.14393.0 SDK.
     return ['d3cb0e37bdd120ad0ac4650b674b09e81be45616']
   if env_version == '2017':
-    # VS 2017 RTM with 10.0.14393.0 SDK and dbghelp.dll fixes.
-    return ['4e8a360587a3c8ff3fa46aa9271e982bf3e948ec']
+    # VS 2017 Update 3 Preview 1 with 10.0.14393.0 SDK and patched statreg.h.
+    return ['3915730f76bd9c6155aed871b944b0a25c18f15e']
   raise Exception('Unsupported VS version %s' % env_version)
 
 
diff --git a/cc/layers/layer_impl.cc b/cc/layers/layer_impl.cc
index e40b570..e0605290 100644
--- a/cc/layers/layer_impl.cc
+++ b/cc/layers/layer_impl.cc
@@ -81,7 +81,7 @@
       needs_push_properties_(false),
       scrollbars_hidden_(false),
       needs_show_scrollbars_(false),
-      raster_even_if_not_in_rsll_(false) {
+      raster_even_if_not_drawn_(false) {
   DCHECK_GT(layer_id_, 0);
 
   DCHECK(layer_tree_impl_);
diff --git a/cc/layers/layer_impl.h b/cc/layers/layer_impl.h
index c32c208..10474cf 100644
--- a/cc/layers/layer_impl.h
+++ b/cc/layers/layer_impl.h
@@ -440,12 +440,10 @@
   void set_needs_show_scrollbars(bool yes) { needs_show_scrollbars_ = yes; }
   bool needs_show_scrollbars() { return needs_show_scrollbars_; }
 
-  void set_raster_even_if_not_in_rsll(bool yes) {
-    raster_even_if_not_in_rsll_ = yes;
+  void set_raster_even_if_not_drawn(bool yes) {
+    raster_even_if_not_drawn_ = yes;
   }
-  bool raster_even_if_not_in_rsll() const {
-    return raster_even_if_not_in_rsll_;
-  }
+  bool raster_even_if_not_drawn() const { return raster_even_if_not_drawn_; }
 
  protected:
   LayerImpl(LayerTreeImpl* layer_impl,
@@ -564,7 +562,11 @@
   // layers) and consumed by LayerTreeImpl::PushPropertiesTo during activation.
   bool needs_show_scrollbars_ : 1;
 
-  bool raster_even_if_not_in_rsll_ : 1;
+  // This is set for layers that have a property because of which they are not
+  // drawn (singular transforms), but they can become visible soon (the property
+  // is being animated). For this reason, while these layers are not drawn, they
+  // are still rasterized.
+  bool raster_even_if_not_drawn_ : 1;
 
   DISALLOW_COPY_AND_ASSIGN(LayerImpl);
 };
diff --git a/cc/layers/picture_layer_impl.cc b/cc/layers/picture_layer_impl.cc
index f43c977..f7d061fb 100644
--- a/cc/layers/picture_layer_impl.cc
+++ b/cc/layers/picture_layer_impl.cc
@@ -515,9 +515,13 @@
   // The reason for this is that we should be able to activate sooner and get a
   // more up to date recording, so we don't run out of recording on the active
   // tree.
-  bool can_require_tiles_for_activation =
-      !only_used_low_res_last_append_quads_ || RequiresHighResToDraw() ||
-      !layer_tree_impl()->SmoothnessTakesPriority();
+  // A layer must be a drawing layer for it to require tiles for activation.
+  bool can_require_tiles_for_activation = false;
+  if (contributes_to_drawn_render_surface()) {
+    can_require_tiles_for_activation =
+        !only_used_low_res_last_append_quads_ || RequiresHighResToDraw() ||
+        !layer_tree_impl()->SmoothnessTakesPriority();
+  }
 
   static const Occlusion kEmptyOcclusion;
   const Occlusion& occlusion_in_content_space =
@@ -1458,8 +1462,8 @@
 }
 
 bool PictureLayerImpl::HasValidTilePriorities() const {
-  return IsOnActiveOrPendingTree() && (contributes_to_drawn_render_surface() ||
-                                       raster_even_if_not_in_rsll());
+  return IsOnActiveOrPendingTree() &&
+         (contributes_to_drawn_render_surface() || raster_even_if_not_drawn());
 }
 
 void PictureLayerImpl::InvalidateRegionForImages(
diff --git a/cc/layers/picture_layer_impl_perftest.cc b/cc/layers/picture_layer_impl_perftest.cc
index 270978d..7db16d60b 100644
--- a/cc/layers/picture_layer_impl_perftest.cc
+++ b/cc/layers/picture_layer_impl_perftest.cc
@@ -86,7 +86,7 @@
       int count = num_tiles;
       std::unique_ptr<TilingSetRasterQueueAll> queue(
           new TilingSetRasterQueueAll(
-              pending_layer_->picture_layer_tiling_set(), false));
+              pending_layer_->picture_layer_tiling_set(), false, true));
       while (count--) {
         ASSERT_TRUE(!queue->IsEmpty()) << "count: " << count;
         ASSERT_TRUE(queue->Top().tile()) << "count: " << count;
@@ -114,7 +114,7 @@
     do {
       std::unique_ptr<TilingSetRasterQueueAll> queue(
           new TilingSetRasterQueueAll(
-              pending_layer_->picture_layer_tiling_set(), false));
+              pending_layer_->picture_layer_tiling_set(), false, true));
       timer_.NextLap();
     } while (!timer_.HasTimeLimitExpired());
 
diff --git a/cc/layers/picture_layer_impl_unittest.cc b/cc/layers/picture_layer_impl_unittest.cc
index d7d28d053..877b460 100644
--- a/cc/layers/picture_layer_impl_unittest.cc
+++ b/cc/layers/picture_layer_impl_unittest.cc
@@ -1659,7 +1659,7 @@
   int num_offscreen = 0;
 
   std::unique_ptr<TilingSetRasterQueueAll> queue(new TilingSetRasterQueueAll(
-      pending_layer()->picture_layer_tiling_set(), false));
+      pending_layer()->picture_layer_tiling_set(), false, false));
   for (; !queue->IsEmpty(); queue->Pop()) {
     const PrioritizedTile& prioritized_tile = queue->Top();
     DCHECK(prioritized_tile.tile());
@@ -2753,7 +2753,7 @@
   int high_res_tile_count = 0u;
   int high_res_now_tiles = 0u;
   std::unique_ptr<TilingSetRasterQueueAll> queue(new TilingSetRasterQueueAll(
-      pending_layer()->picture_layer_tiling_set(), false));
+      pending_layer()->picture_layer_tiling_set(), false, false));
   while (!queue->IsEmpty()) {
     PrioritizedTile prioritized_tile = queue->Top();
     TilePriority priority = prioritized_tile.priority();
@@ -2824,7 +2824,7 @@
   unique_tiles.clear();
   high_res_tile_count = 0u;
   queue.reset(new TilingSetRasterQueueAll(
-      pending_layer()->picture_layer_tiling_set(), false));
+      pending_layer()->picture_layer_tiling_set(), false, false));
   while (!queue->IsEmpty()) {
     PrioritizedTile prioritized_tile = queue->Top();
     TilePriority priority = prioritized_tile.priority();
@@ -2861,7 +2861,7 @@
   }
 
   queue.reset(new TilingSetRasterQueueAll(
-      pending_layer()->picture_layer_tiling_set(), true));
+      pending_layer()->picture_layer_tiling_set(), true, false));
   EXPECT_TRUE(queue->IsEmpty());
 }
 
@@ -3715,7 +3715,7 @@
   // No occlusion.
   int unoccluded_tile_count = 0;
   std::unique_ptr<TilingSetRasterQueueAll> queue(new TilingSetRasterQueueAll(
-      pending_layer()->picture_layer_tiling_set(), false));
+      pending_layer()->picture_layer_tiling_set(), false, false));
   while (!queue->IsEmpty()) {
     PrioritizedTile prioritized_tile = queue->Top();
     Tile* tile = prioritized_tile.tile();
@@ -3749,7 +3749,7 @@
 
   unoccluded_tile_count = 0;
   queue.reset(new TilingSetRasterQueueAll(
-      pending_layer()->picture_layer_tiling_set(), false));
+      pending_layer()->picture_layer_tiling_set(), false, false));
   while (!queue->IsEmpty()) {
     PrioritizedTile prioritized_tile = queue->Top();
     Tile* tile = prioritized_tile.tile();
@@ -3774,7 +3774,7 @@
 
   unoccluded_tile_count = 0;
   queue.reset(new TilingSetRasterQueueAll(
-      pending_layer()->picture_layer_tiling_set(), false));
+      pending_layer()->picture_layer_tiling_set(), false, false));
   while (!queue->IsEmpty()) {
     PrioritizedTile prioritized_tile = queue->Top();
     Tile* tile = prioritized_tile.tile();
diff --git a/cc/tiles/picture_layer_tiling.h b/cc/tiles/picture_layer_tiling.h
index d49d22e..d9c33a0 100644
--- a/cc/tiles/picture_layer_tiling.h
+++ b/cc/tiles/picture_layer_tiling.h
@@ -126,6 +126,9 @@
   void set_can_require_tiles_for_activation(bool can_require_tiles) {
     can_require_tiles_for_activation_ = can_require_tiles;
   }
+  bool can_require_tiles_for_activation() const {
+    return can_require_tiles_for_activation_;
+  }
 
   const scoped_refptr<RasterSource>& raster_source() const {
     return raster_source_;
diff --git a/cc/tiles/raster_tile_priority_queue_all.cc b/cc/tiles/raster_tile_priority_queue_all.cc
index 0d54a59..c09da4a 100644
--- a/cc/tiles/raster_tile_priority_queue_all.cc
+++ b/cc/tiles/raster_tile_priority_queue_all.cc
@@ -16,15 +16,22 @@
   explicit RasterOrderComparator(TreePriority tree_priority)
       : tree_priority_(tree_priority) {}
 
+  // Note that in this function, we have to return true if and only if
+  // a is strictly lower priority than b.
   bool operator()(
       const std::unique_ptr<TilingSetRasterQueueAll>& a_queue,
       const std::unique_ptr<TilingSetRasterQueueAll>& b_queue) const {
-    // Note that in this function, we have to return true if and only if
-    // a is strictly lower priority than b.
     const TilePriority& a_priority = a_queue->Top().priority();
     const TilePriority& b_priority = b_queue->Top().priority();
     bool prioritize_low_res = tree_priority_ == SMOOTHNESS_TAKES_PRIORITY;
 
+    // If the priority bin is the same but one of the tiles is from a
+    // non-drawing layer, then the drawing layer has a higher priority.
+    if (b_priority.priority_bin == a_priority.priority_bin &&
+        b_queue->is_drawing_layer() != a_queue->is_drawing_layer()) {
+      return b_queue->is_drawing_layer();
+    }
+
     // If the bin is the same but the resolution is not, then the order will be
     // determined by whether we prioritize low res or not.
     // TODO(vmpstr): Remove this when TilePriority is no longer a member of Tile
@@ -63,8 +70,9 @@
     PictureLayerTilingSet* tiling_set = layer->picture_layer_tiling_set();
     bool prioritize_low_res = tree_priority == SMOOTHNESS_TAKES_PRIORITY;
     std::unique_ptr<TilingSetRasterQueueAll> tiling_set_queue =
-        base::MakeUnique<TilingSetRasterQueueAll>(tiling_set,
-                                                  prioritize_low_res);
+        base::MakeUnique<TilingSetRasterQueueAll>(
+            tiling_set, prioritize_low_res,
+            layer->contributes_to_drawn_render_surface());
     // Queues will only contain non empty tiling sets.
     if (!tiling_set_queue->IsEmpty())
       queues->push_back(std::move(tiling_set_queue));
diff --git a/cc/tiles/tile_manager_unittest.cc b/cc/tiles/tile_manager_unittest.cc
index 6b22667..3b61ba6 100644
--- a/cc/tiles/tile_manager_unittest.cc
+++ b/cc/tiles/tile_manager_unittest.cc
@@ -1170,7 +1170,7 @@
   //    marked as ready to draw.
   for (int i = 0; i < 3; ++i) {
     std::unique_ptr<TilingSetRasterQueueAll> queue(
-        new TilingSetRasterQueueAll(tiling_set.get(), false));
+        new TilingSetRasterQueueAll(tiling_set.get(), false, false));
 
     // There are 3 bins in TilePriority.
     bool have_tiles[3] = {};
@@ -1282,7 +1282,7 @@
   int eventually_bin_order_correct_count = 0;
   int eventually_bin_order_incorrect_count = 0;
   std::unique_ptr<TilingSetRasterQueueAll> queue(
-      new TilingSetRasterQueueAll(tiling_set.get(), false));
+      new TilingSetRasterQueueAll(tiling_set.get(), false, false));
   for (; !queue->IsEmpty(); queue->Pop()) {
     if (!last_tile.tile())
       last_tile = queue->Top();
@@ -1439,7 +1439,7 @@
         intersecting_rect,      // Soon rect.
         intersecting_rect);     // Eventually rect.
     std::unique_ptr<TilingSetRasterQueueAll> queue(
-        new TilingSetRasterQueueAll(tiling_set.get(), false));
+        new TilingSetRasterQueueAll(tiling_set.get(), false, false));
     EXPECT_FALSE(queue->IsEmpty());
   }
   {
@@ -1449,7 +1449,7 @@
         intersecting_rect,      // Soon rect.
         intersecting_rect);     // Eventually rect.
     std::unique_ptr<TilingSetRasterQueueAll> queue(
-        new TilingSetRasterQueueAll(tiling_set.get(), false));
+        new TilingSetRasterQueueAll(tiling_set.get(), false, false));
     EXPECT_FALSE(queue->IsEmpty());
   }
   {
@@ -1459,7 +1459,7 @@
         non_intersecting_rect,  // Soon rect.
         intersecting_rect);     // Eventually rect.
     std::unique_ptr<TilingSetRasterQueueAll> queue(
-        new TilingSetRasterQueueAll(tiling_set.get(), false));
+        new TilingSetRasterQueueAll(tiling_set.get(), false, false));
     EXPECT_FALSE(queue->IsEmpty());
   }
 }
diff --git a/cc/tiles/tiling_set_raster_queue_all.cc b/cc/tiles/tiling_set_raster_queue_all.cc
index adae8a8..f2c1916 100644
--- a/cc/tiles/tiling_set_raster_queue_all.cc
+++ b/cc/tiles/tiling_set_raster_queue_all.cc
@@ -22,8 +22,11 @@
 
 TilingSetRasterQueueAll::TilingSetRasterQueueAll(
     PictureLayerTilingSet* tiling_set,
-    bool prioritize_low_res)
-    : tiling_set_(tiling_set), current_stage_(0) {
+    bool prioritize_low_res,
+    bool is_drawing_layer)
+    : tiling_set_(tiling_set),
+      current_stage_(0),
+      is_drawing_layer_(is_drawing_layer) {
   DCHECK(tiling_set_);
 
   // Early out if the tiling set has no tilings.
diff --git a/cc/tiles/tiling_set_raster_queue_all.h b/cc/tiles/tiling_set_raster_queue_all.h
index 73bb4c9e..e29736d 100644
--- a/cc/tiles/tiling_set_raster_queue_all.h
+++ b/cc/tiles/tiling_set_raster_queue_all.h
@@ -22,12 +22,14 @@
 class CC_EXPORT TilingSetRasterQueueAll {
  public:
   TilingSetRasterQueueAll(PictureLayerTilingSet* tiling_set,
-                          bool prioritize_low_res);
+                          bool prioritize_low_res,
+                          bool is_drawing_layer);
   ~TilingSetRasterQueueAll();
 
   const PrioritizedTile& Top() const;
   void Pop();
   bool IsEmpty() const;
+  bool is_drawing_layer() const { return is_drawing_layer_; }
 
  private:
   // Helper base class for individual region iterators.
@@ -190,6 +192,7 @@
   // ideal pending high res.
   base::StackVector<IterationStage, 6> stages_;
   TilingIterator iterators_[NUM_ITERATORS];
+  bool is_drawing_layer_ = false;
 
   DISALLOW_COPY_AND_ASSIGN(TilingSetRasterQueueAll);
 };
diff --git a/cc/trees/layer_tree_host_common.cc b/cc/trees/layer_tree_host_common.cc
index e289a34..f561445 100644
--- a/cc/trees/layer_tree_host_common.cc
+++ b/cc/trees/layer_tree_host_common.cc
@@ -377,8 +377,8 @@
     bool skip_layer = !is_root && (skip_draw_properties_computation ||
                                    skip_for_invertibility);
 
-    layer->set_raster_even_if_not_in_rsll(skip_for_invertibility &&
-                                          !skip_draw_properties_computation);
+    layer->set_raster_even_if_not_drawn(skip_for_invertibility &&
+                                        !skip_draw_properties_computation);
     if (skip_layer)
       continue;
 
diff --git a/cc/trees/layer_tree_host_common_unittest.cc b/cc/trees/layer_tree_host_common_unittest.cc
index a26ee7f..3c50e689 100644
--- a/cc/trees/layer_tree_host_common_unittest.cc
+++ b/cc/trees/layer_tree_host_common_unittest.cc
@@ -8347,7 +8347,7 @@
   // Since animated has singular transform, it is not be part of render
   // surface layer list but should be rastered.
   EXPECT_FALSE(animated->contributes_to_drawn_render_surface());
-  EXPECT_TRUE(animated->raster_even_if_not_in_rsll());
+  EXPECT_TRUE(animated->raster_even_if_not_drawn());
 
   // The animated layer has a singular transform and maps to a non-empty rect in
   // clipped target space, so is treated as fully visible.
@@ -8387,7 +8387,7 @@
 
   // Since animated has singular transform, it is not be part of render
   // surface layer list but should be rastered.
-  EXPECT_TRUE(animated->raster_even_if_not_in_rsll());
+  EXPECT_TRUE(animated->raster_even_if_not_drawn());
   EXPECT_EQ(gfx::Rect(120, 120), active_animated->visible_layer_rect());
 }
 
diff --git a/cc/trees/layer_tree_host_impl_unittest.cc b/cc/trees/layer_tree_host_impl_unittest.cc
index b978d05..d90ce99 100644
--- a/cc/trees/layer_tree_host_impl_unittest.cc
+++ b/cc/trees/layer_tree_host_impl_unittest.cc
@@ -12414,5 +12414,136 @@
   EXPECT_EQ(host_impl_->GetRasterColorSpace(), gfx::ColorSpace::CreateSRGB());
 }
 
+TEST_F(LayerTreeHostImplTest, UpdatedTilingsForNonDrawingLayers) {
+  gfx::Size layer_bounds(500, 500);
+
+  host_impl_->SetViewportSize(layer_bounds);
+  host_impl_->CreatePendingTree();
+  std::unique_ptr<LayerImpl> scoped_root =
+      LayerImpl::Create(host_impl_->pending_tree(), 1);
+  scoped_root->SetBounds(layer_bounds);
+  LayerImpl* root = scoped_root.get();
+  host_impl_->pending_tree()->SetRootLayerForTesting(std::move(scoped_root));
+
+  scoped_refptr<FakeRasterSource> raster_source(
+      FakeRasterSource::CreateFilled(layer_bounds));
+  std::unique_ptr<FakePictureLayerImpl> scoped_animated_transform_layer =
+      FakePictureLayerImpl::CreateWithRasterSource(host_impl_->pending_tree(),
+                                                   2, raster_source);
+  scoped_animated_transform_layer->SetBounds(layer_bounds);
+  scoped_animated_transform_layer->SetDrawsContent(true);
+  gfx::Transform singular;
+  singular.Scale3d(6.f, 6.f, 0.f);
+  scoped_animated_transform_layer->test_properties()->transform = singular;
+  FakePictureLayerImpl* animated_transform_layer =
+      scoped_animated_transform_layer.get();
+  root->test_properties()->AddChild(std::move(scoped_animated_transform_layer));
+
+  // A layer with a non-invertible transform is not drawn or rasterized. Since
+  // this layer is not rasterized, we shouldn't be creating any tilings for it.
+  host_impl_->pending_tree()->BuildLayerListAndPropertyTreesForTesting();
+  EXPECT_FALSE(animated_transform_layer->HasValidTilePriorities());
+  EXPECT_EQ(animated_transform_layer->tilings()->num_tilings(), 0u);
+  host_impl_->pending_tree()->UpdateDrawProperties(false);
+  EXPECT_FALSE(animated_transform_layer->raster_even_if_not_drawn());
+  EXPECT_FALSE(animated_transform_layer->contributes_to_drawn_render_surface());
+  EXPECT_EQ(animated_transform_layer->tilings()->num_tilings(), 0u);
+
+  // Now add a transform animation to this layer. While we don't drawn layers
+  // with non-invertible transforms, we still raster them if there is a
+  // transform animation.
+  host_impl_->pending_tree()->SetElementIdsForTesting();
+  TransformOperations start_transform_operations;
+  start_transform_operations.AppendMatrix(singular);
+  TransformOperations end_transform_operations;
+  AddAnimatedTransformToElementWithPlayer(
+      animated_transform_layer->element_id(), timeline(), 10.0,
+      start_transform_operations, end_transform_operations);
+
+  // The layer is still not drawn, but it will be rasterized. Since the layer is
+  // rasterized, we should be creating tilings for it in UpdateDrawProperties.
+  // However, none of these tiles should be required for activation.
+  host_impl_->pending_tree()->BuildLayerListAndPropertyTreesForTesting();
+  host_impl_->pending_tree()->UpdateDrawProperties(false);
+  EXPECT_TRUE(animated_transform_layer->raster_even_if_not_drawn());
+  EXPECT_FALSE(animated_transform_layer->contributes_to_drawn_render_surface());
+  EXPECT_EQ(animated_transform_layer->tilings()->num_tilings(), 1u);
+  EXPECT_FALSE(animated_transform_layer->tilings()
+                   ->tiling_at(0)
+                   ->can_require_tiles_for_activation());
+}
+
+TEST_F(LayerTreeHostImplTest, RasterTilePrioritizationForNonDrawingLayers) {
+  gfx::Size layer_bounds(500, 500);
+
+  host_impl_->SetViewportSize(layer_bounds);
+  host_impl_->CreatePendingTree();
+  std::unique_ptr<LayerImpl> scoped_root =
+      LayerImpl::Create(host_impl_->pending_tree(), 1);
+  scoped_root->SetBounds(layer_bounds);
+  LayerImpl* root = scoped_root.get();
+  host_impl_->pending_tree()->SetRootLayerForTesting(std::move(scoped_root));
+
+  scoped_refptr<FakeRasterSource> raster_source(
+      FakeRasterSource::CreateFilled(layer_bounds));
+
+  std::unique_ptr<FakePictureLayerImpl> scoped_hidden_layer =
+      FakePictureLayerImpl::CreateWithRasterSource(host_impl_->pending_tree(),
+                                                   2, raster_source);
+  scoped_hidden_layer->SetBounds(layer_bounds);
+  scoped_hidden_layer->SetDrawsContent(true);
+  scoped_hidden_layer->set_contributes_to_drawn_render_surface(true);
+  FakePictureLayerImpl* hidden_layer = scoped_hidden_layer.get();
+  root->test_properties()->AddChild(std::move(scoped_hidden_layer));
+
+  std::unique_ptr<FakePictureLayerImpl> scoped_drawing_layer =
+      FakePictureLayerImpl::CreateWithRasterSource(host_impl_->pending_tree(),
+                                                   3, raster_source);
+  scoped_drawing_layer->SetBounds(layer_bounds);
+  scoped_drawing_layer->SetDrawsContent(true);
+  scoped_drawing_layer->set_contributes_to_drawn_render_surface(true);
+  FakePictureLayerImpl* drawing_layer = scoped_drawing_layer.get();
+  root->test_properties()->AddChild(std::move(scoped_drawing_layer));
+
+  gfx::Rect layer_rect(0, 0, 500, 500);
+  gfx::Rect empty_rect(0, 0, 0, 0);
+  host_impl_->pending_tree()->BuildPropertyTreesForTesting();
+
+  hidden_layer->tilings()->AddTiling(gfx::AxisTransform2d(), raster_source);
+  PictureLayerTiling* hidden_tiling = hidden_layer->tilings()->tiling_at(0);
+  hidden_tiling->set_resolution(TileResolution::LOW_RESOLUTION);
+  hidden_tiling->CreateAllTilesForTesting();
+  hidden_tiling->SetTilePriorityRectsForTesting(
+      layer_rect,   // Visible rect.
+      layer_rect,   // Skewport rect.
+      layer_rect,   // Soon rect.
+      layer_rect);  // Eventually rect.
+
+  drawing_layer->tilings()->AddTiling(gfx::AxisTransform2d(), raster_source);
+  PictureLayerTiling* drawing_tiling = drawing_layer->tilings()->tiling_at(0);
+  drawing_tiling->set_resolution(TileResolution::HIGH_RESOLUTION);
+  drawing_tiling->CreateAllTilesForTesting();
+  drawing_tiling->SetTilePriorityRectsForTesting(
+      layer_rect,   // Visible rect.
+      layer_rect,   // Skewport rect.
+      layer_rect,   // Soon rect.
+      layer_rect);  // Eventually rect.
+
+  // Both layers are drawn. Since the hidden layer has a low resolution tiling,
+  // in smoothness priority mode its tile is higher priority.
+  std::unique_ptr<RasterTilePriorityQueue> queue =
+      host_impl_->BuildRasterQueue(TreePriority::SMOOTHNESS_TAKES_PRIORITY,
+                                   RasterTilePriorityQueue::Type::ALL);
+  EXPECT_EQ(queue->Top().tile()->layer_id(), 2);
+
+  // Hide the hidden layer and set it to so it still rasters. Now the drawing
+  // layer should be prioritized over the hidden layer.
+  hidden_layer->set_contributes_to_drawn_render_surface(false);
+  hidden_layer->set_raster_even_if_not_drawn(true);
+  queue = host_impl_->BuildRasterQueue(TreePriority::SMOOTHNESS_TAKES_PRIORITY,
+                                       RasterTilePriorityQueue::Type::ALL);
+  EXPECT_EQ(queue->Top().tile()->layer_id(), 3);
+}
+
 }  // namespace
 }  // namespace cc
diff --git a/cc/trees/layer_tree_impl.cc b/cc/trees/layer_tree_impl.cc
index abf3277..945b5ec 100644
--- a/cc/trees/layer_tree_impl.cc
+++ b/cc/trees/layer_tree_impl.cc
@@ -1155,7 +1155,7 @@
     size_t layers_updated_count = 0;
     bool tile_priorities_updated = false;
     for (PictureLayerImpl* layer : picture_layers_) {
-      if (!layer->contributes_to_drawn_render_surface())
+      if (!layer->HasValidTilePriorities())
         continue;
       ++layers_updated_count;
       tile_priorities_updated |= layer->UpdateTiles();
diff --git a/chrome/android/BUILD.gn b/chrome/android/BUILD.gn
index 25d2f1eb..3eec53a 100644
--- a/chrome/android/BUILD.gn
+++ b/chrome/android/BUILD.gn
@@ -94,10 +94,10 @@
     "//components/strings:components_strings_grd",
     "//content/public/android:content_java_resources",
     "//third_party/android_data_chart:android_data_chart_java_resources",
+    "//third_party/android_media:android_media_resources",
     "//third_party/android_tools:android_support_design_java",
     "//third_party/android_tools:android_support_transition_java",
     "//third_party/android_tools:android_support_v7_appcompat_java",
-    "//third_party/android_tools:android_support_v7_mediarouter_java",
     "//third_party/android_tools:android_support_v7_recyclerview_java",
   ]
   custom_package = "org.chromium.chrome"
@@ -217,6 +217,7 @@
     "//third_party/WebKit/public:android_mojo_bindings_java",
     "//third_party/WebKit/public:blink_headers_java",
     "//third_party/android_data_chart:android_data_chart_java",
+    "//third_party/android_media:android_media_java",
     "//third_party/android_protobuf:protobuf_nano_javalib",
     "//third_party/android_swipe_refresh:android_swipe_refresh_java",
     "//third_party/android_tools:android_gcm_java",
diff --git a/chrome/android/java/res/layout/expanded_cast_controller.xml b/chrome/android/java/res/layout/expanded_cast_controller.xml
index 64856f6..340cef16 100644
--- a/chrome/android/java/res/layout/expanded_cast_controller.xml
+++ b/chrome/android/java/res/layout/expanded_cast_controller.xml
@@ -32,4 +32,10 @@
         android:textSize="14sp"
         android:textStyle="bold"
         android:textColor="@color/cast_media_controller_text" />
+    <org.chromium.third_party.android.media.MediaController
+        android:id="@+id/cast_media_controller"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_gravity="bottom">
+    </org.chromium.third_party.android.media.MediaController>
 </FrameLayout>
diff --git a/chrome/android/java/res/values/colors.xml b/chrome/android/java/res/values/colors.xml
index a4dc391..4a74410 100644
--- a/chrome/android/java/res/values/colors.xml
+++ b/chrome/android/java/res/values/colors.xml
@@ -221,5 +221,5 @@
     <color name="selectable_list_item_highlight_color">#804281f4</color>
     <color name="media_viewer_bg">#000000</color>
     <color name="image_viewer_bg">#0e0e0e</color>
-    <color name="cast_media_controller_text">#bebebe</color>
+
 </resources>
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/media/remote/ExpandedControllerActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/media/remote/ExpandedControllerActivity.java
index 1d7416f..017c3c65 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/media/remote/ExpandedControllerActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/media/remote/ExpandedControllerActivity.java
@@ -11,6 +11,7 @@
 import android.os.Bundle;
 import android.os.Handler;
 import android.support.v4.app.FragmentActivity;
+import android.support.v4.media.session.PlaybackStateCompat;
 import android.text.TextUtils;
 import android.view.KeyEvent;
 import android.view.View;
@@ -18,8 +19,6 @@
 import android.view.Window;
 import android.view.WindowManager;
 import android.widget.ImageView;
-import android.widget.MediaController;
-import android.widget.MediaController.MediaPlayerControl;
 import android.widget.TextView;
 
 import com.google.android.gms.cast.CastMediaControlIntent;
@@ -27,6 +26,7 @@
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.media.remote.RemoteVideoInfo.PlayerState;
 import org.chromium.chrome.browser.metrics.MediaNotificationUma;
+import org.chromium.third_party.android.media.MediaController;
 
 /**
  * The activity that's opened by clicking the video flinging (casting) notification.
@@ -39,48 +39,10 @@
     // The alpha value for the poster/placeholder image, an integer between 0 and 256 (opaque).
     private static final int POSTER_IMAGE_ALPHA = 200;
 
-    // Subclass of {@link android.widget.MediaController} that never hides itself.
-    class AlwaysShownMediaController extends MediaController {
-        public AlwaysShownMediaController(Context context) {
-            super(context);
-        }
-
-        @Override
-        public void show(int timeout) {
-            // Never auto-hide the controls.
-            super.show(0);
-        }
-
-        @Override
-        public boolean dispatchKeyEvent(KeyEvent event) {
-            int keyCode = event.getKeyCode();
-            // MediaController hides the controls when back or menu are pressed.
-            // Close the activity on back and ignore menu.
-            if (keyCode == KeyEvent.KEYCODE_BACK) {
-                finish();
-                return true;
-            } else if (keyCode == KeyEvent.KEYCODE_MENU) {
-                return true;
-            }
-            return super.dispatchKeyEvent(event);
-        }
-
-        @Override
-        public void hide() {
-            // Don't allow the controls to hide until explicitly asked to do so from the host
-            // activity.
-        }
-
-        /**
-         * Actually hides the controls which prevents some window leaks.
-         */
-        public void cleanup() {
-            super.hide();
-        }
-    };
-
     private Handler mHandler;
-    private AlwaysShownMediaController mMediaController;
+    // We don't use the standard android.media.MediaController, but a custom one.
+    // See the class itself for details.
+    private MediaController mMediaController;
     private FullscreenMediaRouteButton mMediaRouteButton;
     private MediaRouteController mMediaRouteController;
     private RemoteVideoInfo mVideoInfo;
@@ -89,51 +51,14 @@
     /**
      * Handle actions from on-screen media controls.
      */
-    private MediaPlayerControl mMediaPlayerControl = new MediaPlayerControl() {
+    private MediaController.Delegate mControllerDelegate = new MediaController.Delegate() {
         @Override
-        public boolean canPause() {
-            return true;
-        }
-
-        @Override
-        public boolean canSeekBackward() {
-            return getDuration() > 0 && getCurrentPosition() > 0;
-        }
-
-        @Override
-        public boolean canSeekForward() {
-            return getDuration() > 0 && getCurrentPosition() < getDuration();
-        }
-
-        @Override
-        public int getAudioSessionId() {
-            // TODO(avayvod): not sure 0 is a valid value to return.
-            return 0;
-        }
-
-        @Override
-        public int getBufferPercentage() {
-            int duration = getDuration();
-            if (duration == 0) return 0;
-            return (getCurrentPosition() * 100) / duration;
-        }
-
-        @Override
-        public int getCurrentPosition() {
-            if (mMediaRouteController == null) return 0;
-            return (int) mMediaRouteController.getPosition();
-        }
-
-        @Override
-        public int getDuration() {
-            if (mMediaRouteController == null) return 0;
-            return (int) mMediaRouteController.getDuration();
-        }
-
-        @Override
-        public boolean isPlaying() {
-            if (mMediaRouteController == null) return false;
-            return mMediaRouteController.isPlaying();
+        public void play() {
+            if (mMediaRouteController == null) return;
+            mMediaRouteController.resume();
+            RecordCastAction.recordFullscreenControlsAction(
+                    RecordCastAction.FULLSCREEN_CONTROLS_RESUME,
+                    mMediaRouteController.getMediaStateListener() != null);
         }
 
         @Override
@@ -146,28 +71,54 @@
         }
 
         @Override
-        public void start() {
-            if (mMediaRouteController == null) return;
-            mMediaRouteController.resume();
-            RecordCastAction.recordFullscreenControlsAction(
-                    RecordCastAction.FULLSCREEN_CONTROLS_RESUME,
-                    mMediaRouteController.getMediaStateListener() != null);
+        public long getDuration() {
+            if (mMediaRouteController == null) return 0;
+            return mMediaRouteController.getDuration();
         }
 
         @Override
-        public void seekTo(int pos) {
+        public long getPosition() {
+            if (mMediaRouteController == null) return 0;
+            return mMediaRouteController.getPosition();
+        }
+
+        @Override
+        public void seekTo(long pos) {
             if (mMediaRouteController == null) return;
             mMediaRouteController.seekTo(pos);
             RecordCastAction.recordFullscreenControlsAction(
                     RecordCastAction.FULLSCREEN_CONTROLS_SEEK,
                     mMediaRouteController.getMediaStateListener() != null);
         }
+
+        @Override
+        public boolean isPlaying() {
+            if (mMediaRouteController == null) return false;
+            return mMediaRouteController.isPlaying();
+        }
+
+        @Override
+        public long getActionFlags() {
+            long flags =
+                    PlaybackStateCompat.ACTION_REWIND | PlaybackStateCompat.ACTION_FAST_FORWARD;
+            if (mMediaRouteController != null && mMediaRouteController.isPlaying()) {
+                flags |= PlaybackStateCompat.ACTION_PAUSE;
+            } else {
+                flags |= PlaybackStateCompat.ACTION_PLAY;
+            }
+            return flags;
+        }
     };
 
-    private Runnable mControlsUpdater = new Runnable() {
+    private Runnable mProgressUpdater = new Runnable() {
         @Override
         public void run() {
-            mMediaController.show();
+            if (mMediaRouteController.isPlaying()) {
+                mMediaController.updateProgress();
+                mHandler.postDelayed(this, PROGRESS_UPDATE_PERIOD_IN_MS);
+            } else {
+                mHandler.removeCallbacks(this);
+            }
         }
     };
 
@@ -201,10 +152,8 @@
         mMediaRouteController.addUiListener(this);
 
         // Create and initialize the media control UI.
-        mMediaController = new AlwaysShownMediaController(this);
-        mMediaController.setEnabled(true);
-        mMediaController.setMediaPlayer(mMediaPlayerControl);
-        mMediaController.setAnchorView(rootView);
+        mMediaController = (MediaController) findViewById(R.id.cast_media_controller);
+        mMediaController.setDelegate(mControllerDelegate);
 
         View button = getLayoutInflater().inflate(R.layout.cast_controller_media_route_button,
                 rootView, false);
@@ -219,13 +168,16 @@
         }
 
         // Initialize the video info.
-        mVideoInfo = new RemoteVideoInfo(null, 0, RemoteVideoInfo.PlayerState.STOPPED, 0, null);
+        setVideoInfo(new RemoteVideoInfo(null, 0, RemoteVideoInfo.PlayerState.STOPPED, 0, null));
+
+        mMediaController.refresh();
+
+        scheduleProgressUpdate();
     }
 
     @Override
     protected void onResume() {
         super.onResume();
-
         if (mVideoInfo.state == PlayerState.FINISHED) finish();
         if (mMediaRouteController == null) return;
 
@@ -241,9 +193,6 @@
         Bitmap posterBitmap = mMediaRouteController.getPoster();
         if (posterBitmap != null) iv.setImageBitmap(posterBitmap);
         iv.setImageAlpha(POSTER_IMAGE_ALPHA);
-
-        // Can't show the media controller until attached to window.
-        scheduleControlsUpdate();
     }
 
     @Override
@@ -264,12 +213,10 @@
     }
 
     private void cleanup() {
-        if (mHandler != null) mHandler.removeCallbacks(mControlsUpdater);
+        if (mHandler != null) mHandler.removeCallbacks(mProgressUpdater);
         if (mMediaRouteController != null) mMediaRouteController.removeUiListener(this);
         mMediaRouteController = null;
-        mControlsUpdater = null;
-        mMediaController.cleanup();
-        mMediaController = null;
+        mProgressUpdater = null;
     }
 
     /**
@@ -279,12 +226,14 @@
         if ((mVideoInfo == null) ? (videoInfo == null) : mVideoInfo.equals(videoInfo)) return;
 
         mVideoInfo = videoInfo;
-        updateUi();
+        onVideoInfoChanged();
     }
 
-    private void scheduleControlsUpdate() {
-        mHandler.removeCallbacks(mControlsUpdater);
-        mHandler.post(mControlsUpdater);
+    private void scheduleProgressUpdate() {
+        mHandler.removeCallbacks(mProgressUpdater);
+        if (mMediaRouteController.isPlaying()) {
+            mHandler.post(mProgressUpdater);
+        }
     }
 
     /**
@@ -294,6 +243,14 @@
         if (TextUtils.equals(mScreenName, screenName)) return;
 
         mScreenName = screenName;
+        onScreenNameChanged();
+    }
+
+    private void onVideoInfoChanged() {
+        updateUi();
+    }
+
+    private void onScreenNameChanged() {
         updateUi();
     }
 
@@ -308,7 +265,7 @@
         TextView castTextView = (TextView) findViewById(R.id.cast_screen_title);
         castTextView.setText(castText);
 
-        scheduleControlsUpdate();
+        mMediaController.refresh();
     }
 
     @Override
@@ -337,7 +294,7 @@
         videoInfo.state = newState;
         setVideoInfo(videoInfo);
 
-        scheduleControlsUpdate();
+        scheduleProgressUpdate();
 
         if (newState == PlayerState.FINISHED || newState == PlayerState.INVALIDATED) {
             // If we are switching to a finished state, stop the notifications.
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd
index 67b5bd8e..1f6d25d 100644
--- a/chrome/app/generated_resources.grd
+++ b/chrome/app/generated_resources.grd
@@ -3589,6 +3589,9 @@
         <message name="IDS_EXTENSION_PROMPT_WARNING_ALL_HOSTS" desc="Permission string for access to data on all websites.">
           Read and change all your data on the websites you visit
         </message>
+        <message name="IDS_EXTENSION_PROMPT_WARNING_CURRENT_HOST" desc="Permission string for access to data on current website.">
+          Read and change all your data on the current website when invoked
+        </message>
         <message name="IDS_EXTENSION_PROMPT_WARNING_ALL_HOSTS_READ_ONLY" desc="Permission string for read-only access to data on all websites.">
           Read all your data on the websites you visit
         </message>
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index 4bc606a..c66a68d1 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -401,14 +401,8 @@
     "external_protocol/external_protocol_handler.h",
     "external_protocol/external_protocol_observer.cc",
     "external_protocol/external_protocol_observer.h",
-    "favicon/chrome_fallback_icon_client.cc",
-    "favicon/chrome_fallback_icon_client.h",
-    "favicon/chrome_fallback_icon_client_factory.cc",
-    "favicon/chrome_fallback_icon_client_factory.h",
     "favicon/chrome_favicon_client.cc",
     "favicon/chrome_favicon_client.h",
-    "favicon/fallback_icon_service_factory.cc",
-    "favicon/fallback_icon_service_factory.h",
     "favicon/favicon_service_factory.cc",
     "favicon/favicon_service_factory.h",
     "favicon/favicon_utils.cc",
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index 588d5e6..bb25ae82 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -2967,6 +2967,11 @@
          kOmniboxUIMaxAutocompleteMatchesVariations,
          "OmniboxUIMaxAutocompleteVariations")},
 
+    {"omnibox-ui-vertical-layout",
+     flag_descriptions::kOmniboxUIVerticalLayoutName,
+     flag_descriptions::kOmniboxUIVerticalLayoutDescription, kOsDesktop,
+     FEATURE_VALUE_TYPE(omnibox::kUIExperimentVerticalLayout)},
+
     {"omnibox-ui-vertical-margin",
      flag_descriptions::kOmniboxUIVerticalMarginName,
      flag_descriptions::kOmniboxUIVerticalMarginDescription, kOsDesktop,
diff --git a/chrome/browser/android/vr_shell/color_scheme.cc b/chrome/browser/android/vr_shell/color_scheme.cc
index a662ed8..d39d5e5 100644
--- a/chrome/browser/android/vr_shell/color_scheme.cc
+++ b/chrome/browser/android/vr_shell/color_scheme.cc
@@ -16,16 +16,16 @@
     return;
 
   ColorScheme& normal_scheme = kColorSchemes[ColorScheme::kModeNormal];
-  normal_scheme.horizon = {0.89, 0.89, 0.89, 1.0};
-  normal_scheme.floor = {0.811, 0.811, 0.811, 1.0};
-  normal_scheme.ceiling = {0.859, 0.859, 0.859, 1.0};
-  normal_scheme.floor_grid = {1.0, 1.0, 1.0, 1.0};
+  normal_scheme.horizon = 0xFFE3E3E3;
+  normal_scheme.floor = 0xFFCFCFCF;
+  normal_scheme.ceiling = 0xFFDBDBDB;
+  normal_scheme.floor_grid = SK_ColorWHITE;
 
   ColorScheme& fullscreen_scheme = kColorSchemes[ColorScheme::kModeFullscreen];
-  fullscreen_scheme.horizon = {0.039215686, 0.0, 0.082352941, 1.0};
-  fullscreen_scheme.floor = {0.02745098, 0.058823529, 0.109803922, 1.0};
-  fullscreen_scheme.ceiling = {0.015686275, 0.031372549, 0.058823529, 1.0};
-  fullscreen_scheme.floor_grid = {0.639215686, 0.878431373, 1.0, 0.5};
+  fullscreen_scheme.horizon = 0xFF0A0015;
+  fullscreen_scheme.floor = 0xFF070F1C;
+  fullscreen_scheme.ceiling = 0xFF04080F;
+  fullscreen_scheme.floor_grid = 0x80A3E0FF;
 
   initialized = true;
 }
diff --git a/chrome/browser/android/vr_shell/color_scheme.h b/chrome/browser/android/vr_shell/color_scheme.h
index 1d304397..76bb78013 100644
--- a/chrome/browser/android/vr_shell/color_scheme.h
+++ b/chrome/browser/android/vr_shell/color_scheme.h
@@ -5,7 +5,7 @@
 #ifndef CHROME_BROWSER_ANDROID_VR_SHELL_COLOR_SCHEME_H_
 #define CHROME_BROWSER_ANDROID_VR_SHELL_COLOR_SCHEME_H_
 
-#include "device/vr/vr_types.h"
+#include "third_party/skia/include/core/SkColor.h"
 
 namespace vr_shell {
 
@@ -21,12 +21,10 @@
 
   // These colors should be named generically, if possible, so that they can be
   // meaningfully reused by multiple elements.
-  // TODO(vollick): we should replace all use of vr::Colorf with SkColor (see
-  // crbug.com/726363).
-  vr::Colorf horizon;
-  vr::Colorf floor;
-  vr::Colorf ceiling;
-  vr::Colorf floor_grid;
+  SkColor horizon;
+  SkColor floor;
+  SkColor ceiling;
+  SkColor floor_grid;
 };
 
 }  // namespace vr_shell
diff --git a/chrome/browser/android/vr_shell/ui_elements/screen_dimmer.cc b/chrome/browser/android/vr_shell/ui_elements/screen_dimmer.cc
index 8935b98..8e938c20 100644
--- a/chrome/browser/android/vr_shell/ui_elements/screen_dimmer.cc
+++ b/chrome/browser/android/vr_shell/ui_elements/screen_dimmer.cc
@@ -6,13 +6,17 @@
 
 #include "chrome/browser/android/vr_shell/vr_shell_renderer.h"
 #include "device/vr/vr_math.h"
+#include "third_party/skia/include/core/SkColor.h"
 
 namespace vr_shell {
 
-ScreenDimmer::ScreenDimmer()
-    : inner_color_({0.05f, 0.05f, 0.05f, 0.8f}),
-      outer_color_({0, 0, 0, 0.9f}),
-      opacity_(0.9f) {}
+namespace {
+static const SkColor kDimmerInnerColor = 0xCC0D0D0D;
+static const SkColor kDimmerOuterColor = 0xE6000000;
+static const float kDimmerOpacity = 0.9f;
+}  // namespace
+
+ScreenDimmer::ScreenDimmer() {}
 
 ScreenDimmer::~ScreenDimmer() = default;
 
@@ -25,8 +29,8 @@
   vr::Mat4f m;
   vr::SetIdentityM(&m);
   vr::ScaleM(m, {2.0f, 2.0f, 1.0f}, &m);
-  renderer->GetGradientQuadRenderer()->Draw(m, outer_color_, inner_color_,
-                                            opacity_);
+  renderer->GetGradientQuadRenderer()->Draw(m, kDimmerOuterColor,
+                                            kDimmerInnerColor, kDimmerOpacity);
 }
 
 }  // namespace vr_shell
diff --git a/chrome/browser/android/vr_shell/ui_elements/screen_dimmer.h b/chrome/browser/android/vr_shell/ui_elements/screen_dimmer.h
index 1e1c958..c2e83f9 100644
--- a/chrome/browser/android/vr_shell/ui_elements/screen_dimmer.h
+++ b/chrome/browser/android/vr_shell/ui_elements/screen_dimmer.h
@@ -7,7 +7,6 @@
 
 #include "base/macros.h"
 #include "chrome/browser/android/vr_shell/ui_elements/ui_element.h"
-#include "device/vr/vr_types.h"
 
 namespace vr_shell {
 
@@ -22,11 +21,6 @@
   void Render(VrShellRenderer* renderer,
               vr::Mat4f view_proj_matrix) const final;
 
- private:
-  vr::Colorf inner_color_;
-  vr::Colorf outer_color_;
-  float opacity_;
-
   DISALLOW_COPY_AND_ASSIGN(ScreenDimmer);
 };
 
diff --git a/chrome/browser/android/vr_shell/ui_elements/ui_element.h b/chrome/browser/android/vr_shell/ui_elements/ui_element.h
index a954948..71dd842c 100644
--- a/chrome/browser/android/vr_shell/ui_elements/ui_element.h
+++ b/chrome/browser/android/vr_shell/ui_elements/ui_element.h
@@ -12,6 +12,7 @@
 #include "base/macros.h"
 #include "chrome/browser/android/vr_shell/ui_elements/ui_element_debug_id.h"
 #include "device/vr/vr_types.h"
+#include "third_party/skia/include/core/SkColor.h"
 
 namespace base {
 class TimeTicks;
@@ -205,13 +206,11 @@
   Fill fill() const { return fill_; }
   void set_fill(Fill fill) { fill_ = fill; }
 
-  vr::Colorf edge_color() const { return edge_color_; }
-  void set_edge_color(const vr::Colorf& edge_color) {
-    edge_color_ = edge_color;
-  }
+  SkColor edge_color() const { return edge_color_; }
+  void set_edge_color(const SkColor& edge_color) { edge_color_ = edge_color; }
 
-  vr::Colorf center_color() const { return center_color_; }
-  void set_center_color(const vr::Colorf& center_color) {
+  SkColor center_color() const { return center_color_; }
+  void set_center_color(const SkColor& center_color) {
     center_color_ = center_color;
   }
 
@@ -299,8 +298,8 @@
 
   Fill fill_ = Fill::NONE;
 
-  vr::Colorf edge_color_ = {1.0f, 1.0f, 1.0f, 1.0f};
-  vr::Colorf center_color_ = {1.0f, 1.0f, 1.0f, 1.0f};
+  SkColor edge_color_ = SK_ColorWHITE;
+  SkColor center_color_ = SK_ColorWHITE;
 
   int gridline_count_ = 1;
 
diff --git a/chrome/browser/android/vr_shell/ui_scene.cc b/chrome/browser/android/vr_shell/ui_scene.cc
index 31348537..39fd3aa 100644
--- a/chrome/browser/android/vr_shell/ui_scene.cc
+++ b/chrome/browser/android/vr_shell/ui_scene.cc
@@ -175,11 +175,11 @@
   return !GetHeadLockedElements().empty();
 }
 
-void UiScene::SetBackgroundColor(const vr::Colorf& color) {
+void UiScene::SetBackgroundColor(const SkColor& color) {
   background_color_ = color;
 }
 
-const vr::Colorf& UiScene::GetBackgroundColor() const {
+const SkColor& UiScene::GetBackgroundColor() const {
   return background_color_;
 }
 
diff --git a/chrome/browser/android/vr_shell/ui_scene.h b/chrome/browser/android/vr_shell/ui_scene.h
index 5a313fd1..119fec70 100644
--- a/chrome/browser/android/vr_shell/ui_scene.h
+++ b/chrome/browser/android/vr_shell/ui_scene.h
@@ -11,7 +11,7 @@
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
 #include "chrome/browser/android/vr_shell/ui_elements/ui_element_debug_id.h"
-#include "device/vr/vr_types.h"
+#include "third_party/skia/include/core/SkColor.h"
 
 namespace base {
 class ListValue;
@@ -66,8 +66,8 @@
   std::vector<const UiElement*> GetHeadLockedElements() const;
   bool HasVisibleHeadLockedElements() const;
 
-  void SetBackgroundColor(const vr::Colorf& color);
-  const vr::Colorf& GetBackgroundColor() const;
+  void SetBackgroundColor(const SkColor& color);
+  const SkColor& GetBackgroundColor() const;
   void SetBackgroundDistance(float distance);
   float GetBackgroundDistance() const;
 
@@ -85,7 +85,7 @@
 
   std::vector<std::unique_ptr<UiElement>> ui_elements_;
   UiElement* content_element_ = nullptr;
-  vr::Colorf background_color_ = {0.1f, 0.1f, 0.1f, 1.0f};
+  SkColor background_color_ = 0xFF1A1A1A;
   float background_distance_ = 10.0f;
   bool webvr_rendering_enabled_ = true;
   bool gl_initialized_ = false;
diff --git a/chrome/browser/android/vr_shell/ui_scene_manager.cc b/chrome/browser/android/vr_shell/ui_scene_manager.cc
index d064981..8a242b1 100644
--- a/chrome/browser/android/vr_shell/ui_scene_manager.cc
+++ b/chrome/browser/android/vr_shell/ui_scene_manager.cc
@@ -364,8 +364,7 @@
   floor_->set_center_color(color_scheme().floor);
   floor_->set_edge_color(color_scheme().horizon);
   floor_grid_->set_center_color(color_scheme().floor_grid);
-  vr::Colorf floor_grid_edge_color = color_scheme().floor_grid;
-  floor_grid_edge_color.a = 0.0;
+  SkColor floor_grid_edge_color = SkColorSetA(color_scheme().floor_grid, 0);
   floor_grid_->set_edge_color(floor_grid_edge_color);
 }
 
diff --git a/chrome/browser/android/vr_shell/ui_scene_manager_unittest.cc b/chrome/browser/android/vr_shell/ui_scene_manager_unittest.cc
index 5afb29e..bf7078e 100644
--- a/chrome/browser/android/vr_shell/ui_scene_manager_unittest.cc
+++ b/chrome/browser/android/vr_shell/ui_scene_manager_unittest.cc
@@ -84,11 +84,6 @@
   std::unique_ptr<MockBrowserInterface> browser_;
   std::unique_ptr<UiScene> scene_;
   std::unique_ptr<UiSceneManager> manager_;
-
-  bool ColorEquals(vr::Colorf expected, vr::Colorf actual) {
-    return (expected.r == actual.r) && (expected.g == actual.g) &&
-           (expected.b == actual.b) && (expected.a == actual.a);
-  }
 };
 
 TEST_F(UiSceneManagerTest, ExitPresentAndFullscreenOnAppButtonClick) {
@@ -158,7 +153,7 @@
   MakeManager(kNotInCct, kNotInWebVr);
 
   // Hold onto the background color to make sure it changes.
-  vr::Colorf initial_background = scene_->GetBackgroundColor();
+  SkColor initial_background = scene_->GetBackgroundColor();
 
   for (const auto& element : scene_->GetUiElements()) {
     SCOPED_TRACE(element->debug_id());
@@ -181,7 +176,7 @@
   {
     SCOPED_TRACE("Entered Fullsceen");
     // Make sure background has changed for fullscreen.
-    EXPECT_FALSE(ColorEquals(initial_background, scene_->GetBackgroundColor()));
+    EXPECT_NE(initial_background, scene_->GetBackgroundColor());
   }
 
   // Exit fullscreen.
@@ -196,7 +191,7 @@
   }
   {
     SCOPED_TRACE("Exited Fullsceen");
-    EXPECT_TRUE(ColorEquals(initial_background, scene_->GetBackgroundColor()));
+    EXPECT_EQ(initial_background, scene_->GetBackgroundColor());
   }
 }
 
diff --git a/chrome/browser/android/vr_shell/vr_shell_gl.cc b/chrome/browser/android/vr_shell/vr_shell_gl.cc
index 5369bbf..9fffc43 100644
--- a/chrome/browser/android/vr_shell/vr_shell_gl.cc
+++ b/chrome/browser/android/vr_shell/vr_shell_gl.cc
@@ -1132,9 +1132,11 @@
     glEnable(GL_DEPTH_TEST);
     glDepthMask(GL_TRUE);
 
-    const vr::Colorf& backgroundColor = scene_->GetBackgroundColor();
-    glClearColor(backgroundColor.r, backgroundColor.g, backgroundColor.b,
-                 backgroundColor.a);
+    const SkColor backgroundColor = scene_->GetBackgroundColor();
+    glClearColor(SkColorGetR(backgroundColor) / 255.0,
+                 SkColorGetG(backgroundColor) / 255.0,
+                 SkColorGetB(backgroundColor) / 255.0,
+                 SkColorGetA(backgroundColor) / 255.0);
     glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
   }
   std::vector<const UiElement*> elements = scene_->GetWorldElements();
diff --git a/chrome/browser/android/vr_shell/vr_shell_renderer.cc b/chrome/browser/android/vr_shell/vr_shell_renderer.cc
index b1ddae5..1390d68 100644
--- a/chrome/browser/android/vr_shell/vr_shell_renderer.cc
+++ b/chrome/browser/android/vr_shell/vr_shell_renderer.cc
@@ -262,6 +262,11 @@
   }
 }
 
+void SetColorUniform(GLuint handle, SkColor c) {
+  glUniform4f(handle, SkColorGetR(c) / 255.0, SkColorGetG(c) / 255.0,
+              SkColorGetB(c) / 255.0, SkColorGetA(c) / 255.0);
+}
+
 }  // namespace
 
 namespace vr_shell {
@@ -692,8 +697,8 @@
 }
 
 void GradientQuadRenderer::Draw(const vr::Mat4f& view_proj_matrix,
-                                const vr::Colorf& edge_color,
-                                const vr::Colorf& center_color,
+                                SkColor edge_color,
+                                SkColor center_color,
                                 float opacity) {
   PrepareToDraw(model_view_proj_matrix_handle_, view_proj_matrix);
 
@@ -701,10 +706,8 @@
   glUniform1f(scene_radius_handle_, kHalfSize);
 
   // Set the edge color to the fog color so that it seems to fade out.
-  glUniform4f(edge_color_handle_, edge_color.r, edge_color.g, edge_color.b,
-              edge_color.a);
-  glUniform4f(center_color_handle_, center_color.r, center_color.g,
-              center_color.b, center_color.a);
+  SetColorUniform(edge_color_handle_, edge_color);
+  SetColorUniform(center_color_handle_, center_color);
   glUniform1f(opacity_handle_, opacity);
 
   glDrawArrays(GL_TRIANGLES, 0, kVerticesNumber);
@@ -726,8 +729,8 @@
 }
 
 void GradientGridRenderer::Draw(const vr::Mat4f& view_proj_matrix,
-                                const vr::Colorf& edge_color,
-                                const vr::Colorf& center_color,
+                                SkColor edge_color,
+                                SkColor center_color,
                                 int gridline_count,
                                 float opacity) {
   // In case the tile number changed we have to regenerate the grid lines.
@@ -745,10 +748,8 @@
   glUniform1f(scene_radius_handle_, kHalfSize);
 
   // Set the edge color to the fog color so that it seems to fade out.
-  glUniform4f(edge_color_handle_, edge_color.r, edge_color.g, edge_color.b,
-              edge_color.a);
-  glUniform4f(center_color_handle_, center_color.r, center_color.g,
-              center_color.b, center_color.a);
+  SetColorUniform(edge_color_handle_, edge_color);
+  SetColorUniform(center_color_handle_, center_color);
   glUniform1f(opacity_handle_, opacity);
 
   // Draw the grid.
diff --git a/chrome/browser/android/vr_shell/vr_shell_renderer.h b/chrome/browser/android/vr_shell/vr_shell_renderer.h
index 7a046ab..5759fea 100644
--- a/chrome/browser/android/vr_shell/vr_shell_renderer.h
+++ b/chrome/browser/android/vr_shell/vr_shell_renderer.h
@@ -233,8 +233,8 @@
   ~GradientQuadRenderer() override;
 
   void Draw(const vr::Mat4f& view_proj_matrix,
-            const vr::Colorf& edge_color,
-            const vr::Colorf& center_color,
+            SkColor edge_color,
+            SkColor center_color,
             float opacity);
 
  private:
@@ -253,8 +253,8 @@
   ~GradientGridRenderer() override;
 
   void Draw(const vr::Mat4f& view_proj_matrix,
-            const vr::Colorf& edge_color,
-            const vr::Colorf& center_color,
+            SkColor edge_color,
+            SkColor center_color,
             int gridline_count,
             float opacity);
 
diff --git a/chrome/browser/chrome_content_browser_manifest_overlay.json b/chrome/browser/chrome_content_browser_manifest_overlay.json
index f03b03a8..887404b 100644
--- a/chrome/browser/chrome_content_browser_manifest_overlay.json
+++ b/chrome/browser/chrome_content_browser_manifest_overlay.json
@@ -50,6 +50,7 @@
           "dom_distiller::mojom::DistillerJavaScriptService",
           "extensions::KeepAlive",
           "extensions::mime_handler::MimeHandlerService",
+          "extensions::mojom::InlineInstall",
           "media_router::mojom::MediaRouter",
           "page_load_metrics::mojom::PageLoadMetrics",
           "password_manager::mojom::CredentialManager",
diff --git a/chrome/browser/chromeos/BUILD.gn b/chrome/browser/chromeos/BUILD.gn
index 6427015..f22e7aa 100644
--- a/chrome/browser/chromeos/BUILD.gn
+++ b/chrome/browser/chromeos/BUILD.gn
@@ -463,6 +463,8 @@
     "events/system_key_event_listener.h",
     "events/xinput_hierarchy_changed_event_listener.cc",
     "events/xinput_hierarchy_changed_event_listener.h",
+    "extensions/active_tab_permission_granter_delegate_chromeos.cc",
+    "extensions/active_tab_permission_granter_delegate_chromeos.h",
     "extensions/default_app_order.cc",
     "extensions/default_app_order.h",
     "extensions/device_local_account_external_policy_loader.cc",
@@ -1609,6 +1611,7 @@
     "drive/write_on_cache_file_unittest.cc",
     "events/event_rewriter_unittest.cc",
     "events/keyboard_driven_event_rewriter_unittest.cc",
+    "extensions/active_tab_permission_granter_delegate_chromeos_unittest.cc",
     "extensions/default_app_order_unittest.cc",
     "extensions/device_local_account_external_policy_loader_unittest.cc",
     "extensions/device_local_account_management_policy_provider_unittest.cc",
diff --git a/chrome/browser/chromeos/extensions/active_tab_permission_granter_delegate_chromeos.cc b/chrome/browser/chromeos/extensions/active_tab_permission_granter_delegate_chromeos.cc
new file mode 100644
index 0000000..03377d5
--- /dev/null
+++ b/chrome/browser/chromeos/extensions/active_tab_permission_granter_delegate_chromeos.cc
@@ -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.
+
+#include "chrome/browser/chromeos/extensions/active_tab_permission_granter_delegate_chromeos.h"
+
+#include "chrome/browser/chromeos/extensions/public_session_permission_helper.h"
+#include "chrome/browser/profiles/profiles_state.h"
+#include "extensions/common/extension.h"
+#include "extensions/common/permissions/api_permission.h"
+
+namespace extensions {
+
+ActiveTabPermissionGranterDelegateChromeOS::
+    ActiveTabPermissionGranterDelegateChromeOS() {}
+
+ActiveTabPermissionGranterDelegateChromeOS::
+    ~ActiveTabPermissionGranterDelegateChromeOS() {}
+
+bool ActiveTabPermissionGranterDelegateChromeOS::ShouldGrantActiveTab(
+    const Extension* extension,
+    content::WebContents* web_contents) {
+  bool already_handled = permission_helper::HandlePermissionRequest(
+      *extension, {APIPermission::kActiveTab}, web_contents,
+      permission_helper::RequestResolvedCallback(),
+      permission_helper::PromptFactory());
+
+  return already_handled && permission_helper::PermissionAllowed(
+                                extension, APIPermission::kActiveTab);
+}
+
+}  // namespace extensions
diff --git a/chrome/browser/chromeos/extensions/active_tab_permission_granter_delegate_chromeos.h b/chrome/browser/chromeos/extensions/active_tab_permission_granter_delegate_chromeos.h
new file mode 100644
index 0000000..83ecfce1e
--- /dev/null
+++ b/chrome/browser/chromeos/extensions/active_tab_permission_granter_delegate_chromeos.h
@@ -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.
+
+#ifndef CHROME_BROWSER_CHROMEOS_EXTENSIONS_ACTIVE_TAB_PERMISSION_GRANTER_DELEGATE_CHROMEOS_H_
+#define CHROME_BROWSER_CHROMEOS_EXTENSIONS_ACTIVE_TAB_PERMISSION_GRANTER_DELEGATE_CHROMEOS_H_
+
+#include "base/macros.h"
+#include "chrome/browser/extensions/active_tab_permission_granter.h"
+
+namespace content {
+class WebContents;
+}
+
+namespace extensions {
+class Extension;
+
+// In Public Sessions, apps and extensions are force-installed by admin policy
+// so the user does not get a chance to review the permissions for these apps.
+// This is not acceptable from a security standpoint, so we show a permission
+// prompt the first time an extension tries to use activeTab permission (unless
+// the extension is whitelisted).
+class ActiveTabPermissionGranterDelegateChromeOS
+    : public ActiveTabPermissionGranter::Delegate {
+ public:
+  ActiveTabPermissionGranterDelegateChromeOS();
+  ~ActiveTabPermissionGranterDelegateChromeOS() override;
+
+  // ActiveTabPermissionGranter::Delegate
+  bool ShouldGrantActiveTab(const Extension* extension,
+                            content::WebContents* web_contents) override;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(ActiveTabPermissionGranterDelegateChromeOS);
+};
+
+}  // namespace extensions
+
+#endif  // CHROME_BROWSER_CHROMEOS_EXTENSIONS_ACTIVE_TAB_PERMISSION_GRANTER_DELEGATE_CHROMEOS_H_
diff --git a/chrome/browser/chromeos/extensions/active_tab_permission_granter_delegate_chromeos_unittest.cc b/chrome/browser/chromeos/extensions/active_tab_permission_granter_delegate_chromeos_unittest.cc
new file mode 100644
index 0000000..9cca475
--- /dev/null
+++ b/chrome/browser/chromeos/extensions/active_tab_permission_granter_delegate_chromeos_unittest.cc
@@ -0,0 +1,85 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/chromeos/extensions/active_tab_permission_granter_delegate_chromeos.h"
+
+#include <string>
+
+#include "base/run_loop.h"
+#include "chrome/browser/chromeos/extensions/public_session_permission_helper.h"
+#include "chrome/test/base/chrome_render_view_host_test_harness.h"
+#include "chromeos/login/scoped_test_public_session_login_state.h"
+#include "extensions/browser/extension_dialog_auto_confirm.h"
+#include "extensions/common/extension.h"
+#include "extensions/common/extension_builder.h"
+#include "extensions/common/value_builder.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace extensions {
+
+namespace {
+
+const char kWhitelistedId[] = "cbkkbcmdlboombapidmoeolnmdacpkch";
+// Use an extension ID that will never be whitelisted.
+const char kNonWhitelistedId[] = "bogus";
+
+scoped_refptr<Extension> CreateExtension(const std::string& id) {
+  return ExtensionBuilder()
+      .SetManifest(
+          DictionaryBuilder().Set("name", "test").Set("version", "0.1").Build())
+      .SetID(id)
+      .Build();
+}
+
+}  // namespace
+
+class ActiveTabPermissionGranterDelegateChromeOSTest
+    : public ChromeRenderViewHostTestHarness {
+ protected:
+  void SetUp() override;
+  void TearDown() override;
+
+  ActiveTabPermissionGranterDelegateChromeOS delegate_;
+  chromeos::ScopedTestPublicSessionLoginState login_state_;
+};
+
+void ActiveTabPermissionGranterDelegateChromeOSTest::SetUp() {
+  ChromeRenderViewHostTestHarness::SetUp();
+}
+
+void ActiveTabPermissionGranterDelegateChromeOSTest::TearDown() {
+  permission_helper::ResetPermissionsForTesting();
+  ChromeRenderViewHostTestHarness::TearDown();
+}
+
+TEST_F(ActiveTabPermissionGranterDelegateChromeOSTest, GrantedForWhitelisted) {
+  auto extension = CreateExtension(kWhitelistedId);
+  EXPECT_TRUE(delegate_.ShouldGrantActiveTab(extension.get(), nullptr));
+}
+
+TEST_F(ActiveTabPermissionGranterDelegateChromeOSTest,
+       RejectedForNonWhitelisted) {
+  auto extension = CreateExtension(kNonWhitelistedId);
+  // Deny the permission request.
+  ScopedTestDialogAutoConfirm auto_confirm(ScopedTestDialogAutoConfirm::CANCEL);
+  // First request is always rejected (by design).
+  EXPECT_FALSE(delegate_.ShouldGrantActiveTab(extension.get(), nullptr));
+  // Spin the loop, allowing the dialog to be resolved.
+  base::RunLoop().RunUntilIdle();
+  // Dialog result is propagated here, permission request is rejected.
+  EXPECT_FALSE(delegate_.ShouldGrantActiveTab(extension.get(), nullptr));
+}
+
+TEST_F(ActiveTabPermissionGranterDelegateChromeOSTest,
+       GrantedForNonWhitelisted) {
+  auto extension = CreateExtension(kNonWhitelistedId);
+  // Allow the permission request.
+  ScopedTestDialogAutoConfirm auto_confirm(ScopedTestDialogAutoConfirm::ACCEPT);
+  EXPECT_FALSE(delegate_.ShouldGrantActiveTab(extension.get(), nullptr));
+  base::RunLoop().RunUntilIdle();
+  // The permission request is granted now.
+  EXPECT_TRUE(delegate_.ShouldGrantActiveTab(extension.get(), nullptr));
+}
+
+}  // namespace extensions
diff --git a/chrome/browser/chromeos/extensions/device_local_account_management_policy_provider.cc b/chrome/browser/chromeos/extensions/device_local_account_management_policy_provider.cc
index 92948d8..48ef2a1a 100644
--- a/chrome/browser/chromeos/extensions/device_local_account_management_policy_provider.cc
+++ b/chrome/browser/chromeos/extensions/device_local_account_management_policy_provider.cc
@@ -389,7 +389,9 @@
     // doesn't trigger a permission warning on install though, so blocking is
     // somewhat at odds with the spirit of the API - however I presume the API
     // design assumes user-installed extensions, which we don't have here.
-    // "activeTab",
+    // Whitelisted because it's restricted now (asks user for permission the
+    // first time an extension tries to use it).
+    "activeTab",
 
     // Schedule code to run at future times.
     "alarms",
diff --git a/chrome/browser/chromeos/extensions/public_session_permission_helper.cc b/chrome/browser/chromeos/extensions/public_session_permission_helper.cc
index c54a197..33b072f8 100644
--- a/chrome/browser/chromeos/extensions/public_session_permission_helper.cc
+++ b/chrome/browser/chromeos/extensions/public_session_permission_helper.cc
@@ -15,14 +15,20 @@
 #include "base/lazy_instance.h"
 #include "base/macros.h"
 #include "base/memory/ptr_util.h"
+#include "chrome/browser/chromeos/extensions/device_local_account_management_policy_provider.h"
 #include "chrome/browser/extensions/extension_install_prompt.h"
+#include "chrome/browser/profiles/profiles_state.h"
+#include "chrome/grit/generated_resources.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/web_contents.h"
 #include "extensions/common/extension.h"
 #include "extensions/common/extension_id.h"
+#include "extensions/common/permissions/api_permission_set.h"
 #include "extensions/common/permissions/manifest_permission_set.h"
+#include "extensions/common/permissions/permission_message.h"
 #include "extensions/common/permissions/permission_set.h"
 #include "extensions/common/url_pattern_set.h"
+#include "ui/base/l10n/l10n_util.h"
 
 namespace extensions {
 namespace permission_helper {
@@ -34,6 +40,11 @@
   return base::MakeUnique<ExtensionInstallPrompt>(web_contents);
 }
 
+bool PermissionCheckNeeded(const Extension* extension) {
+  return !chromeos::DeviceLocalAccountManagementPolicyProvider::IsWhitelisted(
+      extension->id());
+}
+
 // This class is the internal implementation of HandlePermissionRequest(). It
 // contains the actual prompt showing and resolving logic, and it caches the
 // user choices.
@@ -43,12 +54,15 @@
   PublicSessionPermissionHelper(PublicSessionPermissionHelper&& other);
   ~PublicSessionPermissionHelper();
 
-  void HandlePermissionRequestImpl(const Extension& extension,
+  bool HandlePermissionRequestImpl(const Extension& extension,
                                    const PermissionIDSet& requested_permissions,
                                    content::WebContents* web_contents,
                                    const RequestResolvedCallback& callback,
                                    const PromptFactory& prompt_factory);
 
+  bool PermissionAllowedImpl(const Extension* extension,
+                             APIPermission::ID permission);
+
  private:
   void ResolvePermissionPrompt(const ExtensionInstallPrompt* prompt,
                                const PermissionIDSet& unprompted_permissions,
@@ -82,13 +96,18 @@
 
 PublicSessionPermissionHelper::~PublicSessionPermissionHelper() {}
 
-void PublicSessionPermissionHelper::HandlePermissionRequestImpl(
+bool PublicSessionPermissionHelper::HandlePermissionRequestImpl(
     const Extension& extension,
     const PermissionIDSet& requested_permissions,
     content::WebContents* web_contents,
     const RequestResolvedCallback& callback,
     const PromptFactory& prompt_factory) {
-  CHECK(web_contents);
+  DCHECK(profiles::IsPublicSession());
+  if (!PermissionCheckNeeded(&extension)) {
+    if (!callback.is_null())
+      callback.Run(requested_permissions);
+    return true;
+  }
 
   PermissionIDSet unresolved_permissions = PermissionIDSet::Difference(
       requested_permissions, allowed_permission_set_);
@@ -96,20 +115,22 @@
       unresolved_permissions, denied_permission_set_);
   if (unresolved_permissions.empty()) {
     // All requested permissions are already resolved.
-    callback.Run(FilterAllowedPermissions(requested_permissions));
-    return;
+    if (!callback.is_null())
+      callback.Run(FilterAllowedPermissions(requested_permissions));
+    return true;
   }
 
   // Since not all permissions are resolved yet, queue the callback to be called
   // when all of them are resolved.
-  callbacks_.push_back(RequestCallback(callback, requested_permissions));
+  if (!callback.is_null())
+    callbacks_.push_back(RequestCallback(callback, requested_permissions));
 
   PermissionIDSet unprompted_permissions = PermissionIDSet::Difference(
       unresolved_permissions, prompted_permission_set_);
   if (unprompted_permissions.empty()) {
     // Some permissions aren't resolved yet, but they are currently being
     // prompted for, so no need to show a prompt.
-    return;
+    return false;
   }
 
   // Some permissions need prompting, setup the prompt and show it.
@@ -121,6 +142,19 @@
   auto permission_set = base::MakeUnique<PermissionSet>(
       new_apis, ManifestPermissionSet(), URLPatternSet(), URLPatternSet());
   auto prompt = prompt_factory.Run(web_contents);
+
+  auto permissions_prompt = base::MakeUnique<ExtensionInstallPrompt::Prompt>(
+      ExtensionInstallPrompt::PERMISSIONS_PROMPT);
+  // activeTab has no permission message by default, so one is added here.
+  if (unprompted_permissions.ContainsID(APIPermission::kActiveTab)) {
+    PermissionMessages messages;
+    messages.push_back(PermissionMessage(
+        l10n_util::GetStringUTF16(IDS_EXTENSION_PROMPT_WARNING_CURRENT_HOST),
+        extensions::PermissionIDSet()));
+    permissions_prompt->AddPermissions(
+        messages, ExtensionInstallPrompt::REGULAR_PERMISSIONS);
+  }
+
   // This Unretained is safe because the lifetime of this object is until
   // process exit.
   prompt->ShowDialog(
@@ -129,11 +163,20 @@
                  std::move(unprompted_permissions)),
       &extension,
       nullptr,  // Use the extension icon.
-      base::MakeUnique<ExtensionInstallPrompt::Prompt>(
-          ExtensionInstallPrompt::PERMISSIONS_PROMPT),
+      std::move(permissions_prompt),
       std::move(permission_set),
       ExtensionInstallPrompt::GetDefaultShowDialogCallback());
   prompts_.insert(std::move(prompt));
+
+  return false;
+}
+
+bool PublicSessionPermissionHelper::PermissionAllowedImpl(
+    const Extension* extension,
+    APIPermission::ID permission) {
+  DCHECK(profiles::IsPublicSession());
+  return !PermissionCheckNeeded(extension) ||
+         allowed_permission_set_.ContainsID(permission);
 }
 
 void PublicSessionPermissionHelper::ResolvePermissionPrompt(
@@ -203,7 +246,7 @@
 
 }  // namespace
 
-void HandlePermissionRequest(const Extension& extension,
+bool HandlePermissionRequest(const Extension& extension,
                              const PermissionIDSet& requested_permissions,
                              content::WebContents* web_contents,
                              const RequestResolvedCallback& callback,
@@ -216,6 +259,13 @@
       extension, requested_permissions, web_contents, callback, factory);
 }
 
+bool PermissionAllowed(const Extension* extension,
+                       APIPermission::ID permission) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+  return g_helpers.Get()[extension->id()].PermissionAllowedImpl(extension,
+                                                                permission);
+}
+
 void ResetPermissionsForTesting() {
   // Clear out the std::map between tests. Just setting g_helpers to
   // LAZY_INSTANCE_INITIALIZER again causes a memory leak (because of the
diff --git a/chrome/browser/chromeos/extensions/public_session_permission_helper.h b/chrome/browser/chromeos/extensions/public_session_permission_helper.h
index 445f879..251337f 100644
--- a/chrome/browser/chromeos/extensions/public_session_permission_helper.h
+++ b/chrome/browser/chromeos/extensions/public_session_permission_helper.h
@@ -37,7 +37,8 @@
 //
 // This function sets up the prompt asking the user for additional
 // permission(s), handles the result, caches it, and then runs the callback with
-// the allowed permissions as the argument.
+// the allowed permissions as the argument. It returns true if this
+// permission(s) is already resolved, and false otherwise.
 //
 // The user will be prompted about a certain permission only once, and that
 // choice will be cached and used in any subsequent requests that use the same
@@ -47,14 +48,21 @@
 //
 // Caller must ensure that web_contents is valid. Must be called on UI thread.
 //
+// Callback can be null (permission_helper::RequestResolvedCallback()), in which
+// case it's not invoked but the permission prompt is still shown.
+//
 // Passing in a null prompt_factory (permission_helper::PromptFactory())
 // callback gets the default behaviour (ie. it is is used only for tests).
-void HandlePermissionRequest(const Extension& extension,
+bool HandlePermissionRequest(const Extension& extension,
                              const PermissionIDSet& requested_permissions,
                              content::WebContents* web_contents,
                              const RequestResolvedCallback& callback,
                              const PromptFactory& prompt_factory);
 
+// Returns true if user granted this permission to the extension.
+bool PermissionAllowed(const Extension* extension,
+                       APIPermission::ID permission);
+
 // Used to completely reset state in between tests.
 void ResetPermissionsForTesting();
 
diff --git a/chrome/browser/chromeos/extensions/public_session_permission_helper_unittest.cc b/chrome/browser/chromeos/extensions/public_session_permission_helper_unittest.cc
index 0ca18d0..56b71b1 100644
--- a/chrome/browser/chromeos/extensions/public_session_permission_helper_unittest.cc
+++ b/chrome/browser/chromeos/extensions/public_session_permission_helper_unittest.cc
@@ -16,6 +16,7 @@
 #include "chrome/browser/extensions/extension_install_prompt.h"
 #include "chrome/common/extensions/extension_test_util.h"
 #include "chrome/test/base/chrome_render_view_host_test_harness.h"
+#include "chromeos/login/scoped_test_public_session_login_state.h"
 #include "content/public/browser/web_contents.h"
 #include "extensions/browser/extension_dialog_auto_confirm.h"
 #include "extensions/common/extension.h"
@@ -30,12 +31,17 @@
 using extensions::Manifest;
 using Result = ExtensionInstallPrompt::Result;
 
+namespace extensions {
+namespace permission_helper {
 namespace {
 
 auto permission_a = APIPermission::kAudio;
 auto permission_b = APIPermission::kBookmark;
 bool did_show_dialog;
 
+const char kWhitelistedId[] = "cbkkbcmdlboombapidmoeolnmdacpkch";
+const char kNonWhitelistedId[] = "bogus";
+
 scoped_refptr<Extension> LoadManifestHelper(const std::string& id) {
   std::string error;
   scoped_refptr<Extension> extension = LoadManifestUnchecked(
@@ -51,6 +57,11 @@
   return tmp;
 }
 
+base::Callback<void(const PermissionIDSet&)> BindQuitLoop(base::RunLoop* loop) {
+  return base::Bind(
+      [](base::RunLoop* loop, const PermissionIDSet&) { loop->Quit(); }, loop);
+}
+
 class ProgrammableInstallPrompt
     : public ExtensionInstallPrompt,
       public base::SupportsWeakPtr<ProgrammableInstallPrompt> {
@@ -83,9 +94,6 @@
 
 }  // namespace
 
-namespace extensions {
-namespace permission_helper {
-
 class PublicSessionPermissionHelperTest
     : public ChromeRenderViewHostTestHarness {
  public:
@@ -111,6 +119,8 @@
 
   std::vector<PermissionIDSet> allowed_permissions_;
 
+  chromeos::ScopedTestPublicSessionLoginState login_state_;
+
  private:
   DISALLOW_COPY_AND_ASSIGN(PublicSessionPermissionHelperTest);
 };
@@ -251,5 +261,58 @@
   EXPECT_TRUE(allowed_permissions_.at(1).Equals({permission_b}));
 }
 
+TEST_F(PublicSessionPermissionHelperTest, WhitelistedExtension) {
+  auto extension = LoadManifestHelper(kWhitelistedId);
+  // Whitelisted extension can use any permission.
+  EXPECT_TRUE(PermissionAllowed(extension.get(), permission_a));
+  EXPECT_TRUE(PermissionAllowed(extension.get(), permission_b));
+  // Whitelisted extension is already handled (no permission prompt needed).
+  EXPECT_TRUE(HandlePermissionRequest(*extension, {permission_a},
+                                      web_contents(), RequestResolvedCallback(),
+                                      PromptFactory()));
+  EXPECT_TRUE(PermissionAllowed(extension.get(), permission_a));
+  EXPECT_TRUE(PermissionAllowed(extension.get(), permission_b));
+}
+
+TEST_F(PublicSessionPermissionHelperTest, NonWhitelistedExtension) {
+  auto extension = LoadManifestHelper(kNonWhitelistedId);
+  EXPECT_FALSE(PermissionAllowed(extension.get(), permission_a));
+  EXPECT_FALSE(PermissionAllowed(extension.get(), permission_b));
+  // Prompt for permission_a, grant it, verify.
+  {
+    ScopedTestDialogAutoConfirm auto_confirm(
+        ScopedTestDialogAutoConfirm::ACCEPT);
+    // Permission not handled yet, need to show a prompt.
+    base::RunLoop loop;
+    EXPECT_FALSE(HandlePermissionRequest(*extension, {permission_a},
+                                         web_contents(), BindQuitLoop(&loop),
+                                         PromptFactory()));
+    loop.Run();
+    EXPECT_TRUE(PermissionAllowed(extension.get(), permission_a));
+    EXPECT_FALSE(PermissionAllowed(extension.get(), permission_b));
+  }
+  // Already handled (allow), doesn't show a prompt.
+  EXPECT_TRUE(HandlePermissionRequest(*extension, {permission_a},
+                                      web_contents(), RequestResolvedCallback(),
+                                      PromptFactory()));
+  // Prompt for permission_b, deny it, verify.
+  {
+    ScopedTestDialogAutoConfirm auto_confirm(
+        ScopedTestDialogAutoConfirm::CANCEL);
+    // Permission not handled yet, need to show a prompt.
+    base::RunLoop loop;
+    EXPECT_FALSE(HandlePermissionRequest(*extension, {permission_b},
+                                         web_contents(), BindQuitLoop(&loop),
+                                         PromptFactory()));
+    loop.Run();
+    EXPECT_TRUE(PermissionAllowed(extension.get(), permission_a));
+    EXPECT_FALSE(PermissionAllowed(extension.get(), permission_b));
+  }
+  // Already handled (deny), doesn't show a prompt.
+  EXPECT_TRUE(HandlePermissionRequest(*extension, {permission_b},
+                                      web_contents(), RequestResolvedCallback(),
+                                      PromptFactory()));
+}
+
 }  // namespace permission_helper
 }  // namespace extensions
diff --git a/chrome/browser/chromeos/login/users/chrome_user_manager_impl.cc b/chrome/browser/chromeos/login/users/chrome_user_manager_impl.cc
index 660055c..495331f 100644
--- a/chrome/browser/chromeos/login/users/chrome_user_manager_impl.cc
+++ b/chrome/browser/chromeos/login/users/chrome_user_manager_impl.cc
@@ -32,6 +32,7 @@
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/chrome_notification_types.h"
 #include "chrome/browser/chromeos/app_mode/kiosk_app_manager.h"
+#include "chrome/browser/chromeos/extensions/active_tab_permission_granter_delegate_chromeos.h"
 #include "chrome/browser/chromeos/extensions/extension_tab_util_delegate_chromeos.h"
 #include "chrome/browser/chromeos/extensions/permissions_updater_delegate_chromeos.h"
 #include "chrome/browser/chromeos/login/demo_mode/demo_app_launcher.h"
@@ -154,6 +155,21 @@
   return true;
 }
 
+// Sets the neccessary delegates in Public Session. They will be active for the
+// whole user-session and they will go away together with the browser process
+// during logout (the browser process is destroyed during logout), ie. they are
+// not freed and they leak but that is fine.
+void SetPublicAccountDelegates() {
+  extensions::PermissionsUpdater::SetPlatformDelegate(
+      new extensions::PermissionsUpdaterDelegateChromeOS);
+
+  extensions::ExtensionTabUtil::SetPlatformDelegate(
+      new extensions::ExtensionTabUtilDelegateChromeOS);
+
+  extensions::ActiveTabPermissionGranter::SetPlatformDelegate(
+      new extensions::ActiveTabPermissionGranterDelegateChromeOS);
+}
+
 }  // namespace
 
 // static
@@ -837,19 +853,7 @@
   GetUserImageManager(user->GetAccountId())->UserLoggedIn(false, true);
   WallpaperManager::Get()->EnsureLoggedInUserWallpaperLoaded();
 
-  // In Public Sessions set the PS delegate on PermissionsUpdater (used to
-  // remove clipboard read permission from extensions in PS). This delegate will
-  // be active for the whole user-session and it will go away together with the
-  // browser process during logout (the browser process is destroyed during
-  // logout), ie. it's not freed and it leaks but that is fine.
-  extensions::PermissionsUpdater::SetPlatformDelegate(
-      new extensions::PermissionsUpdaterDelegateChromeOS);
-
-  // In Public Sessions set the PS delegate on ExtensionTabUtil (used to scrub
-  // URL down to origin for security reasons). See comment above about
-  // PermissionsUpdaterDelegateChromeOS for more info.
-  extensions::ExtensionTabUtil::SetPlatformDelegate(
-      new extensions::ExtensionTabUtilDelegateChromeOS);
+  SetPublicAccountDelegates();
 }
 
 void ChromeUserManagerImpl::KioskAppLoggedIn(user_manager::User* user) {
diff --git a/chrome/browser/chromeos/printer_detector/cups_printer_detector.cc b/chrome/browser/chromeos/printer_detector/cups_printer_detector.cc
index a8b1e8f..00168bb 100644
--- a/chrome/browser/chromeos/printer_detector/cups_printer_detector.cc
+++ b/chrome/browser/chromeos/printer_detector/cups_printer_detector.cc
@@ -250,7 +250,7 @@
   void SetUpPrinterDone(std::unique_ptr<SetUpPrinterData> data,
                         PrinterSetupResult result) {
     DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-    if (result == PrinterSetupResult::SUCCESS) {
+    if (result == PrinterSetupResult::kSuccess) {
       if (data->is_new) {
         // We aren't done with data->printer yet, so we have to copy it instead
         // of moving it.
diff --git a/chrome/browser/chromeos/printing/printer_configurer.cc b/chrome/browser/chromeos/printing/printer_configurer.cc
index a7c812b..c9e011e1a5 100644
--- a/chrome/browser/chromeos/printing/printer_configurer.cc
+++ b/chrome/browser/chromeos/printing/printer_configurer.cc
@@ -72,25 +72,25 @@
     PrinterSetupResult result;
     switch (result_code) {
       case debugd::CupsResult::CUPS_SUCCESS:
-        result = PrinterSetupResult::SUCCESS;
+        result = PrinterSetupResult::kSuccess;
         break;
       case debugd::CupsResult::CUPS_INVALID_PPD:
-        result = PrinterSetupResult::INVALID_PPD;
+        result = PrinterSetupResult::kInvalidPpd;
         break;
       case debugd::CupsResult::CUPS_AUTOCONF_FAILURE:
         // There are other reasons autoconf fails but this is the most likely.
-        result = PrinterSetupResult::PRINTER_UNREACHABLE;
+        result = PrinterSetupResult::kPrinterUnreachable;
         break;
       case debugd::CupsResult::CUPS_LPADMIN_FAILURE:
         // Printers should always be configurable by lpadmin.
         NOTREACHED() << "lpadmin could not add the printer";
-        result = PrinterSetupResult::FATAL_ERROR;
+        result = PrinterSetupResult::kFatalError;
         break;
       case debugd::CupsResult::CUPS_FATAL:
       default:
         // We have no idea.  It must be fatal.
         LOG(ERROR) << "Unrecognized printer setup error: " << result_code;
-        result = PrinterSetupResult::FATAL_ERROR;
+        result = PrinterSetupResult::kFatalError;
         break;
     }
 
@@ -101,7 +101,7 @@
     // The callback is expected to run on the UI thread.
     DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
     LOG(WARNING) << "Could not contact debugd";
-    cb.Run(PrinterSetupResult::DBUS_ERROR);
+    cb.Run(PrinterSetupResult::kDbusError);
   }
 
   void AddPrinter(const Printer& printer,
@@ -131,14 +131,14 @@
         AddPrinter(printer, ppd_contents, cb);
         break;
       case printing::PpdProvider::CallbackResultCode::NOT_FOUND:
-        cb.Run(PPD_NOT_FOUND);
+        cb.Run(PrinterSetupResult::kPpdNotFound);
         break;
       case printing::PpdProvider::CallbackResultCode::SERVER_ERROR:
-        cb.Run(PPD_UNRETRIEVABLE);
+        cb.Run(PrinterSetupResult::kPpdUnretrievable);
         break;
       case printing::PpdProvider::CallbackResultCode::INTERNAL_ERROR:
-        // TODO(skau): Add PPD_TOO_LARGE when it's reported by the PpdProvider.
-        cb.Run(FATAL_ERROR);
+        // TODO(skau): Add kPpdTooLarge when it's reported by the PpdProvider.
+        cb.Run(PrinterSetupResult::kFatalError);
         break;
     }
   }
diff --git a/chrome/browser/chromeos/printing/printer_configurer.h b/chrome/browser/chromeos/printing/printer_configurer.h
index 5aab7a3..bd97c26 100644
--- a/chrome/browser/chromeos/printing/printer_configurer.h
+++ b/chrome/browser/chromeos/printing/printer_configurer.h
@@ -15,16 +15,18 @@
 namespace chromeos {
 
 enum PrinterSetupResult {
-  FATAL_ERROR,
-  SUCCESS,              // Printer set up successfully
-  PRINTER_UNREACHABLE,  // Could not reach printer
-  DBUS_ERROR,           // Could not contact debugd
+  kFatalError = 0,          // Setup failed in an unrecognized way
+  kSuccess = 1,             // Printer set up successfully
+  kPrinterUnreachable = 2,  // Could not reach printer
+  kDbusError = 3,           // Could not contact debugd
+  // Space left for additional errors
 
   // PPD errors
-  PPD_TOO_LARGE,     // PPD exceeds size limit
-  INVALID_PPD,       // PPD rejected by cupstestppd
-  PPD_NOT_FOUND,     // Could not find PPD
-  PPD_UNRETRIEVABLE  // Could not download PPD
+  kPpdTooLarge = 10,       // PPD exceeds size limit
+  kInvalidPpd = 11,        // PPD rejected by cupstestppd
+  kPpdNotFound = 12,       // Could not find PPD
+  kPpdUnretrievable = 13,  // Could not download PPD
+  kMaxValue                // Maximum value for histograms
 };
 
 using PrinterSetupCallback = base::Callback<void(PrinterSetupResult)>;
diff --git a/chrome/browser/devtools/devtools_sanity_browsertest.cc b/chrome/browser/devtools/devtools_sanity_browsertest.cc
index 5ac3e8a1..fb8514f1 100644
--- a/chrome/browser/devtools/devtools_sanity_browsertest.cc
+++ b/chrome/browser/devtools/devtools_sanity_browsertest.cc
@@ -87,7 +87,6 @@
 #include "ui/gl/gl_switches.h"
 #include "url/gurl.h"
 
-using app_modal::AppModalDialog;
 using app_modal::JavaScriptAppModalDialog;
 using app_modal::NativeAppModalDialog;
 using content::BrowserThread;
@@ -452,11 +451,8 @@
   }
 
   NativeAppModalDialog* GetDialog() {
-    AppModalDialog* dialog = ui_test_utils::WaitForAppModalDialog();
-    EXPECT_TRUE(dialog->IsJavaScriptModalDialog());
-    JavaScriptAppModalDialog* js_dialog =
-        static_cast<JavaScriptAppModalDialog*>(dialog);
-    NativeAppModalDialog* native_dialog = js_dialog->native_dialog();
+    JavaScriptAppModalDialog* dialog = ui_test_utils::WaitForAppModalDialog();
+    NativeAppModalDialog* native_dialog = dialog->native_dialog();
     EXPECT_TRUE(native_dialog);
     return native_dialog;
   }
diff --git a/chrome/browser/extensions/BUILD.gn b/chrome/browser/extensions/BUILD.gn
index e63a44b..1a59b4a 100644
--- a/chrome/browser/extensions/BUILD.gn
+++ b/chrome/browser/extensions/BUILD.gn
@@ -7,6 +7,7 @@
 import("//chrome/common/features.gni")
 import("//extensions/features/features.gni")
 import("//media/media_options.gni")
+import("//mojo/public/tools/bindings/mojom.gni")
 import("//rlz/features/features.gni")
 
 assert(enable_extensions)
@@ -821,6 +822,7 @@
     "//chrome/browser/media/router",
     "//chrome/browser/media/router/discovery",
     "//chrome/common",
+    "//chrome/common/extensions:mojo_bindings",
     "//chrome/common/extensions/api:api_registration",
     "//chrome/common/extensions/api:extensions_features",
     "//chrome/common/safe_browsing:proto",
diff --git a/chrome/browser/extensions/active_tab_permission_granter.cc b/chrome/browser/extensions/active_tab_permission_granter.cc
index 1732c60..593628c 100644
--- a/chrome/browser/extensions/active_tab_permission_granter.cc
+++ b/chrome/browser/extensions/active_tab_permission_granter.cc
@@ -66,6 +66,8 @@
     tab_process->Send(create_message.Run(false));
 }
 
+ActiveTabPermissionGranter::Delegate* g_delegate = nullptr;
+
 }  // namespace
 
 ActiveTabPermissionGranter::ActiveTabPermissionGranter(
@@ -80,6 +82,17 @@
 
 ActiveTabPermissionGranter::~ActiveTabPermissionGranter() {}
 
+// static
+ActiveTabPermissionGranter::Delegate*
+ActiveTabPermissionGranter::SetPlatformDelegate(Delegate* delegate) {
+  // Disallow setting it twice (but allow resetting - don't forget to free in
+  // that case).
+  CHECK(!g_delegate || !delegate);
+  Delegate* previous_delegate = g_delegate;
+  g_delegate = delegate;
+  return previous_delegate;
+}
+
 void ActiveTabPermissionGranter::GrantIfRequested(const Extension* extension) {
   if (granted_extensions_.Contains(extension->id()))
     return;
@@ -89,11 +102,15 @@
 
   const PermissionsData* permissions_data = extension->permissions_data();
 
+  bool should_grant_active_tab =
+      !g_delegate ||
+      g_delegate->ShouldGrantActiveTab(extension, web_contents());
   // If the extension requested all-hosts but has had it withheld, we grant it
   // active tab-style permissions, even if it doesn't have the activeTab
   // permission in the manifest.
-  if (permissions_data->HasAPIPermission(APIPermission::kActiveTab) ||
-      permissions_data->HasWithheldImpliedAllHosts()) {
+  if (should_grant_active_tab &&
+      (permissions_data->HasWithheldImpliedAllHosts() ||
+       permissions_data->HasAPIPermission(APIPermission::kActiveTab))) {
     new_hosts.AddOrigin(UserScript::ValidUserScriptSchemes(),
                         web_contents()->GetVisibleURL().GetOrigin());
     new_apis.insert(APIPermission::kTab);
diff --git a/chrome/browser/extensions/active_tab_permission_granter.h b/chrome/browser/extensions/active_tab_permission_granter.h
index a1245a2..48fcf9a1 100644
--- a/chrome/browser/extensions/active_tab_permission_granter.h
+++ b/chrome/browser/extensions/active_tab_permission_granter.h
@@ -32,11 +32,24 @@
     : public content::WebContentsObserver,
       public extensions::ExtensionRegistryObserver {
  public:
+  // Platform specific delegate.
+  class Delegate {
+   public:
+    virtual ~Delegate() {}
+    // Platform specific check whether the activeTab permission is allowed.
+    virtual bool ShouldGrantActiveTab(
+        const Extension* extension, content::WebContents* web_contents) = 0;
+  };
+
   ActiveTabPermissionGranter(content::WebContents* web_contents,
                              int tab_id,
                              Profile* profile);
   ~ActiveTabPermissionGranter() override;
 
+  // Platform specific delegate should be set during startup. |delegate| is a
+  // singleton instance and is leaked.
+  static Delegate* SetPlatformDelegate(Delegate* delegate);
+
   // If |extension| has the activeTab or tabCapture permission, grants
   // tab-specific permissions to it until the next page navigation or refresh.
   void GrantIfRequested(const Extension* extension);
diff --git a/chrome/browser/extensions/active_tab_unittest.cc b/chrome/browser/extensions/active_tab_unittest.cc
index 908d366a..77b8e472 100644
--- a/chrome/browser/extensions/active_tab_unittest.cc
+++ b/chrome/browser/extensions/active_tab_unittest.cc
@@ -7,6 +7,8 @@
 #include <utility>
 
 #include "base/compiler_specific.h"
+#include "base/macros.h"
+#include "base/memory/ptr_util.h"
 #include "base/message_loop/message_loop.h"
 #include "base/values.h"
 #include "chrome/browser/chrome_notification_types.h"
@@ -32,6 +34,20 @@
 #include "extensions/common/permissions/permissions_data.h"
 #include "extensions/common/value_builder.h"
 
+#if defined(OS_CHROMEOS)
+#include "base/run_loop.h"
+#include "chrome/browser/chromeos/login/users/fake_chrome_user_manager.h"
+#include "chrome/browser/chromeos/login/users/scoped_test_user_manager.h"
+#include "chrome/browser/chromeos/login/users/wallpaper/wallpaper_manager.h"
+#include "chrome/browser/chromeos/profiles/profile_helper.h"
+#include "chrome/browser/chromeos/settings/device_settings_service.h"
+#include "chrome/test/base/scoped_testing_local_state.h"
+#include "chrome/test/base/testing_browser_process.h"
+#include "chromeos/login/scoped_test_public_session_login_state.h"
+#include "components/signin/core/account_id/account_id.h"
+#include "extensions/browser/extension_dialog_auto_confirm.h"
+#endif
+
 using base::DictionaryValue;
 using base::ListValue;
 using content::BrowserThread;
@@ -67,6 +83,28 @@
   PERMITTED_BOTH
 };
 
+class ActiveTabPermissionGranterTestDelegate
+    : public ActiveTabPermissionGranter::Delegate {
+ public:
+  ActiveTabPermissionGranterTestDelegate() {}
+  ~ActiveTabPermissionGranterTestDelegate() override {}
+
+  // ActiveTabPermissionGranterTestDelegate::Delegate
+  bool ShouldGrantActiveTab(const Extension* extension,
+                            content::WebContents* contents) override {
+    return should_grant_;
+  }
+
+  void SetShouldGrant(bool should_grant) {
+    should_grant_ = should_grant;
+  }
+
+ private:
+  bool should_grant_ = false;
+
+  DISALLOW_COPY_AND_ASSIGN(ActiveTabPermissionGranterTestDelegate);
+};
+
 class ActiveTabTest : public ChromeRenderViewHostTestHarness {
  protected:
   ActiveTabTest()
@@ -380,5 +418,81 @@
       tab_id() + 1, APIPermission::kTabCaptureForTab));
 }
 
+// Test that the custom platform delegate works as expected.
+TEST_F(ActiveTabTest, Delegate) {
+  auto test_delegate =
+      base::MakeUnique<ActiveTabPermissionGranterTestDelegate>();
+  ActiveTabPermissionGranter::SetPlatformDelegate(test_delegate.get());
+
+  GURL google("http://www.google.com");
+  NavigateAndCommit(google);
+
+  // Not granted because the delegate denies grant.
+  active_tab_permission_granter()->GrantIfRequested(extension.get());
+  EXPECT_TRUE(IsBlocked(extension, google));
+
+  // This time it's granted because the delegate allows it.
+  test_delegate->SetShouldGrant(true);
+  active_tab_permission_granter()->GrantIfRequested(extension.get());
+  EXPECT_TRUE(IsAllowed(extension, google));
+
+  // Cleanup :).
+  ActiveTabPermissionGranter::SetPlatformDelegate(nullptr);
+}
+
+#if defined(OS_CHROMEOS)
+// Test that the platform delegate is being set and the permission is prompted
+// for.
+TEST_F(ActiveTabTest, DelegateIsSet) {
+  // Setup, login a public account user.
+  chromeos::ScopedTestPublicSessionLoginState login_state;
+  std::string user_id = "public@account.user";
+  std::string user_email = user_id;
+  AccountId account_id = AccountId::FromUserEmailGaiaId(user_email, user_id);
+  std::string user_id_hash = chromeos::ProfileHelper::Get()->
+      GetUserIdHashByUserIdForTesting(user_id);
+  chromeos::ScopedTestDeviceSettingsService test_device_settings_service_;
+  chromeos::ScopedTestCrosSettings test_cros_settings_;
+  ScopedTestingLocalState local_state(TestingBrowserProcess::GetGlobal());
+  chromeos::ScopedTestUserManager test_user_manager_;
+  chromeos::WallpaperManager::Initialize();
+  g_browser_process->local_state()->SetString(
+      "PublicAccountPendingDataRemoval", user_email);
+  user_manager::UserManager::Get()->UserLoggedIn(
+      account_id, user_id_hash, true);
+
+  GURL google("http://www.google.com");
+  NavigateAndCommit(google);
+
+  // Grant and verify.
+  {
+    ScopedTestDialogAutoConfirm auto_confirm(
+        ScopedTestDialogAutoConfirm::ACCEPT);
+    active_tab_permission_granter()->GrantIfRequested(extension.get());
+    base::RunLoop().RunUntilIdle();
+    EXPECT_TRUE(IsBlocked(extension, google));
+    active_tab_permission_granter()->GrantIfRequested(extension.get());
+    base::RunLoop().RunUntilIdle();
+    EXPECT_TRUE(IsAllowed(extension, google));
+  }
+
+  // Deny and verify. Use a different extension so it doesn't trigger the cache.
+  {
+    ScopedTestDialogAutoConfirm auto_confirm(
+        ScopedTestDialogAutoConfirm::CANCEL);
+    active_tab_permission_granter()->GrantIfRequested(another_extension.get());
+    base::RunLoop().RunUntilIdle();
+    EXPECT_TRUE(IsBlocked(another_extension, google));
+    active_tab_permission_granter()->GrantIfRequested(another_extension.get());
+    base::RunLoop().RunUntilIdle();
+    EXPECT_TRUE(IsBlocked(another_extension, google));
+  }
+
+  // Cleanup.
+  chromeos::WallpaperManager::Shutdown();
+  free(ActiveTabPermissionGranter::SetPlatformDelegate(nullptr));
+}
+#endif
+
 }  // namespace
 }  // namespace extensions
diff --git a/chrome/browser/extensions/alert_apitest.cc b/chrome/browser/extensions/alert_apitest.cc
index 9d638b9..f32e4159 100644
--- a/chrome/browser/extensions/alert_apitest.cc
+++ b/chrome/browser/extensions/alert_apitest.cc
@@ -10,7 +10,6 @@
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/test/base/ui_test_utils.h"
-#include "components/app_modal/app_modal_dialog.h"
 #include "components/app_modal/app_modal_dialog_queue.h"
 #include "components/app_modal/javascript_app_modal_dialog.h"
 #include "components/app_modal/native_app_modal_dialog.h"
@@ -25,11 +24,9 @@
 void GetNextDialog(app_modal::NativeAppModalDialog** native_dialog) {
   DCHECK(native_dialog);
   *native_dialog = nullptr;
-  app_modal::AppModalDialog* dialog = ui_test_utils::WaitForAppModalDialog();
-  ASSERT_TRUE(dialog->IsJavaScriptModalDialog());
-  app_modal::JavaScriptAppModalDialog* js_dialog =
-      static_cast<app_modal::JavaScriptAppModalDialog*>(dialog);
-  *native_dialog = js_dialog->native_dialog();
+  app_modal::JavaScriptAppModalDialog* dialog =
+      ui_test_utils::WaitForAppModalDialog();
+  *native_dialog = dialog->native_dialog();
   ASSERT_TRUE(*native_dialog);
 }
 
diff --git a/chrome/browser/extensions/extension_install_prompt.cc b/chrome/browser/extensions/extension_install_prompt.cc
index d691a71..dcd7117 100644
--- a/chrome/browser/extensions/extension_install_prompt.cc
+++ b/chrome/browser/extensions/extension_install_prompt.cc
@@ -161,16 +161,12 @@
 ExtensionInstallPrompt::Prompt::~Prompt() {
 }
 
-void ExtensionInstallPrompt::Prompt::SetPermissions(
+void ExtensionInstallPrompt::Prompt::AddPermissions(
     const PermissionMessages& permissions,
     PermissionsType permissions_type) {
   InstallPromptPermissions& install_permissions =
       GetPermissionsForType(permissions_type);
 
-  install_permissions.permissions.clear();
-  install_permissions.details.clear();
-  install_permissions.is_showing_details.clear();
-
   for (const PermissionMessage& msg : permissions) {
     install_permissions.permissions.push_back(msg.message());
     // Add a dash to the front of each permission detail.
@@ -792,7 +788,7 @@
     const extensions::PermissionMessageProvider* message_provider =
         extensions::PermissionMessageProvider::Get();
 
-    prompt_->SetPermissions(message_provider->GetPermissionMessages(
+    prompt_->AddPermissions(message_provider->GetPermissionMessages(
                                 message_provider->GetAllPermissionIDs(
                                     *permissions_to_display, type)),
                             REGULAR_PERMISSIONS);
@@ -801,7 +797,7 @@
         extension_ ? &extension_->permissions_data()->withheld_permissions()
                    : nullptr;
     if (withheld && !withheld->IsEmpty()) {
-      prompt_->SetPermissions(
+      prompt_->AddPermissions(
           message_provider->GetPermissionMessages(
               message_provider->GetAllPermissionIDs(*withheld, type)),
           WITHHELD_PERMISSIONS);
diff --git a/chrome/browser/extensions/extension_install_prompt.h b/chrome/browser/extensions/extension_install_prompt.h
index 3079697..635975d 100644
--- a/chrome/browser/extensions/extension_install_prompt.h
+++ b/chrome/browser/extensions/extension_install_prompt.h
@@ -98,7 +98,7 @@
     explicit Prompt(PromptType type);
     ~Prompt();
 
-    void SetPermissions(const extensions::PermissionMessages& permissions,
+    void AddPermissions(const extensions::PermissionMessages& permissions,
                         PermissionsType permissions_type);
     void SetIsShowingDetails(DetailsType type,
                              size_t index,
diff --git a/chrome/browser/extensions/lazy_background_page_apitest.cc b/chrome/browser/extensions/lazy_background_page_apitest.cc
index f4774c5..f6599d61 100644
--- a/chrome/browser/extensions/lazy_background_page_apitest.cc
+++ b/chrome/browser/extensions/lazy_background_page_apitest.cc
@@ -26,7 +26,7 @@
 #include "chrome/common/chrome_switches.h"
 #include "chrome/common/url_constants.h"
 #include "chrome/test/base/ui_test_utils.h"
-#include "components/app_modal/app_modal_dialog.h"
+#include "components/app_modal/javascript_app_modal_dialog.h"
 #include "components/bookmarks/browser/bookmark_model.h"
 #include "components/bookmarks/browser/bookmark_utils.h"
 #include "components/bookmarks/test/bookmark_test_helpers.h"
@@ -239,7 +239,8 @@
   ASSERT_TRUE(extension);
 
   // The test extension opens a dialog on installation.
-  app_modal::AppModalDialog* dialog = ui_test_utils::WaitForAppModalDialog();
+  app_modal::JavaScriptAppModalDialog* dialog =
+      ui_test_utils::WaitForAppModalDialog();
   ASSERT_TRUE(dialog);
 
   // With the dialog open the background page is still alive.
diff --git a/chrome/browser/extensions/tab_helper.cc b/chrome/browser/extensions/tab_helper.cc
index 3ba0c93c..0f077ca 100644
--- a/chrome/browser/extensions/tab_helper.cc
+++ b/chrome/browser/extensions/tab_helper.cc
@@ -32,7 +32,6 @@
 #include "chrome/browser/ui/browser_finder.h"
 #include "chrome/browser/web_applications/web_app.h"
 #include "chrome/common/extensions/api/webstore/webstore_api_constants.h"
-#include "chrome/common/extensions/chrome_extension_messages.h"
 #include "chrome/common/extensions/extension_constants.h"
 #include "chrome/common/extensions/manifest_handlers/app_launch_info.h"
 #include "chrome/common/render_messages.h"
@@ -63,6 +62,7 @@
 #include "extensions/common/extension_urls.h"
 #include "extensions/common/feature_switch.h"
 #include "extensions/common/manifest_handlers/icons_handler.h"
+#include "services/service_manager/public/cpp/interface_provider.h"
 #include "url/url_constants.h"
 
 #if defined(OS_WIN)
@@ -83,12 +83,10 @@
  public:
   InlineInstallObserver(TabHelper* tab_helper,
                         content::BrowserContext* browser_context,
-                        int routing_id,
                         const ExtensionId& extension_id,
                         bool observe_download_progress,
                         bool observe_install_stage)
       : tab_helper_(tab_helper),
-        routing_id_(routing_id),
         extension_id_(extension_id),
         observe_download_progress_(observe_download_progress),
         observe_install_stage_(observe_install_stage),
@@ -111,8 +109,10 @@
   void OnDownloadProgress(const ExtensionId& extension_id,
                           int percent_downloaded) override {
     if (observe_download_progress_ && extension_id == extension_id_) {
-      tab_helper_->Send(new ExtensionMsg_InlineInstallDownloadProgress(
-          routing_id_, percent_downloaded));
+      auto iter =
+          tab_helper_->inline_install_progress_listeners_.find(extension_id);
+      DCHECK(iter != tab_helper_->inline_install_progress_listeners_.end());
+      iter->second->InlineInstallDownloadProgress(percent_downloaded);
     }
   }
   void OnBeginCrxInstall(const ExtensionId& extension_id) override {
@@ -124,17 +124,16 @@
   void SendInstallStageChangedMessage(const ExtensionId& extension_id,
                                       api::webstore::InstallStage stage) {
     if (observe_install_stage_ && extension_id == extension_id_) {
-      tab_helper_->Send(
-          new ExtensionMsg_InlineInstallStageChanged(routing_id_, stage));
+      auto iter =
+          tab_helper_->inline_install_progress_listeners_.find(extension_id);
+      DCHECK(iter != tab_helper_->inline_install_progress_listeners_.end());
+      iter->second->InlineInstallStageChanged(stage);
     }
   }
 
   // The owning TabHelper (guaranteed to be valid).
   TabHelper* const tab_helper_;
 
-  // The routing id to use in sending IPC updates.
-  int routing_id_;
-
   // The id of the extension to observe.
   ExtensionId extension_id_;
 
@@ -147,6 +146,10 @@
   DISALLOW_COPY_AND_ASSIGN(InlineInstallObserver);
 };
 
+TabHelper::~TabHelper() {
+  RemoveScriptExecutionObserver(ActivityLog::GetInstance(profile_));
+}
+
 TabHelper::TabHelper(content::WebContents* web_contents)
     : content::WebContentsObserver(web_contents),
       profile_(Profile::FromBrowserContext(web_contents->GetBrowserContext())),
@@ -159,6 +162,7 @@
       extension_action_runner_(new ExtensionActionRunner(web_contents)),
       webstore_inline_installer_factory_(new WebstoreInlineInstallerFactory()),
       registry_observer_(this),
+      bindings_(web_contents, this),
       image_loader_ptr_factory_(this),
       weak_ptr_factory_(this) {
   // The ActiveTabPermissionManager requires a session ID; ensure this
@@ -192,10 +196,6 @@
                      &web_contents->GetController()));
 }
 
-TabHelper::~TabHelper() {
-  RemoveScriptExecutionObserver(ActivityLog::GetInstance(profile_));
-}
-
 void TabHelper::CreateHostedAppFromWebContents() {
   DCHECK(CanCreateBookmarkApp());
   if (pending_web_app_action_ != NONE)
@@ -346,8 +346,6 @@
                                   content::RenderFrameHost* render_frame_host) {
   bool handled = true;
   IPC_BEGIN_MESSAGE_MAP_WITH_PARAM(TabHelper, message, render_frame_host)
-    IPC_MESSAGE_HANDLER(ExtensionHostMsg_InlineWebstoreInstall,
-                        OnInlineWebstoreInstall)
     IPC_MESSAGE_HANDLER(ExtensionHostMsg_GetAppInstallState,
                         OnGetAppInstallState)
     IPC_MESSAGE_HANDLER(ExtensionHostMsg_ContentScriptsExecuting,
@@ -408,11 +406,17 @@
     pending_web_app_action_ = NONE;
 }
 
-void TabHelper::OnInlineWebstoreInstall(content::RenderFrameHost* host,
-                                        int install_id,
-                                        int return_route_id,
-                                        const std::string& webstore_item_id,
-                                        int listeners_mask) {
+void TabHelper::DoInlineInstall(
+    const std::string& webstore_item_id,
+    int listeners_mask,
+    mojom::InlineInstallProgressListenerPtr install_progress_listener,
+    DoInlineInstallCallback callback) {
+  content::RenderFrameHost* host = web_contents()->GetMainFrame();
+  if (bindings_.GetCurrentTargetFrame() != host) {
+    NOTREACHED();
+    return;
+  }
+
   GURL requestor_url(host->GetLastCommittedURL());
   // Check that the listener is reasonable. We should never get anything other
   // than an install stage listener, a download listener, or both.
@@ -420,39 +424,37 @@
   // child frames from sending the IPC.
   if ((listeners_mask & ~(api::webstore::INSTALL_STAGE_LISTENER |
                           api::webstore::DOWNLOAD_PROGRESS_LISTENER)) != 0 ||
-      !requestor_url.is_valid() || requestor_url == url::kAboutBlankURL ||
-      host->GetParent()) {
+      !requestor_url.is_valid() || requestor_url == url::kAboutBlankURL) {
     NOTREACHED();
     return;
   }
 
-  if (pending_inline_installations_.count(webstore_item_id) != 0) {
-    Send(new ExtensionMsg_InlineWebstoreInstallResponse(
-        return_route_id, install_id, false,
-        webstore_install::kInstallInProgressError,
-        webstore_install::INSTALL_IN_PROGRESS));
+  if (base::ContainsKey(install_callbacks_, webstore_item_id)) {
+    std::move(callback).Run(false, webstore_install::kInstallInProgressError,
+                            webstore_install::INSTALL_IN_PROGRESS);
     return;
   }
 
-  pending_inline_installations_.insert(webstore_item_id);
+  install_callbacks_[webstore_item_id] = std::move(callback);
+  inline_install_progress_listeners_[webstore_item_id] =
+      std::move(install_progress_listener);
   // Inform the Webstore API that an inline install is happening, in case the
   // page requested status updates.
   ExtensionRegistry* registry = ExtensionRegistry::Get(profile_);
   if (registry->disabled_extensions().Contains(webstore_item_id) &&
       (ExtensionPrefs::Get(profile_)->GetDisableReasons(webstore_item_id) &
-           Extension::DISABLE_PERMISSIONS_INCREASE) != 0) {
-      // The extension was disabled due to permissions increase. Prompt for
-      // re-enable.
-      // TODO(devlin): We should also prompt for re-enable for other reasons,
-      // like user-disabled.
-      // For clarity, explicitly end any prior reenable process.
-      extension_reenabler_.reset();
-      extension_reenabler_ = ExtensionReenabler::PromptForReenable(
-          registry->disabled_extensions().GetByID(webstore_item_id), profile_,
-          web_contents(), requestor_url,
-          base::Bind(&TabHelper::OnReenableComplete,
-                     weak_ptr_factory_.GetWeakPtr(), install_id,
-                     return_route_id, webstore_item_id));
+       Extension::DISABLE_PERMISSIONS_INCREASE) != 0) {
+    // The extension was disabled due to permissions increase. Prompt for
+    // re-enable.
+    // TODO(devlin): We should also prompt for re-enable for other reasons,
+    // like user-disabled.
+    // For clarity, explicitly end any prior reenable process.
+    extension_reenabler_.reset();
+    extension_reenabler_ = ExtensionReenabler::PromptForReenable(
+        registry->disabled_extensions().GetByID(webstore_item_id), profile_,
+        web_contents(), requestor_url,
+        base::Bind(&TabHelper::OnReenableComplete,
+                   weak_ptr_factory_.GetWeakPtr(), webstore_item_id));
   } else {
     // TODO(devlin): We should adddress the case of the extension already
     // being installed and enabled.
@@ -464,14 +466,13 @@
       DCHECK_EQ(0u, install_observers_.count(webstore_item_id));
       install_observers_[webstore_item_id] =
           base::MakeUnique<InlineInstallObserver>(
-              this, web_contents()->GetBrowserContext(), return_route_id,
-              webstore_item_id, observe_download_progress,
-              observe_install_stage);
+              this, web_contents()->GetBrowserContext(), webstore_item_id,
+              observe_download_progress, observe_install_stage);
     }
 
-    WebstoreStandaloneInstaller::Callback callback = base::Bind(
-        &TabHelper::OnInlineInstallComplete, weak_ptr_factory_.GetWeakPtr(),
-        install_id, return_route_id, webstore_item_id);
+    WebstoreStandaloneInstaller::Callback callback =
+        base::Bind(&TabHelper::OnInlineInstallComplete,
+                   weak_ptr_factory_.GetWeakPtr(), webstore_item_id);
     scoped_refptr<WebstoreInlineInstaller> installer(
         webstore_inline_installer_factory_->CreateInstaller(
             web_contents(), host, webstore_item_id, requestor_url, callback));
@@ -561,9 +562,7 @@
   return ExtensionTabUtil::GetWindowControllerOfTab(web_contents());
 }
 
-void TabHelper::OnReenableComplete(int install_id,
-                                   int return_route_id,
-                                   const ExtensionId& extension_id,
+void TabHelper::OnReenableComplete(const ExtensionId& extension_id,
                                    ExtensionReenabler::ReenableResult result) {
   // Map the re-enable results to webstore-install results.
   webstore_install::Result webstore_result = webstore_install::SUCCESS;
@@ -585,7 +584,7 @@
       break;
   }
 
-  OnInlineInstallComplete(install_id, return_route_id, extension_id,
+  OnInlineInstallComplete(extension_id,
                           result == ExtensionReenabler::REENABLE_SUCCESS, error,
                           webstore_result);
   // Note: ExtensionReenabler contained the callback with the curried-in
@@ -593,21 +592,15 @@
   extension_reenabler_.reset();
 }
 
-void TabHelper::OnInlineInstallComplete(int install_id,
-                                        int return_route_id,
-                                        const ExtensionId& extension_id,
+void TabHelper::OnInlineInstallComplete(const ExtensionId& extension_id,
                                         bool success,
                                         const std::string& error,
                                         webstore_install::Result result) {
-  DCHECK_EQ(1u, pending_inline_installations_.count(extension_id));
-  pending_inline_installations_.erase(extension_id);
   install_observers_.erase(extension_id);
-  Send(new ExtensionMsg_InlineWebstoreInstallResponse(
-      return_route_id,
-      install_id,
-      success,
-      success ? std::string() : error,
-      result));
+  auto iter = install_callbacks_.find(extension_id);
+  DCHECK(iter != install_callbacks_.end());
+  std::move(iter->second).Run(success, success ? std::string() : error, result);
+  install_callbacks_.erase(iter);
 }
 
 WebContents* TabHelper::GetAssociatedWebContents() const {
diff --git a/chrome/browser/extensions/tab_helper.h b/chrome/browser/extensions/tab_helper.h
index 09b09f6..c0df70eb 100644
--- a/chrome/browser/extensions/tab_helper.h
+++ b/chrome/browser/extensions/tab_helper.h
@@ -16,10 +16,12 @@
 #include "base/scoped_observer.h"
 #include "chrome/browser/extensions/active_tab_permission_granter.h"
 #include "chrome/browser/extensions/extension_reenabler.h"
+#include "chrome/common/extensions/mojom/inline_install.mojom.h"
 #include "chrome/common/extensions/webstore_install_result.h"
 #include "chrome/common/web_application_info.h"
 #include "content/public/browser/notification_observer.h"
 #include "content/public/browser/notification_registrar.h"
+#include "content/public/browser/web_contents_binding_set.h"
 #include "content/public/browser/web_contents_observer.h"
 #include "content/public/browser/web_contents_user_data.h"
 #include "extensions/browser/extension_function_dispatcher.h"
@@ -45,7 +47,8 @@
                   public ExtensionFunctionDispatcher::Delegate,
                   public ExtensionRegistryObserver,
                   public content::NotificationObserver,
-                  public content::WebContentsUserData<TabHelper> {
+                  public content::WebContentsUserData<TabHelper>,
+                  public mojom::InlineInstaller {
  public:
   ~TabHelper() override;
 
@@ -128,10 +131,11 @@
   };
 
   explicit TabHelper(content::WebContents* web_contents);
+
   friend class content::WebContentsUserData<TabHelper>;
 
   // Displays UI for completion of creating a bookmark hosted app.
-  void FinishCreateBookmarkApp(const extensions::Extension* extension,
+  void FinishCreateBookmarkApp(const Extension* extension,
                                const WebApplicationInfo& web_app_info);
 
   // content::WebContentsObserver overrides.
@@ -145,8 +149,8 @@
       content::WebContents* old_web_contents,
       content::WebContents* new_web_contents) override;
 
-  // extensions::ExtensionFunctionDispatcher::Delegate overrides.
-  extensions::WindowController* GetExtensionWindowController() const override;
+  // ExtensionFunctionDispatcher::Delegate overrides.
+  WindowController* GetExtensionWindowController() const override;
   content::WebContents* GetAssociatedWebContents() const override;
 
   // ExtensionRegistryObserver:
@@ -154,13 +158,15 @@
                            const Extension* extension,
                            UnloadedExtensionReason reason) override;
 
+  // mojom::InlineInstall:
+  void DoInlineInstall(
+      const std::string& webstore_item_id,
+      int listeners_mask,
+      mojom::InlineInstallProgressListenerPtr install_progress_listener,
+      DoInlineInstallCallback callback) override;
+
   // Message handlers.
   void OnDidGetWebApplicationInfo(const WebApplicationInfo& info);
-  void OnInlineWebstoreInstall(content::RenderFrameHost* host,
-                               int install_id,
-                               int return_route_id,
-                               const std::string& webstore_item_id,
-                               int listeners_mask);
   void OnGetAppInstallState(content::RenderFrameHost* host,
                             const GURL& requestor_url,
                             int return_route_id,
@@ -181,17 +187,13 @@
   void OnImageLoaded(const gfx::Image& image);
 
   // WebstoreStandaloneInstaller::Callback.
-  void OnInlineInstallComplete(int install_id,
-                               int return_route_id,
-                               const ExtensionId& extension_id,
+  void OnInlineInstallComplete(const ExtensionId& extension_id,
                                bool success,
                                const std::string& error,
                                webstore_install::Result result);
 
   // ExtensionReenabler::Callback.
-  void OnReenableComplete(int install_id,
-                          int return_route_id,
-                          const ExtensionId& extension_id,
+  void OnReenableComplete(const ExtensionId& extension_id,
                           ExtensionReenabler::ReenableResult result);
 
   // content::NotificationObserver.
@@ -260,8 +262,14 @@
   std::map<ExtensionId, std::unique_ptr<InlineInstallObserver>>
       install_observers_;
 
-  // The set of extension ids that are currently being installed.
-  std::set<ExtensionId> pending_inline_installations_;
+  // Map of function callbacks that are invoked when the inline installation for
+  // a particular extension (hence ExtensionId) completes.
+  std::map<ExtensionId, DoInlineInstallCallback> install_callbacks_;
+
+  content::WebContentsFrameBindingSet<mojom::InlineInstaller> bindings_;
+
+  std::map<ExtensionId, mojom::InlineInstallProgressListenerPtr>
+      inline_install_progress_listeners_;
 
   // Vend weak pointers that can be invalidated to stop in-progress loads.
   base::WeakPtrFactory<TabHelper> image_loader_ptr_factory_;
diff --git a/chrome/browser/favicon/chrome_fallback_icon_client.cc b/chrome/browser/favicon/chrome_fallback_icon_client.cc
deleted file mode 100644
index ee983aa..0000000
--- a/chrome/browser/favicon/chrome_fallback_icon_client.cc
+++ /dev/null
@@ -1,25 +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/favicon/chrome_fallback_icon_client.h"
-
-#include "build/build_config.h"
-#include "chrome/grit/platform_locale_settings.h"
-#include "ui/base/l10n/l10n_util.h"
-
-ChromeFallbackIconClient::ChromeFallbackIconClient() {
-#if defined(OS_CHROMEOS)
-  font_list_.push_back("Noto Sans");
-#else
-  font_list_.push_back(l10n_util::GetStringUTF8(IDS_SANS_SERIF_FONT_FAMILY));
-#endif
-}
-
-ChromeFallbackIconClient::~ChromeFallbackIconClient() {
-}
-
-const std::vector<std::string>& ChromeFallbackIconClient::GetFontNameList()
-    const {
-  return font_list_;
-}
diff --git a/chrome/browser/favicon/chrome_fallback_icon_client.h b/chrome/browser/favicon/chrome_fallback_icon_client.h
deleted file mode 100644
index 85013a2..0000000
--- a/chrome/browser/favicon/chrome_fallback_icon_client.h
+++ /dev/null
@@ -1,29 +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_FAVICON_CHROME_FALLBACK_ICON_CLIENT_H_
-#define CHROME_BROWSER_FAVICON_CHROME_FALLBACK_ICON_CLIENT_H_
-
-#include <string>
-#include <vector>
-
-#include "base/macros.h"
-#include "components/favicon/core/fallback_icon_client.h"
-
-// ChromeFallbackIconClient implements the FallbackIconClient interface.
-class ChromeFallbackIconClient : public favicon::FallbackIconClient {
- public:
-  ChromeFallbackIconClient();
-  ~ChromeFallbackIconClient() override;
-
-  // FallbackIconClient implementation:
-  const std::vector<std::string>& GetFontNameList() const override;
-
- private:
-  std::vector<std::string> font_list_;
-
-  DISALLOW_COPY_AND_ASSIGN(ChromeFallbackIconClient);
-};
-
-#endif  // CHROME_BROWSER_FAVICON_CHROME_FALLBACK_ICON_CLIENT_H_
diff --git a/chrome/browser/favicon/chrome_fallback_icon_client_factory.cc b/chrome/browser/favicon/chrome_fallback_icon_client_factory.cc
deleted file mode 100644
index e2ace70..0000000
--- a/chrome/browser/favicon/chrome_fallback_icon_client_factory.cc
+++ /dev/null
@@ -1,39 +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/favicon/chrome_fallback_icon_client_factory.h"
-
-#include "base/memory/singleton.h"
-#include "chrome/browser/favicon/chrome_fallback_icon_client.h"
-#include "chrome/browser/profiles/incognito_helpers.h"
-#include "components/keyed_service/content/browser_context_dependency_manager.h"
-#include "content/public/browser/browser_context.h"
-
-ChromeFallbackIconClientFactory::ChromeFallbackIconClientFactory()
-    : BrowserContextKeyedServiceFactory(
-        "ChromeFallbackIconClient",
-        BrowserContextDependencyManager::GetInstance()) {
-}
-
-ChromeFallbackIconClientFactory::~ChromeFallbackIconClientFactory() {
-}
-
-// static
-favicon::FallbackIconClient*
-ChromeFallbackIconClientFactory::GetForBrowserContext(
-    content::BrowserContext* context) {
-  return static_cast<favicon::FallbackIconClient*>(
-      GetInstance()->GetServiceForBrowserContext(context, true));
-}
-
-// static
-ChromeFallbackIconClientFactory*
-ChromeFallbackIconClientFactory::GetInstance() {
-  return base::Singleton<ChromeFallbackIconClientFactory>::get();
-}
-
-KeyedService* ChromeFallbackIconClientFactory::BuildServiceInstanceFor(
-    content::BrowserContext* context) const {
-  return new ChromeFallbackIconClient();
-}
diff --git a/chrome/browser/favicon/chrome_fallback_icon_client_factory.h b/chrome/browser/favicon/chrome_fallback_icon_client_factory.h
deleted file mode 100644
index 0f349a6..0000000
--- a/chrome/browser/favicon/chrome_fallback_icon_client_factory.h
+++ /dev/null
@@ -1,46 +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_FAVICON_CHROME_FALLBACK_ICON_CLIENT_FACTORY_H_
-#define CHROME_BROWSER_FAVICON_CHROME_FALLBACK_ICON_CLIENT_FACTORY_H_
-
-#include "components/keyed_service/content/browser_context_keyed_service_factory.h"
-
-namespace base {
-template <typename T> struct DefaultSingletonTraits;
-}
-
-namespace content {
-class BrowserContext;
-}
-
-namespace favicon {
-class FallbackIconClient;
-}
-
-// Singleton that owns all ChromeFallbackIconClients and associates them with
-// Profiles.
-class ChromeFallbackIconClientFactory
-    : public BrowserContextKeyedServiceFactory {
- public:
-  // Returns the instance of FallbackIconClient associated with this profile
-  // (creating one if none exists).
-  static favicon::FallbackIconClient* GetForBrowserContext(
-      content::BrowserContext* context);
-
-  // Returns an instance of the factory singleton.
-  static ChromeFallbackIconClientFactory* GetInstance();
-
- private:
-  friend struct base::DefaultSingletonTraits<ChromeFallbackIconClientFactory>;
-
-  ChromeFallbackIconClientFactory();
-  ~ChromeFallbackIconClientFactory() override;
-
-  // BrowserContextKeyedServiceFactory:
-  KeyedService* BuildServiceInstanceFor(
-      content::BrowserContext* context) const override;
-};
-
-#endif  // CHROME_BROWSER_FAVICON_CHROME_FALLBACK_ICON_CLIENT_FACTORY_H_
diff --git a/chrome/browser/favicon/chrome_fallback_icon_client_unittest.cc b/chrome/browser/favicon/chrome_fallback_icon_client_unittest.cc
deleted file mode 100644
index 41c1a68..0000000
--- a/chrome/browser/favicon/chrome_fallback_icon_client_unittest.cc
+++ /dev/null
@@ -1,14 +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/favicon/chrome_fallback_icon_client.h"
-
-#include "base/macros.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-TEST(ChromeFallbackIconClientTest, GetFontNameList) {
-  ChromeFallbackIconClient client;
-  // Just ensure non-empty, otherwise not checking the actual font.
-  EXPECT_FALSE(client.GetFontNameList().empty());
-}
diff --git a/chrome/browser/favicon/fallback_icon_service_factory.cc b/chrome/browser/favicon/fallback_icon_service_factory.cc
deleted file mode 100644
index a1871dd..0000000
--- a/chrome/browser/favicon/fallback_icon_service_factory.cc
+++ /dev/null
@@ -1,49 +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/favicon/fallback_icon_service_factory.h"
-
-#include "base/memory/singleton.h"
-#include "chrome/browser/favicon/chrome_fallback_icon_client_factory.h"
-#include "chrome/browser/profiles/incognito_helpers.h"
-#include "components/favicon/core/fallback_icon_service.h"
-#include "components/keyed_service/content/browser_context_dependency_manager.h"
-#include "content/public/browser/browser_context.h"
-
-// static
-favicon::FallbackIconService* FallbackIconServiceFactory::GetForBrowserContext(
-    content::BrowserContext* context) {
-  return static_cast<favicon::FallbackIconService*>(
-      GetInstance()->GetServiceForBrowserContext(context, true));
-}
-
-// static
-FallbackIconServiceFactory* FallbackIconServiceFactory::GetInstance() {
-  return base::Singleton<FallbackIconServiceFactory>::get();
-}
-
-FallbackIconServiceFactory::FallbackIconServiceFactory()
-    : BrowserContextKeyedServiceFactory(
-        "FallbackIconService",
-        BrowserContextDependencyManager::GetInstance()) {
-  DependsOn(ChromeFallbackIconClientFactory::GetInstance());
-}
-
-FallbackIconServiceFactory::~FallbackIconServiceFactory() {}
-
-content::BrowserContext* FallbackIconServiceFactory::GetBrowserContextToUse(
-      content::BrowserContext* context) const {
-  return chrome::GetBrowserContextRedirectedInIncognito(context);
-}
-
-KeyedService* FallbackIconServiceFactory::BuildServiceInstanceFor(
-    content::BrowserContext* context) const {
-  favicon::FallbackIconClient* fallback_icon_client =
-      ChromeFallbackIconClientFactory::GetForBrowserContext(context);
-  return new favicon::FallbackIconService(fallback_icon_client);
-}
-
-bool FallbackIconServiceFactory::ServiceIsNULLWhileTesting() const {
-  return true;
-}
diff --git a/chrome/browser/favicon/fallback_icon_service_factory.h b/chrome/browser/favicon/fallback_icon_service_factory.h
deleted file mode 100644
index 82b30bc9..0000000
--- a/chrome/browser/favicon/fallback_icon_service_factory.h
+++ /dev/null
@@ -1,48 +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_FAVICON_FALLBACK_ICON_SERVICE_FACTORY_H_
-#define CHROME_BROWSER_FAVICON_FALLBACK_ICON_SERVICE_FACTORY_H_
-
-#include "base/macros.h"
-#include "components/keyed_service/content/browser_context_keyed_service_factory.h"
-
-namespace base {
-template <typename T> struct DefaultSingletonTraits;
-}
-
-namespace content {
-class BrowserContext;
-}
-
-namespace favicon {
-class FallbackIconService;
-}
-
-// Singleton that owns all FallbackIconService and associates them with
-// BrowserContext instances.
-class FallbackIconServiceFactory : public BrowserContextKeyedServiceFactory {
- public:
-  static favicon::FallbackIconService* GetForBrowserContext(
-      content::BrowserContext* context);
-
-  static FallbackIconServiceFactory* GetInstance();
-
- private:
-  friend struct base::DefaultSingletonTraits<FallbackIconServiceFactory>;
-
-  FallbackIconServiceFactory();
-  ~FallbackIconServiceFactory() override;
-
-  // BrowserContextKeyedServiceFactory:
-  content::BrowserContext* GetBrowserContextToUse(
-      content::BrowserContext* context) const override;
-  KeyedService* BuildServiceInstanceFor(
-      content::BrowserContext* context) const override;
-  bool ServiceIsNULLWhileTesting() const override;
-
-  DISALLOW_COPY_AND_ASSIGN(FallbackIconServiceFactory);
-};
-
-#endif  // CHROME_BROWSER_FAVICON_FALLBACK_ICON_SERVICE_FACTORY_H_
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc
index 3fe7dc57..b3fd4593 100644
--- a/chrome/browser/flag_descriptions.cc
+++ b/chrome/browser/flag_descriptions.cc
@@ -3115,6 +3115,11 @@
     "Changes the maximum number af autocomplete matches displayed in the "
     "Omnibox UI.";
 
+const char kOmniboxUIVerticalLayoutName[] = "Omnibox UI Vertical Layout";
+
+const char kOmniboxUIVerticalLayoutDescription[] =
+    "Displays Omnibox sugestions in 2 lines - title over origin.";
+
 const char kOmniboxUIVerticalMarginName[] = "Omnibox UI Vertical Margin";
 
 const char kOmniboxUIVerticalMarginDescription[] =
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h
index a7b0869f7..9b73cfc 100644
--- a/chrome/browser/flag_descriptions.h
+++ b/chrome/browser/flag_descriptions.h
@@ -494,6 +494,9 @@
 extern const char kOmniboxUIMaxAutocompleteMatchesName[];
 extern const char kOmniboxUIMaxAutocompleteMatchesDescription[];
 
+extern const char kOmniboxUIVerticalLayoutName[];
+extern const char kOmniboxUIVerticalLayoutDescription[];
+
 extern const char kOmniboxUIVerticalMarginName[];
 extern const char kOmniboxUIVerticalMarginDescription[];
 
diff --git a/chrome/browser/io_thread.cc b/chrome/browser/io_thread.cc
index 935ebc38..12463d42 100644
--- a/chrome/browser/io_thread.cc
+++ b/chrome/browser/io_thread.cc
@@ -649,15 +649,15 @@
       net::ProxyService::CreateDirectWithNetLog(net_log_);
   globals_->dns_probe_service.reset(new chrome_browser_net::DnsProbeService());
   globals_->host_mapping_rules.reset(new net::HostMappingRules());
-  params_.host_mapping_rules = globals_->host_mapping_rules.get();
-  globals_->http_user_agent_settings.reset(
-      new net::StaticHttpUserAgentSettings(std::string(), GetUserAgent()));
   if (command_line.HasSwitch(switches::kHostRules)) {
     TRACE_EVENT_BEGIN0("startup", "IOThread::InitAsync:SetRulesFromString");
     globals_->host_mapping_rules->SetRulesFromString(
         command_line.GetSwitchValueASCII(switches::kHostRules));
     TRACE_EVENT_END0("startup", "IOThread::InitAsync:SetRulesFromString");
   }
+  params_.host_mapping_rules = *globals_->host_mapping_rules.get();
+  globals_->http_user_agent_settings.reset(
+      new net::StaticHttpUserAgentSettings(std::string(), GetUserAgent()));
   globals_->enable_brotli =
       base::FeatureList::IsEnabled(features::kBrotliEncoding);
   params_.enable_token_binding =
diff --git a/chrome/browser/lifetime/browser_close_manager_browsertest.cc b/chrome/browser/lifetime/browser_close_manager_browsertest.cc
index 67f6bb69..d4e0baaa 100644
--- a/chrome/browser/lifetime/browser_close_manager_browsertest.cc
+++ b/chrome/browser/lifetime/browser_close_manager_browsertest.cc
@@ -64,12 +64,10 @@
 namespace {
 
 app_modal::NativeAppModalDialog* GetNextDialog() {
-  app_modal::AppModalDialog* dialog = ui_test_utils::WaitForAppModalDialog();
-  EXPECT_TRUE(dialog->IsJavaScriptModalDialog());
-  app_modal::JavaScriptAppModalDialog* js_dialog =
-      static_cast<app_modal::JavaScriptAppModalDialog*>(dialog);
-  CHECK(js_dialog->native_dialog());
-  return js_dialog->native_dialog();
+  app_modal::JavaScriptAppModalDialog* dialog =
+      ui_test_utils::WaitForAppModalDialog();
+  CHECK(dialog->native_dialog());
+  return dialog->native_dialog();
 }
 
 // Note: call |PrepareForDialog| on the relevant WebContents or Browser before
diff --git a/chrome/browser/resources/device_log_ui/device_log_ui.html b/chrome/browser/resources/device_log_ui/device_log_ui.html
index 17635af..30a1073 100644
--- a/chrome/browser/resources/device_log_ui/device_log_ui.html
+++ b/chrome/browser/resources/device_log_ui/device_log_ui.html
@@ -1,69 +1,68 @@
 <!DOCTYPE html>
-<html i18n-values="dir:textdirection;lang:language">
+<html dir="$i18n{textdirection}" lang="$i18n{language}">
 <head>
   <meta charset="utf-8">
-  <title id="device-log-title" i18n-content="titleText"></title>
+  <title id="device-log-title">$i18n{titleText}</title>
   <link rel="stylesheet" href="chrome://device-log/device_log_ui.css">
   <script src="chrome://resources/js/load_time_data.js"></script>
   <script src="chrome://resources/js/util.js"></script>
   <script src="chrome://device-log/strings.js"></script>
   <script src="chrome://device-log/device_log_ui.js"></script>
 </head>
-<body i18n-values=".style.fontFamily:fontfamily;.style.fontSize:fontsize">
+<body style="fontFamily:$i18n{fontfamily};fontSize:$i18n{fontsize};">
   <div id="header">
-    <p i18n-content="autoRefreshText"></p>
+    <p>$i18n{autoRefreshText}</p>
   </div>
   <div id="log-checkbox-container">
-    <button id="log-refresh" i18n-content="logRefreshText"></button>
-    <label id="log-checkbox-show" i18n-content="logLevelShowText"></label>
-
+    <button id="log-refresh">$i18n{logRefreshText}</button>
+    <label id="log-checkbox-show">$i18n{logLevelShowText}</label>
     <label>
       <input id="log-level-error" type="checkbox">
-      <span i18n-content="logLevelErrorText"></span>
+      <span>$i18n{logLevelErrorText}</span>
     </label>
     <label>
       <input id="log-level-user" type="checkbox">
-      <span i18n-content="logLevelUserText"></span>
+      <span>$i18n{logLevelUserText}</span>
     </label>
     <label>
       <input id="log-level-event" type="checkbox">
-      <span i18n-content="logLevelEventText"></span>
+      <span>$i18n{logLevelEventText}</span>
     </label>
     <label>
       <input id="log-level-debug" type="checkbox">
-      <span i18n-content="logLevelDebugText"></span>
+      <span>$i18n{logLevelDebugText}</span>
     </label>
     <label>
       <input id="log-type-login" type="checkbox">
-      <span i18n-content="logTypeLoginText"></span>
+      <span>$i18n{logTypeLoginText}</span>
     </label>
     <label>
       <input id="log-type-network" type="checkbox">
-      <span i18n-content="logTypeNetworkText"></span>
+      <span>$i18n{logTypeNetworkText}</span>
     </label>
     <label>
       <input id="log-type-power" type="checkbox">
-      <span i18n-content="logTypePowerText"></span>
+      <span>$i18n{logTypePowerText}</span>
     </label>
     <label>
       <input id="log-type-bluetooth" type="checkbox">
-      <span i18n-content="logTypeBluetoothText"></span>
+      <span>$i18n{logTypeBluetoothText}</span>
     </label>
     <label>
       <input id="log-type-usb" type="checkbox">
-      <span i18n-content="logTypeUsbText"></span>
+      <span>$i18n{logTypeUsbText}</span>
     </label>
     <label>
       <input id="log-type-hid" type="checkbox">
-      <span i18n-content="logTypeHidText"></span>
+      <span>$i18n{logTypeHidText}</span>
     </label>
     <label>
       <input id="log-fileinfo" type="checkbox">
-      <span i18n-content="logLevelFileinfoText"></span>
+      <span>$i18n{logLevelFileinfoText}</span>
     </label>
     <label>
       <input id="log-timedetail" type="checkbox">
-      <span i18n-content="logLevelTimeDetailText"></span>
+      <span>$i18n{logLevelTimeDetailText}</span>
     </label>
   </div>
   <div id="log-container"></div>
diff --git a/chrome/browser/safe_browsing/certificate_reporting_service_browsertest.cc b/chrome/browser/safe_browsing/certificate_reporting_service_browsertest.cc
index cfdd6ff..c265435 100644
--- a/chrome/browser/safe_browsing/certificate_reporting_service_browsertest.cc
+++ b/chrome/browser/safe_browsing/certificate_reporting_service_browsertest.cc
@@ -16,6 +16,7 @@
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/safe_browsing/certificate_reporting_service_factory.h"
 #include "chrome/browser/safe_browsing/certificate_reporting_service_test_utils.h"
+#include "chrome/browser/ssl/cert_report_helper.h"
 #include "chrome/browser/ssl/certificate_reporting_test_utils.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
@@ -65,7 +66,9 @@
 class CertificateReportingServiceBrowserTest : public InProcessBrowserTest {
  public:
   CertificateReportingServiceBrowserTest()
-      : https_server_(net::EmbeddedTestServer::TYPE_HTTPS) {}
+      : https_server_(net::EmbeddedTestServer::TYPE_HTTPS) {
+    CertReportHelper::SetFakeOfficialBuildForTesting();
+  }
 
   void SetUpOnMainThread() override {
     DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
diff --git a/chrome/browser/search/instant_service.cc b/chrome/browser/search/instant_service.cc
index 42d838d1..2218ddb2 100644
--- a/chrome/browser/search/instant_service.cc
+++ b/chrome/browser/search/instant_service.cc
@@ -29,7 +29,6 @@
 #include "chrome/browser/ui/webui/theme_source.h"
 #include "chrome/common/render_messages.h"
 #include "chrome/grit/theme_resources.h"
-#include "components/favicon/core/fallback_icon_service.h"
 #include "components/history/core/browser/top_sites.h"
 #include "components/image_fetcher/core/image_fetcher_impl.h"
 #include "components/keyed_service/core/service_access_type.h"
diff --git a/chrome/browser/ssl/captive_portal_blocking_page_browsertest.cc b/chrome/browser/ssl/captive_portal_blocking_page_browsertest.cc
index 4b93c43..e7f3115 100644
--- a/chrome/browser/ssl/captive_portal_blocking_page_browsertest.cc
+++ b/chrome/browser/ssl/captive_portal_blocking_page_browsertest.cc
@@ -95,7 +95,9 @@
 
 class CaptivePortalBlockingPageTest : public InProcessBrowserTest {
  public:
-  CaptivePortalBlockingPageTest() {}
+  CaptivePortalBlockingPageTest() {
+    CertReportHelper::SetFakeOfficialBuildForTesting();
+  }
 
   void SetUpCommandLine(base::CommandLine* command_line) override {
     command_line->AppendSwitchASCII(
diff --git a/chrome/browser/ssl/cert_report_helper.cc b/chrome/browser/ssl/cert_report_helper.cc
index 053ce92..418750ad 100644
--- a/chrome/browser/ssl/cert_report_helper.cc
+++ b/chrome/browser/ssl/cert_report_helper.cc
@@ -29,6 +29,10 @@
 
 namespace {
 
+// Certificate reports are only sent from official builds, but this flag can be
+// set by tests.
+static bool g_is_fake_official_build_for_testing = false;
+
 // Returns a pointer to the Profile associated with |web_contents|.
 Profile* GetProfile(content::WebContents* web_contents) {
   return Profile::FromBrowserContext(web_contents->GetBrowserContext());
@@ -65,6 +69,11 @@
 CertReportHelper::~CertReportHelper() {
 }
 
+// static
+void CertReportHelper::SetFakeOfficialBuildForTesting() {
+  g_is_fake_official_build_for_testing = true;
+}
+
 void CertReportHelper::PopulateExtendedReportingOption(
     base::DictionaryValue* load_time_data) {
   // Only show the checkbox if not off-the-record and if this client is
@@ -149,6 +158,15 @@
 
 bool CertReportHelper::ShouldReportCertificateError() {
   DCHECK(ShouldShowCertificateReporterCheckbox());
+
+  bool is_official_build = g_is_fake_official_build_for_testing;
+#if defined(OFFICIAL_BUILD) && defined(GOOGLE_CHROME_BUILD)
+  is_official_build = true;
+#endif
+
+  if (!is_official_build)
+    return false;
+
   // Even in case the checkbox was shown, we don't send error reports
   // for all of these users. Check the Finch configuration for a sending
   // threshold and only send reports in case the threshold isn't exceeded.
diff --git a/chrome/browser/ssl/cert_report_helper.h b/chrome/browser/ssl/cert_report_helper.h
index 8982bcea..0222524 100644
--- a/chrome/browser/ssl/cert_report_helper.h
+++ b/chrome/browser/ssl/cert_report_helper.h
@@ -48,6 +48,10 @@
 
   virtual ~CertReportHelper();
 
+  // This method can be called by tests to fake an official build (reports are
+  // only sent from official builds).
+  static void SetFakeOfficialBuildForTesting();
+
   // Populates data that JavaScript code on the interstitial uses to show
   // the checkbox.
   void PopulateExtendedReportingOption(base::DictionaryValue* load_time_data);
diff --git a/chrome/browser/ssl/ssl_browser_tests.cc b/chrome/browser/ssl/ssl_browser_tests.cc
index f943989..5992e360 100644
--- a/chrome/browser/ssl/ssl_browser_tests.cc
+++ b/chrome/browser/ssl/ssl_browser_tests.cc
@@ -768,7 +768,11 @@
 
 class SSLUITestWithExtendedReporting : public SSLUITest {
  public:
-  SSLUITestWithExtendedReporting() : SSLUITest() {}
+  SSLUITestWithExtendedReporting() : SSLUITest() {
+    // Certificate reports are only sent from official builds, unless this has
+    // been called.
+    CertReportHelper::SetFakeOfficialBuildForTesting();
+  }
 };
 
 // Visits a regular page over http.
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn
index a8db9d9b..9162e927 100644
--- a/chrome/browser/ui/BUILD.gn
+++ b/chrome/browser/ui/BUILD.gn
@@ -346,8 +346,6 @@
     "webui/domain_reliability_internals_ui.h",
     "webui/engagement/site_engagement_ui.cc",
     "webui/engagement/site_engagement_ui.h",
-    "webui/fallback_icon_source.cc",
-    "webui/fallback_icon_source.h",
     "webui/favicon_source.cc",
     "webui/favicon_source.h",
     "webui/fileicon_source.cc",
@@ -1574,8 +1572,6 @@
       "views/payments/payment_request_views_util.h",
       "views/payments/payment_sheet_view_controller.cc",
       "views/payments/payment_sheet_view_controller.h",
-      "views/payments/preselected_combobox_model.cc",
-      "views/payments/preselected_combobox_model.h",
       "views/payments/profile_list_view_controller.cc",
       "views/payments/profile_list_view_controller.h",
       "views/payments/shipping_address_editor_view_controller.cc",
diff --git a/chrome/browser/ui/android/javascript_app_modal_dialog_android.cc b/chrome/browser/ui/android/javascript_app_modal_dialog_android.cc
index fe7062ce..f3aeb72 100644
--- a/chrome/browser/ui/android/javascript_app_modal_dialog_android.cc
+++ b/chrome/browser/ui/android/javascript_app_modal_dialog_android.cc
@@ -152,7 +152,7 @@
 ScopedJavaLocalRef<jobject> GetCurrentModalDialog(
     JNIEnv* env,
     const JavaParamRef<jclass>& clazz) {
-  app_modal::AppModalDialog* dialog =
+  app_modal::JavaScriptAppModalDialog* dialog =
       app_modal::AppModalDialogQueue::GetInstance()->active_dialog();
   if (!dialog || !dialog->native_dialog())
     return ScopedJavaLocalRef<jobject>();
diff --git a/chrome/browser/ui/ash/chrome_shell_delegate.cc b/chrome/browser/ui/ash/chrome_shell_delegate.cc
index b418cee..dd8d124 100644
--- a/chrome/browser/ui/ash/chrome_shell_delegate.cc
+++ b/chrome/browser/ui/ash/chrome_shell_delegate.cc
@@ -432,9 +432,8 @@
   return chrome::IsRunningInForcedAppMode();
 }
 
-bool ChromeShellDelegate::CanShowWindowForUser(ash::WmWindow* window) const {
-  return ::CanShowWindowForUser(ash::WmWindow::GetAuraWindow(window),
-                                base::Bind(&GetActiveBrowserContext));
+bool ChromeShellDelegate::CanShowWindowForUser(aura::Window* window) const {
+  return ::CanShowWindowForUser(window, base::Bind(&GetActiveBrowserContext));
 }
 
 bool ChromeShellDelegate::IsForceMaximizeOnFirstRun() const {
diff --git a/chrome/browser/ui/ash/chrome_shell_delegate.h b/chrome/browser/ui/ash/chrome_shell_delegate.h
index cdfc843e..e2e1063 100644
--- a/chrome/browser/ui/ash/chrome_shell_delegate.h
+++ b/chrome/browser/ui/ash/chrome_shell_delegate.h
@@ -35,7 +35,7 @@
   bool IsMultiProfilesEnabled() const override;
   bool IsIncognitoAllowed() const override;
   bool IsRunningInForcedAppMode() const override;
-  bool CanShowWindowForUser(ash::WmWindow* window) const override;
+  bool CanShowWindowForUser(aura::Window* window) const override;
   bool IsForceMaximizeOnFirstRun() const override;
   void PreInit() override;
   void PreShutdown() override;
diff --git a/chrome/browser/ui/ash/multi_user/multi_user_window_manager_chromeos_unittest.cc b/chrome/browser/ui/ash/multi_user/multi_user_window_manager_chromeos_unittest.cc
index b838b68..d6f8cc9 100644
--- a/chrome/browser/ui/ash/multi_user/multi_user_window_manager_chromeos_unittest.cc
+++ b/chrome/browser/ui/ash/multi_user/multi_user_window_manager_chromeos_unittest.cc
@@ -116,9 +116,9 @@
     return new ash::test::TestSessionStateDelegate;
   }
 
-  bool CanShowWindowForUser(ash::WmWindow* window) const override {
+  bool CanShowWindowForUser(aura::Window* window) const override {
     return ::CanShowWindowForUser(
-        ash::WmWindow::GetAuraWindow(window),
+        window,
         base::Bind(&ash::ShellContentState::GetActiveBrowserContext,
                    base::Unretained(ash::ShellContentState::GetInstance())));
   }
diff --git a/chrome/browser/ui/autofill/autofill_dialog_models.cc b/chrome/browser/ui/autofill/autofill_dialog_models.cc
index 71f02b1..c0ad8ef 100644
--- a/chrome/browser/ui/autofill/autofill_dialog_models.cc
+++ b/chrome/browser/ui/autofill/autofill_dialog_models.cc
@@ -1,21 +1,60 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2012 The Chromium Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include "chrome/browser/ui/autofill/autofill_dialog_models.h"
 
-#include "base/bind.h"
+#include "base/strings/string16.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/stringprintf.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/time/time.h"
 #include "chrome/grit/generated_resources.h"
-#include "components/autofill/core/browser/autofill_country.h"
-#include "components/prefs/pref_service.h"
+#include "components/autofill/core/common/autofill_clock.h"
 #include "ui/base/l10n/l10n_util.h"
 
 namespace autofill {
 
+namespace {
+
+// Number of years to be shown in the year combobox, including the current year.
+// YearComboboxModel has the option of passing an additional year if not
+// contained within the initial range.
+const int kNumberOfExpirationYears = 10;
+
+// Returns the items that are in the expiration year dropdown. If
+// |additional_year| is not 0 and not within the normal range, it will be added
+// accordingly.
+std::vector<base::string16> GetExpirationYearItems(int additional_year) {
+  std::vector<base::string16> years;
+  // Add the "Year" placeholder item.
+  years.push_back(
+      l10n_util::GetStringUTF16(IDS_AUTOFILL_DIALOG_PLACEHOLDER_EXPIRY_YEAR));
+
+  base::Time::Exploded now_exploded;
+  AutofillClock::Now().LocalExplode(&now_exploded);
+
+  if (additional_year != 0 && additional_year < now_exploded.year)
+    years.push_back(base::UTF8ToUTF16(std::to_string(additional_year)));
+
+  for (int i = 0; i < kNumberOfExpirationYears; i++)
+    years.push_back(base::UTF8ToUTF16(std::to_string(now_exploded.year + i)));
+
+  if (additional_year != 0 &&
+      additional_year >= now_exploded.year + kNumberOfExpirationYears) {
+    years.push_back(base::UTF8ToUTF16(std::to_string(additional_year)));
+  }
+
+  return years;
+}
+
+// Formats a month, zero-padded (e.g. "02").
+base::string16 FormatMonth(int month) {
+  return base::ASCIIToUTF16(base::StringPrintf("%.2d", month));
+}
+
+}  // namespace
+
 SuggestionsMenuModelDelegate::~SuggestionsMenuModelDelegate() {}
 
 // SuggestionsMenuModel ----------------------------------------------------
@@ -135,11 +174,6 @@
   return 13;
 }
 
-// static
-base::string16 MonthComboboxModel::FormatMonth(int index) {
-  return base::ASCIIToUTF16(base::StringPrintf("%.2d", index));
-}
-
 base::string16 MonthComboboxModel::GetItemAt(int index) {
   return index == 0 ?
       l10n_util::GetStringUTF16(IDS_AUTOFILL_DIALOG_PLACEHOLDER_EXPIRY_MONTH) :
@@ -148,27 +182,9 @@
 
 // YearComboboxModel -----------------------------------------------------------
 
-YearComboboxModel::YearComboboxModel() : this_year_(0) {
-  base::Time time = base::Time::Now();
-  base::Time::Exploded exploded;
-  time.LocalExplode(&exploded);
-  this_year_ = exploded.year;
-}
+YearComboboxModel::YearComboboxModel(int additional_year)
+    : ui::SimpleComboboxModel(GetExpirationYearItems(additional_year)) {}
 
 YearComboboxModel::~YearComboboxModel() {}
 
-int YearComboboxModel::GetItemCount() const {
-  // 10 years plus the empty entry.
-  return 11;
-}
-
-base::string16 YearComboboxModel::GetItemAt(int index) {
-  if (index == 0) {
-    return l10n_util::GetStringUTF16(
-        IDS_AUTOFILL_DIALOG_PLACEHOLDER_EXPIRY_YEAR);
-  }
-
-  return base::IntToString16(this_year_ + index - 1);
-}
-
 }  // namespace autofill
diff --git a/chrome/browser/ui/autofill/autofill_dialog_models.h b/chrome/browser/ui/autofill/autofill_dialog_models.h
index 0fec489..f2f00e7 100644
--- a/chrome/browser/ui/autofill/autofill_dialog_models.h
+++ b/chrome/browser/ui/autofill/autofill_dialog_models.h
@@ -14,6 +14,7 @@
 #include "base/macros.h"
 #include "base/strings/string16.h"
 #include "ui/base/models/combobox_model.h"
+#include "ui/base/models/simple_combobox_model.h"
 #include "ui/base/models/simple_menu_model.h"
 
 namespace autofill {
@@ -114,8 +115,6 @@
   MonthComboboxModel();
   ~MonthComboboxModel() override;
 
-  static base::string16 FormatMonth(int index);
-
   // ui::Combobox implementation:
   int GetItemCount() const override;
   base::string16 GetItemAt(int index) override;
@@ -125,19 +124,15 @@
 };
 
 // A model for years between now and a decade hence.
-class YearComboboxModel : public ui::ComboboxModel {
+class YearComboboxModel : public ui::SimpleComboboxModel {
  public:
-  YearComboboxModel();
+  // If |additional_year| is not in the year range initially in this model
+  // [current year, current year + 9], this will add |additional_year| to the
+  // model. Passing 0 has no effect.
+  explicit YearComboboxModel(int additional_year = 0);
   ~YearComboboxModel() override;
 
-  // ui::Combobox implementation:
-  int GetItemCount() const override;
-  base::string16 GetItemAt(int index) override;
-
  private:
-  // The current year (e.g., 2012).
-  int this_year_;
-
   DISALLOW_COPY_AND_ASSIGN(YearComboboxModel);
 };
 
diff --git a/chrome/browser/ui/autofill/autofill_dialog_models_unittest.cc b/chrome/browser/ui/autofill/autofill_dialog_models_unittest.cc
index b9a90dea..921a71e 100644
--- a/chrome/browser/ui/autofill/autofill_dialog_models_unittest.cc
+++ b/chrome/browser/ui/autofill/autofill_dialog_models_unittest.cc
@@ -3,9 +3,87 @@
 // found in the LICENSE file.
 
 #include "chrome/browser/ui/autofill/autofill_dialog_models.h"
-#include "testing/gmock/include/gmock/gmock.h"
+
+#include "base/strings/utf_string_conversions.h"
+#include "chrome/grit/generated_resources.h"
+#include "components/autofill/core/browser/test_autofill_clock.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "ui/base/l10n/l10n_util.h"
 
 namespace autofill {
 
+namespace {
+
+const base::Time kJune2017 = base::Time::FromDoubleT(1497552271);
+
+}  // namespace
+
+TEST(YearComboboxModelTest, ExpirationYear) {
+  autofill::TestAutofillClock test_clock;
+  test_clock.SetNow(kJune2017);
+
+  YearComboboxModel model;
+  ASSERT_EQ(11, model.GetItemCount());  // Placeholder + 2017-2026.
+  EXPECT_EQ(
+      l10n_util::GetStringUTF16(IDS_AUTOFILL_DIALOG_PLACEHOLDER_EXPIRY_YEAR),
+      model.GetItemAt(0));
+  EXPECT_EQ(base::ASCIIToUTF16("2017"), model.GetItemAt(1));
+  EXPECT_EQ(base::ASCIIToUTF16("2018"), model.GetItemAt(2));
+  EXPECT_EQ(base::ASCIIToUTF16("2019"), model.GetItemAt(3));
+  EXPECT_EQ(base::ASCIIToUTF16("2020"), model.GetItemAt(4));
+  EXPECT_EQ(base::ASCIIToUTF16("2021"), model.GetItemAt(5));
+  EXPECT_EQ(base::ASCIIToUTF16("2022"), model.GetItemAt(6));
+  EXPECT_EQ(base::ASCIIToUTF16("2023"), model.GetItemAt(7));
+  EXPECT_EQ(base::ASCIIToUTF16("2024"), model.GetItemAt(8));
+  EXPECT_EQ(base::ASCIIToUTF16("2025"), model.GetItemAt(9));
+  EXPECT_EQ(base::ASCIIToUTF16("2026"), model.GetItemAt(10));
+}
+
+// Tests that we show the correct years, including an additional year.
+TEST(YearComboboxModelTest, ShowAdditionalYear) {
+  autofill::TestAutofillClock test_clock;
+  test_clock.SetNow(kJune2017);
+
+  YearComboboxModel model(2016);
+  ASSERT_EQ(12, model.GetItemCount());  // Placeholder + 2016 + 2017-2026.
+  EXPECT_EQ(
+      l10n_util::GetStringUTF16(IDS_AUTOFILL_DIALOG_PLACEHOLDER_EXPIRY_YEAR),
+      model.GetItemAt(0));
+  EXPECT_EQ(base::ASCIIToUTF16("2016"), model.GetItemAt(1));
+  EXPECT_EQ(base::ASCIIToUTF16("2017"), model.GetItemAt(2));
+  EXPECT_EQ(base::ASCIIToUTF16("2018"), model.GetItemAt(3));
+  EXPECT_EQ(base::ASCIIToUTF16("2019"), model.GetItemAt(4));
+  EXPECT_EQ(base::ASCIIToUTF16("2020"), model.GetItemAt(5));
+  EXPECT_EQ(base::ASCIIToUTF16("2021"), model.GetItemAt(6));
+  EXPECT_EQ(base::ASCIIToUTF16("2022"), model.GetItemAt(7));
+  EXPECT_EQ(base::ASCIIToUTF16("2023"), model.GetItemAt(8));
+  EXPECT_EQ(base::ASCIIToUTF16("2024"), model.GetItemAt(9));
+  EXPECT_EQ(base::ASCIIToUTF16("2025"), model.GetItemAt(10));
+  EXPECT_EQ(base::ASCIIToUTF16("2026"), model.GetItemAt(11));
+}
+
+// Tests that we show the additional year, even if it is more than 10 years from
+// now.
+TEST(YearComboboxModelTest, ExpirationYear_ShowFarFutureYear) {
+  autofill::TestAutofillClock test_clock;
+  test_clock.SetNow(kJune2017);
+
+  YearComboboxModel model(2042);
+  ASSERT_EQ(12, model.GetItemCount());  // Placeholder + 2017-2026 + 2042.
+  EXPECT_EQ(
+      l10n_util::GetStringUTF16(IDS_AUTOFILL_DIALOG_PLACEHOLDER_EXPIRY_YEAR),
+      model.GetItemAt(0));
+  EXPECT_EQ(base::ASCIIToUTF16("2017"), model.GetItemAt(1));
+  EXPECT_EQ(base::ASCIIToUTF16("2018"), model.GetItemAt(2));
+  EXPECT_EQ(base::ASCIIToUTF16("2019"), model.GetItemAt(3));
+  EXPECT_EQ(base::ASCIIToUTF16("2020"), model.GetItemAt(4));
+  EXPECT_EQ(base::ASCIIToUTF16("2021"), model.GetItemAt(5));
+  EXPECT_EQ(base::ASCIIToUTF16("2022"), model.GetItemAt(6));
+  EXPECT_EQ(base::ASCIIToUTF16("2023"), model.GetItemAt(7));
+  EXPECT_EQ(base::ASCIIToUTF16("2024"), model.GetItemAt(8));
+  EXPECT_EQ(base::ASCIIToUTF16("2025"), model.GetItemAt(9));
+  EXPECT_EQ(base::ASCIIToUTF16("2026"), model.GetItemAt(10));
+  EXPECT_EQ(base::ASCIIToUTF16("2042"), model.GetItemAt(11));
+}
+
 }  // namespace autofill
diff --git a/chrome/browser/ui/blocked_content/popup_blocker_browsertest.cc b/chrome/browser/ui/blocked_content/popup_blocker_browsertest.cc
index ea174126..2a9fd4f 100644
--- a/chrome/browser/ui/blocked_content/popup_blocker_browsertest.cc
+++ b/chrome/browser/ui/blocked_content/popup_blocker_browsertest.cc
@@ -708,19 +708,16 @@
   tab->GetMainFrame()->ExecuteJavaScriptForTests(
       base::UTF8ToUTF16("var o = document.createElement('object'); o.data = "
                         "'/alert_dialog.pdf'; document.body.appendChild(o);"));
-  app_modal::AppModalDialog* dialog = ui_test_utils::WaitForAppModalDialog();
+  app_modal::JavaScriptAppModalDialog* dialog =
+      ui_test_utils::WaitForAppModalDialog();
 #if !defined(OS_MACOSX)
   if (chrome::FindLastActive() != browser())
     alert_waiter.WaitForActivation();
 #endif
 
   // Verify that after the dialog was closed, the popup is in front again.
-  ASSERT_TRUE(dialog->IsJavaScriptModalDialog());
-  app_modal::JavaScriptAppModalDialog* js_dialog =
-      static_cast<app_modal::JavaScriptAppModalDialog*>(dialog);
-
   ui_test_utils::BrowserActivationWaiter waiter(popup_browser);
-  js_dialog->native_dialog()->AcceptAppModalDialog();
+  dialog->native_dialog()->AcceptAppModalDialog();
   waiter.WaitForActivation();
   ASSERT_EQ(popup_browser, chrome::FindLastActive());
 }
diff --git a/chrome/browser/ui/browser_browsertest.cc b/chrome/browser/ui/browser_browsertest.cc
index 3c05a8e..0610283d 100644
--- a/chrome/browser/ui/browser_browsertest.cc
+++ b/chrome/browser/ui/browser_browsertest.cc
@@ -71,7 +71,6 @@
 #include "chrome/grit/generated_resources.h"
 #include "chrome/test/base/in_process_browser_test.h"
 #include "chrome/test/base/ui_test_utils.h"
-#include "components/app_modal/app_modal_dialog.h"
 #include "components/app_modal/app_modal_dialog_queue.h"
 #include "components/app_modal/javascript_app_modal_dialog.h"
 #include "components/app_modal/native_app_modal_dialog.h"
@@ -127,7 +126,6 @@
 #include "chrome/browser/browser_process.h"
 #endif
 
-using app_modal::AppModalDialog;
 using app_modal::AppModalDialogQueue;
 using app_modal::JavaScriptAppModalDialog;
 using base::ASCIIToUTF16;
@@ -617,7 +615,7 @@
   // Start a navigation to trigger the beforeunload dialog.
   contents->GetMainFrame()->ExecuteJavaScriptForTests(
       ASCIIToUTF16("window.location.href = 'about:blank'"));
-  AppModalDialog* alert = ui_test_utils::WaitForAppModalDialog();
+  JavaScriptAppModalDialog* alert = ui_test_utils::WaitForAppModalDialog();
   EXPECT_TRUE(alert->IsValid());
   AppModalDialogQueue* dialog_queue = AppModalDialogQueue::GetInstance();
   EXPECT_TRUE(dialog_queue->HasActiveDialog());
@@ -708,7 +706,7 @@
   // Navigate to another page, but click cancel in the dialog.  Make sure that
   // the throbber stops spinning.
   chrome::Reload(browser(), WindowOpenDisposition::CURRENT_TAB);
-  AppModalDialog* alert = ui_test_utils::WaitForAppModalDialog();
+  JavaScriptAppModalDialog* alert = ui_test_utils::WaitForAppModalDialog();
 
   alert->CloseModalDialog();
   EXPECT_FALSE(contents->IsLoading());
@@ -835,9 +833,8 @@
   browser()->OpenURL(OpenURLParams(redirect_url, Referrer(),
                                    WindowOpenDisposition::CURRENT_TAB,
                                    ui::PAGE_TRANSITION_TYPED, false));
-  AppModalDialog* alert = ui_test_utils::WaitForAppModalDialog();
-  EXPECT_TRUE(
-      static_cast<JavaScriptAppModalDialog*>(alert)->is_before_unload_dialog());
+  JavaScriptAppModalDialog* alert = ui_test_utils::WaitForAppModalDialog();
+  EXPECT_TRUE(alert->is_before_unload_dialog());
   alert->native_dialog()->AcceptAppModalDialog();
   nav_observer.Wait();
 
@@ -866,7 +863,7 @@
       content::NotificationService::AllSources());
 
   // Cancel the dialog.
-  AppModalDialog* alert = ui_test_utils::WaitForAppModalDialog();
+  JavaScriptAppModalDialog* alert = ui_test_utils::WaitForAppModalDialog();
   alert->CloseModalDialog();
   EXPECT_FALSE(contents->IsLoading());
 
@@ -906,12 +903,11 @@
       ->GetMainFrame()
       ->ExecuteJavaScriptWithUserGestureForTests(
           ASCIIToUTF16("w.close(); alert('bar');"));
-  AppModalDialog* alert = ui_test_utils::WaitForAppModalDialog();
+  JavaScriptAppModalDialog* alert = ui_test_utils::WaitForAppModalDialog();
   alert->native_dialog()->AcceptAppModalDialog();
 
   alert = ui_test_utils::WaitForAppModalDialog();
-  EXPECT_FALSE(static_cast<JavaScriptAppModalDialog*>(alert)->
-                   is_before_unload_dialog());
+  EXPECT_FALSE(alert->is_before_unload_dialog());
   alert->native_dialog()->AcceptAppModalDialog();
 }
 
@@ -925,8 +921,8 @@
 
   // Reload the page, and check that we get a "before reload" dialog.
   chrome::Reload(browser(), WindowOpenDisposition::CURRENT_TAB);
-  AppModalDialog* alert = ui_test_utils::WaitForAppModalDialog();
-  EXPECT_TRUE(static_cast<JavaScriptAppModalDialog*>(alert)->is_reload());
+  JavaScriptAppModalDialog* alert = ui_test_utils::WaitForAppModalDialog();
+  EXPECT_TRUE(alert->is_reload());
 
   // Proceed with the reload.
   alert->native_dialog()->AcceptAppModalDialog();
@@ -941,7 +937,7 @@
                                    ui::PAGE_TRANSITION_TYPED, false));
 
   alert = ui_test_utils::WaitForAppModalDialog();
-  EXPECT_FALSE(static_cast<JavaScriptAppModalDialog*>(alert)->is_reload());
+  EXPECT_FALSE(alert->is_reload());
 
   // Accept the navigation so we end up on a page without a beforeunload hook.
   alert->native_dialog()->AcceptAppModalDialog();
@@ -1013,10 +1009,9 @@
 
   // The beforeunload handler will run at exit, ensure it does, and then accept
   // it to allow shutdown to proceed.
-  AppModalDialog* alert = ui_test_utils::WaitForAppModalDialog();
+  JavaScriptAppModalDialog* alert = ui_test_utils::WaitForAppModalDialog();
   ASSERT_TRUE(alert);
-  EXPECT_TRUE(
-      static_cast<JavaScriptAppModalDialog*>(alert)->is_before_unload_dialog());
+  EXPECT_TRUE(alert->is_before_unload_dialog());
   alert->native_dialog()->AcceptAppModalDialog();
 
   // But wait there's more! If this test times out, it likely means that the
diff --git a/chrome/browser/ui/cocoa/extensions/extension_install_view_controller_unittest.mm b/chrome/browser/ui/cocoa/extensions/extension_install_view_controller_unittest.mm
index a15c22a3..fc11f65b 100644
--- a/chrome/browser/ui/cocoa/extensions/extension_install_view_controller_unittest.mm
+++ b/chrome/browser/ui/cocoa/extensions/extension_install_view_controller_unittest.mm
@@ -83,7 +83,7 @@
   PermissionMessages permissions;
   permissions.push_back(PermissionMessage(base::UTF8ToUTF16("warning 1"),
                                           PermissionIDSet()));
-  prompt->SetPermissions(permissions, type);
+  prompt->AddPermissions(permissions, type);
   base::string16 permissionString = prompt->GetPermission(0, type);
 
   base::scoped_nsobject<ExtensionInstallViewController> controller(
@@ -141,7 +141,7 @@
   PermissionMessages permissions;
   permissions.push_back(PermissionMessage(base::UTF8ToUTF16("warning 1"),
                                           PermissionIDSet()));
-  prompt->SetPermissions(permissions, type);
+  prompt->AddPermissions(permissions, type);
 
   base::scoped_nsobject<ExtensionInstallViewController> controller(
       [[ExtensionInstallViewController alloc]
@@ -171,13 +171,13 @@
   PermissionMessages permissions;
   permissions.push_back(PermissionMessage(base::UTF8ToUTF16("warning 1"),
                                           PermissionIDSet()));
-  one_warning_prompt->SetPermissions(permissions, type);
+  one_warning_prompt->AddPermissions(permissions, type);
 
   std::unique_ptr<ExtensionInstallPrompt::Prompt> two_warnings_prompt(
       chrome::BuildExtensionInstallPrompt(extension_.get()));
   permissions.push_back(PermissionMessage(base::UTF8ToUTF16("warning 2"),
                                           PermissionIDSet()));
-  two_warnings_prompt->SetPermissions(permissions, type);
+  two_warnings_prompt->AddPermissions(permissions, type);
 
   base::scoped_nsobject<ExtensionInstallViewController> controller1(
       [[ExtensionInstallViewController alloc]
@@ -322,7 +322,7 @@
   PermissionMessages permissions;
   permissions.push_back(PermissionMessage(base::UTF8ToUTF16("warning 1"),
                                           PermissionIDSet()));
-  prompt->SetPermissions(permissions, type);
+  prompt->AddPermissions(permissions, type);
 
   base::scoped_nsobject<ExtensionInstallViewController> controller(
       [[ExtensionInstallViewController alloc]
@@ -355,7 +355,7 @@
       base::UTF8ToUTF16("warning 1"),
       PermissionIDSet(),
       std::vector<base::string16>(1, base::UTF8ToUTF16("Detail 1"))));
-  prompt->SetPermissions(permissions, type);
+  prompt->AddPermissions(permissions, type);
   prompt->SetIsShowingDetails(
       ExtensionInstallPrompt::PERMISSIONS_DETAILS, 0, true);
   base::string16 permissionString = prompt->GetPermissionsDetails(0, type);
diff --git a/chrome/browser/ui/views/extensions/extension_install_dialog_view_browsertest.cc b/chrome/browser/ui/views/extensions/extension_install_dialog_view_browsertest.cc
index 3ac2aad7..7bece9ae 100644
--- a/chrome/browser/ui/views/extensions/extension_install_dialog_view_browsertest.cc
+++ b/chrome/browser/ui/views/extensions/extension_install_dialog_view_browsertest.cc
@@ -128,7 +128,7 @@
                                             PermissionIDSet()));
   }
   std::unique_ptr<ExtensionInstallPrompt::Prompt> prompt = CreatePrompt();
-  prompt->SetPermissions(permissions,
+  prompt->AddPermissions(permissions,
                          ExtensionInstallPrompt::REGULAR_PERMISSIONS);
   ASSERT_TRUE(IsScrollbarVisible(std::move(prompt)))
       << "Scrollbar is not visible";
@@ -143,7 +143,7 @@
   permissions.push_back(PermissionMessage(permission_string,
                                           PermissionIDSet()));
   std::unique_ptr<ExtensionInstallPrompt::Prompt> prompt = CreatePrompt();
-  prompt->SetPermissions(permissions,
+  prompt->AddPermissions(permissions,
                          ExtensionInstallPrompt::REGULAR_PERMISSIONS);
   ASSERT_FALSE(IsScrollbarVisible(std::move(prompt))) << "Scrollbar is visible";
 }
diff --git a/chrome/browser/ui/views/frame/browser_view.cc b/chrome/browser/ui/views/frame/browser_view.cc
index 810c6f9..6120c4b 100644
--- a/chrome/browser/ui/views/frame/browser_view.cc
+++ b/chrome/browser/ui/views/frame/browser_view.cc
@@ -103,8 +103,8 @@
 #include "chrome/grit/generated_resources.h"
 #include "chrome/grit/locale_settings.h"
 #include "chrome/grit/theme_resources.h"
-#include "components/app_modal/app_modal_dialog.h"
 #include "components/app_modal/app_modal_dialog_queue.h"
+#include "components/app_modal/javascript_app_modal_dialog.h"
 #include "components/app_modal/native_app_modal_dialog.h"
 #include "components/omnibox/browser/omnibox_popup_model.h"
 #include "components/omnibox/browser/omnibox_popup_view.h"
@@ -2597,7 +2597,7 @@
 
 void BrowserView::ActivateAppModalDialog() const {
   // If another browser is app modal, flash and activate the modal browser.
-  app_modal::AppModalDialog* active_dialog =
+  app_modal::JavaScriptAppModalDialog* active_dialog =
       app_modal::AppModalDialogQueue::GetInstance()->active_dialog();
   if (!active_dialog)
     return;
diff --git a/chrome/browser/ui/views/omnibox/omnibox_result_view.cc b/chrome/browser/ui/views/omnibox/omnibox_result_view.cc
index aff68fc..dab6daf3 100644
--- a/chrome/browser/ui/views/omnibox/omnibox_result_view.cc
+++ b/chrome/browser/ui/views/omnibox/omnibox_result_view.cc
@@ -17,6 +17,7 @@
 
 #include <algorithm>  // NOLINT
 
+#include "base/feature_list.h"
 #include "base/i18n/bidi_line_iterator.h"
 #include "base/metrics/field_trial_params.h"
 #include "base/strings/string_number_conversions.h"
@@ -303,6 +304,8 @@
   int height = GetTextHeight() + (2 * GetVerticalMargin());
   if (match_.answer)
     height += GetAnswerHeight() + kVerticalPadding;
+  else if (base::FeatureList::IsEnabled(omnibox::kUIExperimentVerticalLayout))
+    height += GetTextHeight() + kVerticalPadding;
   return gfx::Size(0, height);
 }
 
@@ -351,42 +354,57 @@
   if (description)
     description->SetDisplayRect(gfx::Rect(gfx::Size(INT_MAX, 0)));
   int contents_max_width, description_max_width;
+  bool description_on_separate_line =
+      match.answer != nullptr ||
+      base::FeatureList::IsEnabled(omnibox::kUIExperimentVerticalLayout);
   OmniboxPopupModel::ComputeMatchMaxWidths(
-      contents->GetContentWidth(),
-      separator_width_,
+      contents->GetContentWidth(), separator_width_,
       description ? description->GetContentWidth() : 0,
-      mirroring_context_->remaining_width(x),
-      match.answer != nullptr,
-      !AutocompleteMatch::IsSearchType(match.type),
-      &contents_max_width,
+      mirroring_context_->remaining_width(x), description_on_separate_line,
+      !AutocompleteMatch::IsSearchType(match.type), &contents_max_width,
       &description_max_width);
 
-  int after_contents_x = DrawRenderText(match, contents, CONTENTS, canvas,
-                                        x, y, contents_max_width);
-
-  if (description_max_width != 0) {
-    if (match.answer) {
-      y += GetTextHeight() + kVerticalPadding;
-      if (!answer_image_.isNull()) {
-        int answer_icon_size = GetAnswerHeight();
-        canvas->DrawImageInt(
-            answer_image_,
-            0, 0, answer_image_.width(), answer_image_.height(),
-            GetMirroredXInView(x), y, answer_icon_size, answer_icon_size, true);
-        // TODO(dschuyler): Perhaps this should be based on the font size
-        // instead of hardcoded to 2 dp (e.g. by adding a space in an
-        // appropriate font to the beginning of the description, then reducing
-        // the additional padding here to zero).
-        const int kAnswerIconToTextPadding = 2;
-        x += answer_icon_size + kAnswerIconToTextPadding;
-      }
-    } else {
-      x = DrawRenderText(match, separator_rendertext_.get(), SEPARATOR, canvas,
-                         after_contents_x, y, separator_width_);
+  // Answers in Suggest results.
+  if (match.answer && description_max_width != 0) {
+    DrawRenderText(match, contents, CONTENTS, canvas, x, y, contents_max_width);
+    y += GetTextHeight() + kVerticalPadding;
+    if (!answer_image_.isNull()) {
+      int answer_icon_size = GetAnswerHeight();
+      canvas->DrawImageInt(answer_image_, 0, 0, answer_image_.width(),
+                           answer_image_.height(), GetMirroredXInView(x), y,
+                           answer_icon_size, answer_icon_size, true);
+      // TODO(dschuyler): Perhaps this should be based on the font size
+      // instead of hardcoded to 2 dp (e.g. by adding a space in an
+      // appropriate font to the beginning of the description, then reducing
+      // the additional padding here to zero).
+      const int kAnswerIconToTextPadding = 2;
+      x += answer_icon_size + kAnswerIconToTextPadding;
     }
+    return;
+  }
 
-    DrawRenderText(match, description, DESCRIPTION, canvas, x, y,
-                   description_max_width);
+  // Regular results.
+  if (base::FeatureList::IsEnabled(omnibox::kUIExperimentVerticalLayout)) {
+    // For no description, shift down halfways to draw contents in middle.
+    if (description_max_width == 0)
+      y += (GetTextHeight() + kVerticalPadding) / 2;
+
+    DrawRenderText(match, contents, CONTENTS, canvas, x, y, contents_max_width);
+
+    if (description_max_width != 0) {
+      y += GetTextHeight() + kVerticalPadding;
+      DrawRenderText(match, description, DESCRIPTION, canvas, x, y,
+                     description_max_width);
+    }
+  } else {
+    x = DrawRenderText(match, contents, CONTENTS, canvas, x, y,
+                       contents_max_width);
+    if (description_max_width != 0) {
+      x = DrawRenderText(match, separator_rendertext_.get(), SEPARATOR, canvas,
+                         x, y, separator_width_);
+      DrawRenderText(match, description, DESCRIPTION, canvas, x, y,
+                     description_max_width);
+    }
   }
 }
 
@@ -599,8 +617,15 @@
       contents_rendertext_ =
           CreateAnswerText(match_.answer->first_line(), font_list_);
     } else {
+      bool swap_match_text =
+          base::FeatureList::IsEnabled(omnibox::kUIExperimentVerticalLayout) &&
+          !AutocompleteMatch::IsSearchType(match_.type) &&
+          !match_.description.empty();
+
       contents_rendertext_ = CreateClassifiedRenderText(
-          match_.contents, match_.contents_class, false);
+          swap_match_text ? match_.description : match_.contents,
+          swap_match_text ? match_.description_class : match_.contents_class,
+          false);
     }
   }
 }
@@ -617,8 +642,12 @@
   const int end_x = width() - start_x;
 
   const gfx::ImageSkia icon = GetIcon();
-  const int icon_y =
-      GetVerticalMargin() + (GetTextHeight() - icon.height()) / 2;
+
+  int row_height = GetTextHeight();
+  if (base::FeatureList::IsEnabled(omnibox::kUIExperimentVerticalLayout))
+    row_height += kVerticalPadding + GetTextHeight();
+
+  const int icon_y = GetVerticalMargin() + (row_height - icon.height()) / 2;
   icon_bounds_.SetRect(start_x, icon_y, icon.width(), icon.height());
 
   const int text_x = start_x + LocationBarView::kIconWidth + horizontal_padding;
@@ -660,8 +689,16 @@
         description_rendertext_ =
             CreateAnswerText(match_.answer->second_line(), GetAnswerFont());
       } else if (!match_.description.empty()) {
+        // If the description is empty, we wouldn't swap with the contents --
+        // nor would we create the description RenderText object anyways.
+        bool swap_match_text = base::FeatureList::IsEnabled(
+                                   omnibox::kUIExperimentVerticalLayout) &&
+                               !AutocompleteMatch::IsSearchType(match_.type);
+
         description_rendertext_ = CreateClassifiedRenderText(
-            match_.description, match_.description_class, true);
+            swap_match_text ? match_.contents : match_.description,
+            swap_match_text ? match_.contents_class : match_.description_class,
+            false);
       }
     }
     PaintMatch(match_, contents_rendertext_.get(),
diff --git a/chrome/browser/ui/views/payments/credit_card_editor_view_controller.cc b/chrome/browser/ui/views/payments/credit_card_editor_view_controller.cc
index 687d476..0068ad3 100644
--- a/chrome/browser/ui/views/payments/credit_card_editor_view_controller.cc
+++ b/chrome/browser/ui/views/payments/credit_card_editor_view_controller.cc
@@ -16,10 +16,10 @@
 #include "base/strings/stringprintf.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/time/time.h"
+#include "chrome/browser/ui/autofill/autofill_dialog_models.h"
 #include "chrome/browser/ui/views/payments/payment_request_dialog_view.h"
 #include "chrome/browser/ui/views/payments/payment_request_dialog_view_ids.h"
 #include "chrome/browser/ui/views/payments/payment_request_views_util.h"
-#include "chrome/browser/ui/views/payments/preselected_combobox_model.h"
 #include "chrome/browser/ui/views/payments/validating_combobox.h"
 #include "chrome/browser/ui/views/payments/validating_textfield.h"
 #include "chrome/grit/generated_resources.h"
@@ -50,12 +50,6 @@
 
 namespace {
 
-// Number of years to be shown in the expiration year dropdown. If the card's
-// year is outside of the range of
-// [currentYear, currentYear+kNumberOfExpirationYears], then the UI shows an
-// additional entry for the card's expiration year.
-const int kNumberOfExpirationYears = 10;
-
 // Opacity of card network icons when they are not selected and are displayed
 // alongside a selected icon.
 const float kDimmedCardIconOpacity = 0.33f;
@@ -64,49 +58,6 @@
 // used.
 const auto kBillingAddressType = autofill::ADDRESS_BILLING_LINE1;
 
-// Returns the items that are in the expiration month dropdown. Will return the
-// months in order starting at "01" until "12". Uses a clock so that the
-// |default_index| is set to the current month.
-std::vector<base::string16> GetExpirationMonthItems(int* default_index) {
-  std::vector<base::string16> months;
-  months.reserve(12);
-  for (int i = 1; i <= 12; i++)
-    months.push_back(base::UTF8ToUTF16(base::StringPrintf("%02d", i)));
-
-  base::Time::Exploded now_exploded;
-  autofill::AutofillClock::Now().LocalExplode(&now_exploded);
-  *default_index = now_exploded.month - 1;
-
-  return months;
-}
-
-// Returns the items that are in the expiration year dropdown.
-std::vector<base::string16> GetExpirationYearItems(
-    const autofill::CreditCard* card_to_edit) {
-  std::vector<base::string16> years;
-  years.reserve(kNumberOfExpirationYears + 1);
-
-  base::Time::Exploded now_exploded;
-  autofill::AutofillClock::Now().LocalExplode(&now_exploded);
-
-  if (card_to_edit && card_to_edit->expiration_year() < now_exploded.year) {
-    years.push_back(
-        base::UTF8ToUTF16(std::to_string(card_to_edit->expiration_year())));
-  }
-
-  for (int i = 0; i < kNumberOfExpirationYears; i++) {
-    years.push_back(base::UTF8ToUTF16(std::to_string(now_exploded.year + i)));
-  }
-
-  if (card_to_edit && card_to_edit->expiration_year() >=
-                          now_exploded.year + kNumberOfExpirationYears) {
-    years.push_back(
-        base::UTF8ToUTF16(std::to_string(card_to_edit->expiration_year())));
-  }
-
-  return years;
-}
-
 bool IsCardExpired(const base::string16& month,
                    const base::string16& year,
                    const std::string& app_locale) {
@@ -533,14 +484,11 @@
     const autofill::ServerFieldType& type) {
   switch (type) {
     case autofill::CREDIT_CARD_EXP_MONTH: {
-      int default_index = 0;
-      std::vector<base::string16> months =
-          GetExpirationMonthItems(&default_index);
-      return base::MakeUnique<PreselectedComboboxModel>(months, default_index);
+      return base::MakeUnique<autofill::MonthComboboxModel>();
     }
     case autofill::CREDIT_CARD_EXP_4_DIGIT_YEAR:
-      return base::MakeUnique<ui::SimpleComboboxModel>(
-          GetExpirationYearItems(credit_card_to_edit_));
+      return base::MakeUnique<autofill::YearComboboxModel>(
+          credit_card_to_edit_ ? credit_card_to_edit_->expiration_year() : 0);
     case kBillingAddressType:
       // The combobox filled with potential billing addresses. It's fine to pass
       // empty string as the default selected guid if there are no cards being
diff --git a/chrome/browser/ui/views/payments/credit_card_editor_view_controller_browsertest.cc b/chrome/browser/ui/views/payments/credit_card_editor_view_controller_browsertest.cc
index c3813dfd..0047e43 100644
--- a/chrome/browser/ui/views/payments/credit_card_editor_view_controller_browsertest.cc
+++ b/chrome/browser/ui/views/payments/credit_card_editor_view_controller_browsertest.cc
@@ -453,16 +453,25 @@
 
   InvokePaymentRequestUI();
 
-  // One instrument is available, but it's not selected.
+  // One instrument is available, and it's selected because being expired can
+  // still select the instrument.
   PaymentRequest* request = GetPaymentRequests(GetActiveWebContents()).front();
   EXPECT_EQ(1U, request->state()->available_instruments().size());
-  EXPECT_EQ(nullptr, request->state()->selected_instrument());
+  EXPECT_NE(nullptr, request->state()->selected_instrument());
 
   OpenPaymentMethodScreen();
 
+  // Opening the credit card editor by clicking the edit button.
+  views::View* list_view = dialog_view()->GetViewByID(
+      static_cast<int>(DialogViewID::PAYMENT_METHOD_SHEET_LIST_VIEW));
+  EXPECT_TRUE(list_view);
+  EXPECT_EQ(1, list_view->child_count());
+
+  views::View* edit_button = list_view->child_at(0)->GetViewByID(
+      static_cast<int>(DialogViewID::EDIT_ITEM_BUTTON));
+
   ResetEventObserver(DialogEvent::CREDIT_CARD_EDITOR_OPENED);
-  ClickOnChildInListViewAndWait(/*child_index=*/0, /*num_children=*/1,
-                                DialogViewID::PAYMENT_METHOD_SHEET_LIST_VIEW);
+  ClickOnDialogViewAndWait(edit_button);
 
   EXPECT_EQ(base::ASCIIToUTF16("Test User"),
             GetEditorTextfieldValue(autofill::CREDIT_CARD_NAME_FULL));
@@ -507,7 +516,7 @@
   EXPECT_EQ(base::ASCIIToUTF16("Test User"),
             credit_card->GetRawInfo(autofill::CREDIT_CARD_NAME_FULL));
 
-  // Still have one instrument, but now it's selected.
+  // Still have one instrument, and it's still selected.
   EXPECT_EQ(1U, request->state()->available_instruments().size());
   EXPECT_EQ(request->state()->available_instruments().back().get(),
             request->state()->selected_instrument());
diff --git a/chrome/browser/ui/views/payments/credit_card_editor_view_controller_unittest.cc b/chrome/browser/ui/views/payments/credit_card_editor_view_controller_unittest.cc
deleted file mode 100644
index a377bd8..0000000
--- a/chrome/browser/ui/views/payments/credit_card_editor_view_controller_unittest.cc
+++ /dev/null
@@ -1,168 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/ui/views/payments/credit_card_editor_view_controller.h"
-
-#include "base/callback_forward.h"
-#include "base/strings/utf_string_conversions.h"
-#include "chrome/browser/ui/views/payments/editor_view_controller.h"
-#include "components/autofill/core/browser/test_autofill_clock.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#include "ui/views/controls/combobox/combobox.h"
-
-namespace payments {
-
-namespace {
-
-const base::Time kJanuary2017 = base::Time::FromDoubleT(1484505871);
-const base::Time kJune2017 = base::Time::FromDoubleT(1497552271);
-
-}  // namespace
-
-// Test that if we are in January, the returned months are as expected, and the
-// default selected month is January.
-TEST(CreditCardEditorViewControllerTest, ExpirationMonth_FromJanuary) {
-  autofill::TestAutofillClock test_clock;
-  test_clock.SetNow(kJanuary2017);
-
-  CreditCardEditorViewController view_controller(
-      nullptr, nullptr, nullptr, BackNavigationType::kPaymentSheet, 0,
-      base::OnceClosure(),
-      base::OnceCallback<void(const autofill::CreditCard&)>(), nullptr);
-
-  auto model =
-      view_controller.GetComboboxModelForType(autofill::CREDIT_CARD_EXP_MONTH);
-  ASSERT_EQ(12, model->GetItemCount());
-  EXPECT_EQ(base::ASCIIToUTF16("01"), model->GetItemAt(0));
-  EXPECT_EQ(base::ASCIIToUTF16("02"), model->GetItemAt(1));
-  EXPECT_EQ(base::ASCIIToUTF16("03"), model->GetItemAt(2));
-  EXPECT_EQ(base::ASCIIToUTF16("04"), model->GetItemAt(3));
-  EXPECT_EQ(base::ASCIIToUTF16("05"), model->GetItemAt(4));
-  EXPECT_EQ(base::ASCIIToUTF16("06"), model->GetItemAt(5));
-  EXPECT_EQ(base::ASCIIToUTF16("07"), model->GetItemAt(6));
-  EXPECT_EQ(base::ASCIIToUTF16("08"), model->GetItemAt(7));
-  EXPECT_EQ(base::ASCIIToUTF16("09"), model->GetItemAt(8));
-  EXPECT_EQ(base::ASCIIToUTF16("10"), model->GetItemAt(9));
-  EXPECT_EQ(base::ASCIIToUTF16("11"), model->GetItemAt(10));
-  EXPECT_EQ(base::ASCIIToUTF16("12"), model->GetItemAt(11));
-
-  // Selected item is the current month.
-  EXPECT_EQ(0, model->GetDefaultIndex());
-}
-
-// Test that if we are in June, the returned months are as expected, and the
-// default selected month is June.
-TEST(CreditCardEditorViewControllerTest, ExpirationMonth_FromJune) {
-  autofill::TestAutofillClock test_clock;
-  test_clock.SetNow(kJune2017);
-
-  CreditCardEditorViewController view_controller(
-      nullptr, nullptr, nullptr, BackNavigationType::kPaymentSheet, 0,
-      base::OnceClosure(),
-      base::OnceCallback<void(const autofill::CreditCard&)>(), nullptr);
-
-  auto model =
-      view_controller.GetComboboxModelForType(autofill::CREDIT_CARD_EXP_MONTH);
-  ASSERT_EQ(12, model->GetItemCount());
-  EXPECT_EQ(base::ASCIIToUTF16("01"), model->GetItemAt(0));
-  EXPECT_EQ(base::ASCIIToUTF16("02"), model->GetItemAt(1));
-  EXPECT_EQ(base::ASCIIToUTF16("03"), model->GetItemAt(2));
-  EXPECT_EQ(base::ASCIIToUTF16("04"), model->GetItemAt(3));
-  EXPECT_EQ(base::ASCIIToUTF16("05"), model->GetItemAt(4));
-  EXPECT_EQ(base::ASCIIToUTF16("06"), model->GetItemAt(5));
-  EXPECT_EQ(base::ASCIIToUTF16("07"), model->GetItemAt(6));
-  EXPECT_EQ(base::ASCIIToUTF16("08"), model->GetItemAt(7));
-  EXPECT_EQ(base::ASCIIToUTF16("09"), model->GetItemAt(8));
-  EXPECT_EQ(base::ASCIIToUTF16("10"), model->GetItemAt(9));
-  EXPECT_EQ(base::ASCIIToUTF16("11"), model->GetItemAt(10));
-  EXPECT_EQ(base::ASCIIToUTF16("12"), model->GetItemAt(11));
-
-  EXPECT_EQ(5, model->GetDefaultIndex());
-}
-
-// Tests that we show the correct amount of years in the year dropdown, starting
-// with the current one.
-TEST(CreditCardEditorViewControllerTest, ExpirationYear_From2017) {
-  autofill::TestAutofillClock test_clock;
-  test_clock.SetNow(kJune2017);
-
-  CreditCardEditorViewController view_controller(
-      nullptr, nullptr, nullptr, BackNavigationType::kPaymentSheet, 0,
-      base::OnceClosure(),
-      base::OnceCallback<void(const autofill::CreditCard&)>(), nullptr);
-
-  auto model = view_controller.GetComboboxModelForType(
-      autofill::CREDIT_CARD_EXP_4_DIGIT_YEAR);
-  ASSERT_EQ(10, model->GetItemCount());
-  EXPECT_EQ(base::ASCIIToUTF16("2017"), model->GetItemAt(0));
-  EXPECT_EQ(base::ASCIIToUTF16("2018"), model->GetItemAt(1));
-  EXPECT_EQ(base::ASCIIToUTF16("2019"), model->GetItemAt(2));
-  EXPECT_EQ(base::ASCIIToUTF16("2020"), model->GetItemAt(3));
-  EXPECT_EQ(base::ASCIIToUTF16("2021"), model->GetItemAt(4));
-  EXPECT_EQ(base::ASCIIToUTF16("2022"), model->GetItemAt(5));
-  EXPECT_EQ(base::ASCIIToUTF16("2023"), model->GetItemAt(6));
-  EXPECT_EQ(base::ASCIIToUTF16("2024"), model->GetItemAt(7));
-  EXPECT_EQ(base::ASCIIToUTF16("2025"), model->GetItemAt(8));
-  EXPECT_EQ(base::ASCIIToUTF16("2026"), model->GetItemAt(9));
-}
-
-// Tests that we show the expiration year of the card, even if the card is
-// expired.
-TEST(CreditCardEditorViewControllerTest, ExpirationYear_ShowExpiredYear) {
-  autofill::TestAutofillClock test_clock;
-  test_clock.SetNow(kJune2017);
-
-  autofill::CreditCard card;
-  card.SetExpirationYear(2016);
-  CreditCardEditorViewController view_controller(
-      nullptr, nullptr, nullptr, BackNavigationType::kPaymentSheet, 0,
-      base::OnceClosure(),
-      base::OnceCallback<void(const autofill::CreditCard&)>(), &card);
-
-  auto model = view_controller.GetComboboxModelForType(
-      autofill::CREDIT_CARD_EXP_4_DIGIT_YEAR);
-  ASSERT_EQ(11, model->GetItemCount());
-  EXPECT_EQ(base::ASCIIToUTF16("2016"), model->GetItemAt(0));
-  EXPECT_EQ(base::ASCIIToUTF16("2017"), model->GetItemAt(1));
-  EXPECT_EQ(base::ASCIIToUTF16("2018"), model->GetItemAt(2));
-  EXPECT_EQ(base::ASCIIToUTF16("2019"), model->GetItemAt(3));
-  EXPECT_EQ(base::ASCIIToUTF16("2020"), model->GetItemAt(4));
-  EXPECT_EQ(base::ASCIIToUTF16("2021"), model->GetItemAt(5));
-  EXPECT_EQ(base::ASCIIToUTF16("2022"), model->GetItemAt(6));
-  EXPECT_EQ(base::ASCIIToUTF16("2023"), model->GetItemAt(7));
-  EXPECT_EQ(base::ASCIIToUTF16("2024"), model->GetItemAt(8));
-  EXPECT_EQ(base::ASCIIToUTF16("2025"), model->GetItemAt(9));
-  EXPECT_EQ(base::ASCIIToUTF16("2026"), model->GetItemAt(10));
-}
-
-// Tests that we show the expiration year of the card, even if the card expires
-// more than 10 years from now.
-TEST(CreditCardEditorViewControllerTest, ExpirationYear_ShowFarFutureYear) {
-  autofill::TestAutofillClock test_clock;
-  test_clock.SetNow(kJune2017);
-
-  autofill::CreditCard card;
-  card.SetExpirationYear(2027);
-  CreditCardEditorViewController view_controller(
-      nullptr, nullptr, nullptr, BackNavigationType::kPaymentSheet, 0,
-      base::OnceClosure(),
-      base::OnceCallback<void(const autofill::CreditCard&)>(), &card);
-
-  auto model = view_controller.GetComboboxModelForType(
-      autofill::CREDIT_CARD_EXP_4_DIGIT_YEAR);
-  ASSERT_EQ(11, model->GetItemCount());
-  EXPECT_EQ(base::ASCIIToUTF16("2017"), model->GetItemAt(0));
-  EXPECT_EQ(base::ASCIIToUTF16("2018"), model->GetItemAt(1));
-  EXPECT_EQ(base::ASCIIToUTF16("2019"), model->GetItemAt(2));
-  EXPECT_EQ(base::ASCIIToUTF16("2020"), model->GetItemAt(3));
-  EXPECT_EQ(base::ASCIIToUTF16("2021"), model->GetItemAt(4));
-  EXPECT_EQ(base::ASCIIToUTF16("2022"), model->GetItemAt(5));
-  EXPECT_EQ(base::ASCIIToUTF16("2023"), model->GetItemAt(6));
-  EXPECT_EQ(base::ASCIIToUTF16("2024"), model->GetItemAt(7));
-  EXPECT_EQ(base::ASCIIToUTF16("2025"), model->GetItemAt(8));
-  EXPECT_EQ(base::ASCIIToUTF16("2026"), model->GetItemAt(9));
-  EXPECT_EQ(base::ASCIIToUTF16("2027"), model->GetItemAt(10));
-}
-
-}  // namespace payments
diff --git a/chrome/browser/ui/views/payments/cvc_unmask_view_controller.cc b/chrome/browser/ui/views/payments/cvc_unmask_view_controller.cc
index 9f005eb..39951a8a 100644
--- a/chrome/browser/ui/views/payments/cvc_unmask_view_controller.cc
+++ b/chrome/browser/ui/views/payments/cvc_unmask_view_controller.cc
@@ -53,6 +53,7 @@
         result_delegate,
     content::WebContents* web_contents)
     : PaymentRequestSheetController(spec, state, dialog),
+      year_combobox_model_(credit_card.expiration_year()),
       credit_card_(credit_card),
       web_contents_(web_contents),
       payments_client_(
@@ -205,11 +206,15 @@
     auto month = base::MakeUnique<views::Combobox>(&month_combobox_model_);
     month->set_listener(this);
     month->set_id(static_cast<int>(DialogViewID::CVC_MONTH));
+    month->SelectValue(credit_card_.ExpirationMonthAsString());
+    month->SetInvalid(true);
     layout->AddView(month.release());
 
     auto year = base::MakeUnique<views::Combobox>(&year_combobox_model_);
     year->set_listener(this);
     year->set_id(static_cast<int>(DialogViewID::CVC_YEAR));
+    year->SelectValue(credit_card_.Expiration4DigitYearAsString());
+    year->SetInvalid(true);
     layout->AddView(year.release());
   }
 
@@ -342,6 +347,9 @@
 
       expiration_date_valid = autofill::IsValidCreditCardExpirationDate(
           year_value, month_value, autofill::AutofillClock::Now());
+
+      month->SetInvalid(!expiration_date_valid);
+      year->SetInvalid(!expiration_date_valid);
     }
   }
 
diff --git a/chrome/browser/ui/views/payments/preselected_combobox_model.cc b/chrome/browser/ui/views/payments/preselected_combobox_model.cc
deleted file mode 100644
index 14339d9..0000000
--- a/chrome/browser/ui/views/payments/preselected_combobox_model.cc
+++ /dev/null
@@ -1,20 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/ui/views/payments/preselected_combobox_model.h"
-
-namespace payments {
-
-PreselectedComboboxModel::PreselectedComboboxModel(
-    const std::vector<base::string16>& items,
-    int default_index)
-    : SimpleComboboxModel(items), default_index_(default_index) {}
-
-PreselectedComboboxModel::~PreselectedComboboxModel() {}
-
-int PreselectedComboboxModel::GetDefaultIndex() const {
-  return default_index_;
-}
-
-}  // namespace payments
diff --git a/chrome/browser/ui/views/payments/preselected_combobox_model.h b/chrome/browser/ui/views/payments/preselected_combobox_model.h
deleted file mode 100644
index 8a1bb75..0000000
--- a/chrome/browser/ui/views/payments/preselected_combobox_model.h
+++ /dev/null
@@ -1,35 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_UI_VIEWS_PAYMENTS_PRESELECTED_COMBOBOX_MODEL_H_
-#define CHROME_BROWSER_UI_VIEWS_PAYMENTS_PRESELECTED_COMBOBOX_MODEL_H_
-
-#include <vector>
-
-#include "base/macros.h"
-#include "base/strings/string16.h"
-#include "ui/base/models/simple_combobox_model.h"
-
-namespace payments {
-
-// A simple data model for a combobox that takes a string16 vector as the items,
-// as well as the default selected index. An empty string is a separator.
-class PreselectedComboboxModel : public ui::SimpleComboboxModel {
- public:
-  PreselectedComboboxModel(const std::vector<base::string16>& items,
-                           int default_index);
-  ~PreselectedComboboxModel() override;
-
-  // ui::ComboboxModel:
-  int GetDefaultIndex() const override;
-
- private:
-  const int default_index_;
-
-  DISALLOW_COPY_AND_ASSIGN(PreselectedComboboxModel);
-};
-
-}  // namespace payments
-
-#endif  // CHROME_BROWSER_UI_VIEWS_PAYMENTS_PRESELECTED_COMBOBOX_MODEL_H_
diff --git a/chrome/browser/ui/webui/fallback_icon_source.cc b/chrome/browser/ui/webui/fallback_icon_source.cc
deleted file mode 100644
index 7fbe9f3..0000000
--- a/chrome/browser/ui/webui/fallback_icon_source.cc
+++ /dev/null
@@ -1,99 +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/ui/webui/fallback_icon_source.h"
-
-#include <vector>
-
-#include "base/memory/ref_counted_memory.h"
-#include "chrome/browser/search/instant_io_context.h"
-#include "chrome/common/url_constants.h"
-#include "components/favicon/core/fallback_icon_service.h"
-#include "components/favicon_base/fallback_icon_url_parser.h"
-#include "components/keyed_service/core/service_access_type.h"
-#include "net/url_request/url_request.h"
-#include "ui/gfx/favicon_size.h"
-#include "url/gurl.h"
-
-FallbackIconSource::FallbackIconSource(
-    favicon::FallbackIconService* fallback_icon_service)
-    : fallback_icon_service_(fallback_icon_service) {
-}
-
-FallbackIconSource::~FallbackIconSource() {
-}
-
-std::string FallbackIconSource::GetSource() const {
-  return chrome::kChromeUIFallbackIconHost;
-}
-
-void FallbackIconSource::StartDataRequest(
-    const std::string& path,
-    const content::ResourceRequestInfo::WebContentsGetter& wc_getter,
-    const content::URLDataSource::GotDataCallback& callback) {
-  chrome::ParsedFallbackIconPath parsed;
-  bool success = parsed.Parse(path);
-  if (!success) {
-    SendDefaultResponse(callback);
-    return;
-  }
-
-  GURL url(parsed.url_string());
-  if (url.is_empty() || !url.is_valid()) {
-    SendDefaultResponse(callback);
-    return;
-  }
-
-  SendFallbackIconHelper(
-      url, parsed.size_in_pixels(), parsed.style(), callback);
-}
-
-std::string FallbackIconSource::GetMimeType(const std::string&) const {
-  // We need to explicitly return a mime type, otherwise if the user tries to
-  // drag the image they get no extension.
-  return "image/png";
-}
-
-bool FallbackIconSource::AllowCaching() const {
-  return false;
-}
-
-bool FallbackIconSource::ShouldReplaceExistingSource() const {
-  // Leave the existing DataSource in place, otherwise we'll drop any pending
-  // requests on the floor.
-  return false;
-}
-
-bool FallbackIconSource::ShouldServiceRequest(
-    const GURL& url,
-    content::ResourceContext* resource_context,
-    int render_process_id) const {
-  if (url.SchemeIs(chrome::kChromeSearchScheme)) {
-    return InstantIOContext::ShouldServiceRequest(url, resource_context,
-                                                  render_process_id);
-  }
-  return URLDataSource::ShouldServiceRequest(url, resource_context,
-                                             render_process_id);
-}
-
-void FallbackIconSource::SendFallbackIconHelper(
-    const GURL& url,
-    int size_in_pixels,
-    const favicon_base::FallbackIconStyle& style,
-    const content::URLDataSource::GotDataCallback& callback) {
-  if (!fallback_icon_service_) {  // Can be null for tests.
-    callback.Run(nullptr);  // Trigger "Not Found" response.
-    return;
-  }
-  std::vector<unsigned char> bitmap_data =
-      fallback_icon_service_->RenderFallbackIconBitmap(
-          url, size_in_pixels, style);
-  callback.Run(base::RefCountedBytes::TakeVector(&bitmap_data));
-}
-
-void FallbackIconSource::SendDefaultResponse(
-    const content::URLDataSource::GotDataCallback& callback) {
-  favicon_base::FallbackIconStyle default_style;
-  SendFallbackIconHelper(GURL(), gfx::kFaviconSize, default_style, callback);
-}
diff --git a/chrome/browser/ui/webui/fallback_icon_source.h b/chrome/browser/ui/webui/fallback_icon_source.h
deleted file mode 100644
index 5526f77..0000000
--- a/chrome/browser/ui/webui/fallback_icon_source.h
+++ /dev/null
@@ -1,91 +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_UI_WEBUI_FALLBACK_ICON_SOURCE_H_
-#define CHROME_BROWSER_UI_WEBUI_FALLBACK_ICON_SOURCE_H_
-
-#include <memory>
-#include <string>
-
-#include "base/macros.h"
-#include "content/public/browser/url_data_source.h"
-
-class GURL;
-
-namespace favicon_base {
-struct FallbackIconStyle;
-}
-
-namespace favicon {
-class FallbackIconService;
-}
-
-// FallbackIconSource services explicit chrome:// requests for fallback icons.
-//
-// Format:
-//   chrome://fallback-icon/size,bc,tc,fsr,r/url
-// All of the parameters except for the url are optional. However, the order of
-// the parameters is not interchangeable, and all "," must be in place.
-//
-// Parameter:
-//  'size'
-//    Positive integer to specify the fallback icon's size in pixels.
-//  'bc'
-//    Fallback icon's background color, as named CSS color, or RGB / ARGB /
-//    RRGGBB / AARRGGBB hex formats (no leading "#").
-//  'tc'
-//    Fallback icon text color, as named CSS color, or RGB / ARGB / RRGGBB /
-//    AARRGGBB hex formats (no leading "#").
-//  'fsr'
-//    Number in [0.0, 1.0] to specify the fallback icon's font size (pixels)
-//    as a ratio to the icon's size.
-//  'r'
-//    Number in [0.0, 1.0] to specify the fallback icon's roundness.
-//    0.0 specifies a square icon; 1.0 specifies a circle icon; intermediate
-//    values specify a rounded square icon.
-//  'url'
-//    String to specify the page URL of the fallback icon.
-//
-//  Example: chrome://fallback-icon/32,red,#000,0.5,1.0/http://www.google.com/
-//    This requests a 32x32 fallback icon for http://www.google.com, using
-//    red as the background color, #000 as the text color, with font size of
-//    32 * 0.5 = 16, and the icon's background shape is a circle.
-class FallbackIconSource : public content::URLDataSource {
- public:
-  // |fallback_icon_service| is owned by caller, and may be null.
-  explicit FallbackIconSource(
-      favicon::FallbackIconService* fallback_icon_service);
-
-  ~FallbackIconSource() override;
-
-  // content::URLDataSource implementation.
-  std::string GetSource() const override;
-  void StartDataRequest(
-      const std::string& path,
-      const content::ResourceRequestInfo::WebContentsGetter& wc_getter,
-      const content::URLDataSource::GotDataCallback& callback) override;
-  std::string GetMimeType(const std::string&) const override;
-  bool AllowCaching() const override;
-  bool ShouldReplaceExistingSource() const override;
-  bool ShouldServiceRequest(const GURL& url,
-                            content::ResourceContext* resource_context,
-                            int render_process_id) const override;
-
- private:
-  void SendFallbackIconHelper(
-      const GURL& url,
-      int size_in_pixels,
-      const favicon_base::FallbackIconStyle& style,
-      const content::URLDataSource::GotDataCallback& callback);
-
-  // Sends the default fallback icon.
-  void SendDefaultResponse(
-      const content::URLDataSource::GotDataCallback& callback);
-
-  favicon::FallbackIconService* fallback_icon_service_;
-
-  DISALLOW_COPY_AND_ASSIGN(FallbackIconSource);
-};
-
-#endif  // CHROME_BROWSER_UI_WEBUI_FALLBACK_ICON_SOURCE_H_
diff --git a/chrome/browser/ui/webui/print_preview/printer_backend_proxy_chromeos.cc b/chrome/browser/ui/webui/print_preview/printer_backend_proxy_chromeos.cc
index 94cca8f5..22867769 100644
--- a/chrome/browser/ui/webui/print_preview/printer_backend_proxy_chromeos.cc
+++ b/chrome/browser/ui/webui/print_preview/printer_backend_proxy_chromeos.cc
@@ -131,7 +131,7 @@
 
     if (prefs_->IsConfigurationCurrent(*printer)) {
       // Skip setup if the printer is already installed.
-      HandlePrinterSetup(std::move(printer), cb, chromeos::SUCCESS);
+      HandlePrinterSetup(std::move(printer), cb, chromeos::kSuccess);
       return;
     }
 
@@ -149,7 +149,7 @@
     DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
 
     switch (result) {
-      case chromeos::PrinterSetupResult::SUCCESS:
+      case chromeos::PrinterSetupResult::kSuccess:
         VLOG(1) << "Printer setup successful for " << printer->id()
                 << " fetching properties";
         prefs_->PrinterInstalled(*printer);
@@ -157,23 +157,26 @@
         // fetch settings on the blocking pool and invoke callback.
         FetchCapabilities(std::move(printer), cb);
         return;
-      case chromeos::PrinterSetupResult::PPD_NOT_FOUND:
+      case chromeos::PrinterSetupResult::kPpdNotFound:
         LOG(WARNING) << "Could not find PPD.  Check printer configuration.";
         // Prompt user to update configuration.
         // TODO(skau): Fill me in
         break;
-      case chromeos::PrinterSetupResult::PPD_UNRETRIEVABLE:
+      case chromeos::PrinterSetupResult::kPpdUnretrievable:
         LOG(WARNING) << "Could not download PPD.  Check Internet connection.";
         // Could not download PPD.  Connect to Internet.
         // TODO(skau): Fill me in
         break;
-      case chromeos::PrinterSetupResult::PRINTER_UNREACHABLE:
-      case chromeos::PrinterSetupResult::DBUS_ERROR:
-      case chromeos::PrinterSetupResult::PPD_TOO_LARGE:
-      case chromeos::PrinterSetupResult::INVALID_PPD:
-      case chromeos::PrinterSetupResult::FATAL_ERROR:
+      case chromeos::PrinterSetupResult::kPrinterUnreachable:
+      case chromeos::PrinterSetupResult::kDbusError:
+      case chromeos::PrinterSetupResult::kPpdTooLarge:
+      case chromeos::PrinterSetupResult::kInvalidPpd:
+      case chromeos::PrinterSetupResult::kFatalError:
         LOG(ERROR) << "Unexpected error in printer setup." << result;
         break;
+      case chromeos::PrinterSetupResult::kMaxValue:
+        NOTREACHED() << "This value is not expected";
+        break;
     }
 
     // TODO(skau): Open printer settings if this is resolvable.
diff --git a/chrome/browser/ui/webui/settings/chromeos/cups_printers_handler.cc b/chrome/browser/ui/webui/settings/chromeos/cups_printers_handler.cc
index 28a1781f..0a07cfa 100644
--- a/chrome/browser/ui/webui/settings/chromeos/cups_printers_handler.cc
+++ b/chrome/browser/ui/webui/settings/chromeos/cups_printers_handler.cc
@@ -256,35 +256,38 @@
     chromeos::PrinterSetupResult result_code) {
   std::string printer_name = printer->display_name();
   switch (result_code) {
-    case chromeos::PrinterSetupResult::SUCCESS: {
+    case chromeos::PrinterSetupResult::kSuccess: {
       auto* manager = PrintersManagerFactory::GetForBrowserContext(profile_);
       manager->PrinterInstalled(*printer);
       manager->RegisterPrinter(std::move(printer));
       break;
     }
-    case chromeos::PrinterSetupResult::PPD_NOT_FOUND:
+    case chromeos::PrinterSetupResult::kPpdNotFound:
       LOG(WARNING) << "Could not locate requested PPD";
       break;
-    case chromeos::PrinterSetupResult::PPD_TOO_LARGE:
+    case chromeos::PrinterSetupResult::kPpdTooLarge:
       LOG(WARNING) << "PPD is too large";
       break;
-    case chromeos::PrinterSetupResult::PPD_UNRETRIEVABLE:
+    case chromeos::PrinterSetupResult::kPpdUnretrievable:
       LOG(WARNING) << "Could not retrieve PPD from server";
       break;
-    case chromeos::PrinterSetupResult::INVALID_PPD:
+    case chromeos::PrinterSetupResult::kInvalidPpd:
       LOG(WARNING) << "Provided PPD is invalid.";
       break;
-    case chromeos::PrinterSetupResult::PRINTER_UNREACHABLE:
+    case chromeos::PrinterSetupResult::kPrinterUnreachable:
       LOG(WARNING) << "Could not contact printer for configuration";
       break;
-    case chromeos::PrinterSetupResult::DBUS_ERROR:
-    case chromeos::PrinterSetupResult::FATAL_ERROR:
+    case chromeos::PrinterSetupResult::kDbusError:
+    case chromeos::PrinterSetupResult::kFatalError:
       LOG(ERROR) << "Unrecoverable error.  Reboot required.";
       break;
+    case chromeos::PrinterSetupResult::kMaxValue:
+      NOTREACHED() << "This is not an expected value";
+      break;
   }
   CallJavascriptFunction(
       "cr.webUIListenerCallback", base::Value("on-add-cups-printer"),
-      base::Value(result_code == chromeos::PrinterSetupResult::SUCCESS),
+      base::Value(result_code == chromeos::PrinterSetupResult::kSuccess),
       base::Value(printer_name));
 }
 
diff --git a/chrome/browser/unload_browsertest.cc b/chrome/browser/unload_browsertest.cc
index 1051724..6b50fa4c 100644
--- a/chrome/browser/unload_browsertest.cc
+++ b/chrome/browser/unload_browsertest.cc
@@ -195,14 +195,12 @@
   // If |accept| is true, simulates user clicking OK, otherwise simulates
   // clicking Cancel.
   void ClickModalDialogButton(bool accept) {
-    app_modal::AppModalDialog* dialog = ui_test_utils::WaitForAppModalDialog();
-    ASSERT_TRUE(dialog->IsJavaScriptModalDialog());
-    app_modal::JavaScriptAppModalDialog* js_dialog =
-        static_cast<app_modal::JavaScriptAppModalDialog*>(dialog);
+    app_modal::JavaScriptAppModalDialog* dialog =
+        ui_test_utils::WaitForAppModalDialog();
     if (accept)
-      js_dialog->native_dialog()->AcceptAppModalDialog();
+      dialog->native_dialog()->AcceptAppModalDialog();
     else
-      js_dialog->native_dialog()->CancelAppModalDialog();
+      dialog->native_dialog()->CancelAppModalDialog();
   }
 
   void PrepareForDialog(Browser* browser) {
diff --git a/chrome/common/BUILD.gn b/chrome/common/BUILD.gn
index 958a29a..e0170a7 100644
--- a/chrome/common/BUILD.gn
+++ b/chrome/common/BUILD.gn
@@ -312,6 +312,7 @@
       "extensions/manifest_handlers/theme_handler.h",
       "extensions/manifest_handlers/ui_overrides_handler.cc",
       "extensions/manifest_handlers/ui_overrides_handler.h",
+      "extensions/mojom/inline_install_traits.h",
       "extensions/permissions/chrome_api_permissions.cc",
       "extensions/permissions/chrome_api_permissions.h",
       "extensions/permissions/chrome_permission_message_provider.cc",
diff --git a/chrome/common/extensions/BUILD.gn b/chrome/common/extensions/BUILD.gn
index fc461066..3d7c604 100644
--- a/chrome/common/extensions/BUILD.gn
+++ b/chrome/common/extensions/BUILD.gn
@@ -26,6 +26,7 @@
 mojom("mojo_bindings") {
   sources = [
     "media_parser.mojom",
+    "mojom/inline_install.mojom",
     "removable_storage_writer.mojom",
   ]
 
diff --git a/chrome/common/extensions/chrome_extension_messages.h b/chrome/common/extensions/chrome_extension_messages.h
index b21ab89c..14712e95 100644
--- a/chrome/common/extensions/chrome_extension_messages.h
+++ b/chrome/common/extensions/chrome_extension_messages.h
@@ -29,6 +29,8 @@
 
 #define IPC_MESSAGE_START ChromeExtensionMsgStart
 
+// TODO(crbug.com/725275): Remove these ipc enums once all ipc messages here are
+// converted to mojo.
 IPC_ENUM_TRAITS_MAX_VALUE(extensions::api::webstore::InstallStage,
                           extensions::api::webstore::INSTALL_STAGE_INSTALLING)
 IPC_ENUM_TRAITS_MAX_VALUE(extensions::webstore_install::Result,
@@ -36,24 +38,6 @@
 
 // Messages sent from the browser to the renderer.
 
-// Sent to the renderer if install stage updates were requested for an inline
-// install.
-IPC_MESSAGE_ROUTED1(ExtensionMsg_InlineInstallStageChanged,
-                    extensions::api::webstore::InstallStage /* stage */)
-
-// Sent to the renderer if download progress updates were requested for an
-// inline install.
-IPC_MESSAGE_ROUTED1(ExtensionMsg_InlineInstallDownloadProgress,
-                    int /* percent_downloaded */)
-
-// Send to renderer once the installation mentioned on
-// ExtensionHostMsg_InlineWebstoreInstall is complete.
-IPC_MESSAGE_ROUTED4(ExtensionMsg_InlineWebstoreInstallResponse,
-                    int32_t /* install id */,
-                    bool /* whether the install was successful */,
-                    std::string /* error */,
-                    extensions::webstore_install::Result /* result */)
-
 IPC_STRUCT_TRAITS_BEGIN(ui::AXNodeData)
   IPC_STRUCT_TRAITS_MEMBER(id)
   IPC_STRUCT_TRAITS_MEMBER(role)
@@ -141,12 +125,3 @@
 IPC_MESSAGE_ROUTED1(ExtensionMsg_AccessibilityLocationChange,
                     ExtensionMsg_AccessibilityLocationChangeParams)
 
-// Messages sent from the renderer to the browser.
-
-
-// Sent by the renderer to implement chrome.webstore.install().
-IPC_MESSAGE_ROUTED4(ExtensionHostMsg_InlineWebstoreInstall,
-                    int32_t /* install id */,
-                    int32_t /* return route id */,
-                    std::string /* Web Store item ID */,
-                    int /* listeners_mask */)
diff --git a/chrome/common/extensions/mojom/OWNERS b/chrome/common/extensions/mojom/OWNERS
new file mode 100644
index 0000000..229dacdd
--- /dev/null
+++ b/chrome/common/extensions/mojom/OWNERS
@@ -0,0 +1,4 @@
+per-file *.mojom=set noparent
+per-file *.mojom=file://ipc/SECURITY_OWNERS
+per-file *.typemap=set noparent
+per-file *.typemap=file://ipc/SECURITY_OWNERS
diff --git a/chrome/common/extensions/mojom/inline_install.mojom b/chrome/common/extensions/mojom/inline_install.mojom
new file mode 100644
index 0000000..e298424
--- /dev/null
+++ b/chrome/common/extensions/mojom/inline_install.mojom
@@ -0,0 +1,31 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+module extensions.mojom;
+
+[Native]
+enum WebstoreInstallResult;
+
+[Native]
+enum WebstoreInstallStage;
+
+interface InlineInstallProgressListener {
+  // Notifies the renderer when install stage updates were requested for an
+  // inline install.
+  InlineInstallStageChanged(WebstoreInstallStage stage);
+
+  // Notifies the renderer when download progress updates were requested for an
+  // inline install.
+  InlineInstallDownloadProgress(int32 percent_downloaded);
+};
+
+interface InlineInstaller {
+  // Sent by the renderer to implement chrome.webstore.install() and notifies
+  // the renderer once the installation is complete.
+  DoInlineInstall(string webstore_item_id, int32 listeners_mask,
+      InlineInstallProgressListener install_progress_listener) =>
+      (bool success, string error, WebstoreInstallResult result);
+};
+
+
diff --git a/chrome/common/extensions/mojom/inline_install.typemap b/chrome/common/extensions/mojom/inline_install.typemap
new file mode 100644
index 0000000..3a08ce5
--- /dev/null
+++ b/chrome/common/extensions/mojom/inline_install.typemap
@@ -0,0 +1,14 @@
+# Copyright 2017 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+mojom = "//chrome/common/extensions/mojom/inline_install.mojom"
+public_headers = [
+  "//chrome/common/extensions/api/webstore/webstore_api_constants.h",
+  "//chrome/common/extensions/webstore_install_result.h",
+]
+traits_headers = [ "//chrome/common/extensions/mojom/inline_install_traits.h" ]
+type_mappings = [
+  "extensions.mojom.WebstoreInstallStage=::extensions::api::webstore::InstallStage",
+  "extensions.mojom.WebstoreInstallResult=::extensions::webstore_install::Result",
+]
diff --git a/chrome/common/extensions/mojom/inline_install_traits.h b/chrome/common/extensions/mojom/inline_install_traits.h
new file mode 100644
index 0000000..4363634
--- /dev/null
+++ b/chrome/common/extensions/mojom/inline_install_traits.h
@@ -0,0 +1,18 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_COMMON_EXTENSIONS_MOJOM_INLINE_INSTALL_TRAITS_H_
+#define CHROME_COMMON_EXTENSIONS_MOJOM_INLINE_INSTALL_TRAITS_H_
+
+#include "chrome/common/extensions/api/webstore/webstore_api_constants.h"
+#include "chrome/common/extensions/webstore_install_result.h"
+#include "ipc/ipc_message_macros.h"
+
+IPC_ENUM_TRAITS_MAX_VALUE(
+    extensions::api::webstore::InstallStage,
+    extensions::api::webstore::InstallStage::INSTALL_STAGE_INSTALLING)
+IPC_ENUM_TRAITS_MAX_VALUE(extensions::webstore_install::Result,
+                          extensions::webstore_install::RESULT_LAST)
+
+#endif  // CHROME_COMMON_EXTENSIONS_MOJOM_INLINE_INSTALL_TRAITS_H_
diff --git a/chrome/common/extensions/typemaps.gni b/chrome/common/extensions/typemaps.gni
index 0194203..17f61e7 100644
--- a/chrome/common/extensions/typemaps.gni
+++ b/chrome/common/extensions/typemaps.gni
@@ -2,4 +2,7 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-typemaps = [ "//chrome/common/extensions/media_parser.typemap" ]
+typemaps = [
+  "//chrome/common/extensions/media_parser.typemap",
+  "//chrome/common/extensions/mojom/inline_install.typemap",
+]
diff --git a/chrome/common/url_constants.cc b/chrome/common/url_constants.cc
index d21a5c8..ea8be8c 100644
--- a/chrome/common/url_constants.cc
+++ b/chrome/common/url_constants.cc
@@ -52,7 +52,6 @@
 const char kChromeUIExtensionIconURL[] = "chrome://extension-icon/";
 const char kChromeUIExtensionsFrameURL[] = "chrome://extensions-frame/";
 const char kChromeUIExtensionsURL[] = "chrome://extensions/";
-const char kChromeUIFallbackIconURL[] = "chrome://fallback-icon/";
 const char kChromeUIFaviconURL[] = "chrome://favicon/";
 const char kChromeUIFeedbackURL[] = "chrome://feedback/";
 const char kChromeUIFlagsURL[] = "chrome://flags/";
@@ -199,7 +198,6 @@
 const char kChromeUIExtensionIconHost[] = "extension-icon";
 const char kChromeUIExtensionsFrameHost[] = "extensions-frame";
 const char kChromeUIExtensionsHost[] = "extensions";
-const char kChromeUIFallbackIconHost[] = "fallback-icon";
 const char kChromeUIFaviconHost[] = "favicon";
 const char kChromeUIFeedbackHost[] = "feedback";
 const char kChromeUIFlagsHost[] = "flags";
diff --git a/chrome/renderer/extensions/webstore_bindings.cc b/chrome/renderer/extensions/webstore_bindings.cc
index 2a3c2aa..290814e 100644
--- a/chrome/renderer/extensions/webstore_bindings.cc
+++ b/chrome/renderer/extensions/webstore_bindings.cc
@@ -9,8 +9,8 @@
 #include "base/macros.h"
 #include "base/strings/string_util.h"
 #include "chrome/common/extensions/api/webstore/webstore_api_constants.h"
-#include "chrome/common/extensions/chrome_extension_messages.h"
 #include "components/crx_file/id_util.h"
+#include "content/public/common/associated_interface_provider.h"
 #include "content/public/renderer/render_frame.h"
 #include "extensions/common/extension.h"
 #include "extensions/common/extension_urls.h"
@@ -45,7 +45,7 @@
 
 // chrome.webstore.install() calls generate an install ID so that the install's
 // callbacks may be fired when the browser notifies us of install completion
-// (successful or not) via OnInlineWebstoreInstallResponse.
+// (successful or not) via |InlineInstallResponse|.
 int g_next_install_id = 0;
 
 } // anonymous namespace
@@ -54,6 +54,59 @@
     : ObjectBackedNativeHandler(context) {
   RouteFunction("Install", "webstore",
                 base::Bind(&WebstoreBindings::Install, base::Unretained(this)));
+  content::RenderFrame* render_frame = context->GetRenderFrame();
+  if (render_frame)
+    render_frame->GetRemoteAssociatedInterfaces()->GetInterface(
+        &inline_installer_);
+}
+
+WebstoreBindings::~WebstoreBindings() {}
+
+void WebstoreBindings::InlineInstallResponse(int install_id,
+                                             bool success,
+                                             const std::string& error,
+                                             webstore_install::Result result) {
+  v8::Isolate* isolate = context()->isolate();
+  v8::HandleScope handle_scope(isolate);
+  v8::Context::Scope context_scope(context()->v8_context());
+  v8::Local<v8::Value> argv[] = {
+      v8::Integer::New(isolate, install_id), v8::Boolean::New(isolate, success),
+      v8::String::NewFromUtf8(isolate, error.c_str()),
+      v8::String::NewFromUtf8(
+          isolate,
+          api::webstore::kInstallResultCodes[static_cast<int>(result)])};
+  context()->module_system()->CallModuleMethodSafe(
+      "webstore", "onInstallResponse", arraysize(argv), argv);
+}
+
+void WebstoreBindings::InlineInstallStageChanged(
+    api::webstore::InstallStage stage) {
+  const char* stage_string = nullptr;
+  switch (stage) {
+    case api::webstore::INSTALL_STAGE_DOWNLOADING:
+      stage_string = api::webstore::kInstallStageDownloading;
+      break;
+    case api::webstore::INSTALL_STAGE_INSTALLING:
+      stage_string = api::webstore::kInstallStageInstalling;
+      break;
+  }
+  v8::Isolate* isolate = context()->isolate();
+  v8::HandleScope handle_scope(isolate);
+  v8::Context::Scope context_scope(context()->v8_context());
+  v8::Local<v8::Value> argv[] = {
+      v8::String::NewFromUtf8(isolate, stage_string)};
+  context()->module_system()->CallModuleMethodSafe(
+      "webstore", "onInstallStageChanged", arraysize(argv), argv);
+}
+
+void WebstoreBindings::InlineInstallDownloadProgress(int percent_downloaded) {
+  v8::Isolate* isolate = context()->isolate();
+  v8::HandleScope handle_scope(isolate);
+  v8::Context::Scope context_scope(context()->v8_context());
+  v8::Local<v8::Value> argv[] = {
+      v8::Number::New(isolate, percent_downloaded / 100.0)};
+  context()->module_system()->CallModuleMethodSafe(
+      "webstore", "onDownloadProgress", arraysize(argv), argv);
 }
 
 void WebstoreBindings::Install(
@@ -91,10 +144,13 @@
 
   int install_id = g_next_install_id++;
 
-  Send(new ExtensionHostMsg_InlineWebstoreInstall(
-      render_frame->GetRoutingID(), install_id, GetRoutingID(),
-      webstore_item_id, listener_mask));
+  mojom::InlineInstallProgressListenerPtr install_progress_listener =
+      install_progress_listener_bindings_.CreateInterfacePtrAndBind(this);
 
+  inline_installer_->DoInlineInstall(
+      webstore_item_id, listener_mask, std::move(install_progress_listener),
+      base::Bind(&WebstoreBindings::InlineInstallResponse,
+                 base::Unretained(this), install_id));
   args.GetReturnValue().Set(static_cast<int32_t>(install_id));
 }
 
@@ -186,67 +242,4 @@
   return false;
 }
 
-bool WebstoreBindings::OnMessageReceived(const IPC::Message& message) {
-  IPC_BEGIN_MESSAGE_MAP(WebstoreBindings, message)
-    IPC_MESSAGE_HANDLER(ExtensionMsg_InlineWebstoreInstallResponse,
-                        OnInlineWebstoreInstallResponse)
-    IPC_MESSAGE_HANDLER(ExtensionMsg_InlineInstallStageChanged,
-                        OnInlineInstallStageChanged)
-    IPC_MESSAGE_HANDLER(ExtensionMsg_InlineInstallDownloadProgress,
-                        OnInlineInstallDownloadProgress)
-    IPC_MESSAGE_UNHANDLED(CHECK(false) << "Unhandled IPC message")
-  IPC_END_MESSAGE_MAP()
-  return true;
-}
-
-void WebstoreBindings::OnInlineWebstoreInstallResponse(
-    int install_id,
-    bool success,
-    const std::string& error,
-    webstore_install::Result result) {
-  v8::Isolate* isolate = context()->isolate();
-  v8::HandleScope handle_scope(isolate);
-  v8::Context::Scope context_scope(context()->v8_context());
-  v8::Local<v8::Value> argv[] = {
-    v8::Integer::New(isolate, install_id),
-    v8::Boolean::New(isolate, success),
-    v8::String::NewFromUtf8(isolate, error.c_str()),
-    v8::String::NewFromUtf8(
-        isolate, api::webstore::kInstallResultCodes[static_cast<int>(result)])
-  };
-  context()->module_system()->CallModuleMethodSafe(
-      "webstore", "onInstallResponse", arraysize(argv), argv);
-}
-
-void WebstoreBindings::OnInlineInstallStageChanged(int stage) {
-  const char* stage_string = NULL;
-  api::webstore::InstallStage install_stage =
-      static_cast<api::webstore::InstallStage>(stage);
-  switch (install_stage) {
-    case api::webstore::INSTALL_STAGE_DOWNLOADING:
-      stage_string = api::webstore::kInstallStageDownloading;
-      break;
-    case api::webstore::INSTALL_STAGE_INSTALLING:
-      stage_string = api::webstore::kInstallStageInstalling;
-      break;
-  }
-  v8::Isolate* isolate = context()->isolate();
-  v8::HandleScope handle_scope(isolate);
-  v8::Context::Scope context_scope(context()->v8_context());
-  v8::Local<v8::Value> argv[] = {
-      v8::String::NewFromUtf8(isolate, stage_string)};
-  context()->module_system()->CallModuleMethodSafe(
-      "webstore", "onInstallStageChanged", arraysize(argv), argv);
-}
-
-void WebstoreBindings::OnInlineInstallDownloadProgress(int percent_downloaded) {
-  v8::Isolate* isolate = context()->isolate();
-  v8::HandleScope handle_scope(isolate);
-  v8::Context::Scope context_scope(context()->v8_context());
-  v8::Local<v8::Value> argv[] = {
-      v8::Number::New(isolate, percent_downloaded / 100.0)};
-  context()->module_system()->CallModuleMethodSafe(
-      "webstore", "onDownloadProgress", arraysize(argv), argv);
-}
-
 }  // namespace extensions
diff --git a/chrome/renderer/extensions/webstore_bindings.h b/chrome/renderer/extensions/webstore_bindings.h
index 641fddd..09c5dacb 100644
--- a/chrome/renderer/extensions/webstore_bindings.h
+++ b/chrome/renderer/extensions/webstore_bindings.h
@@ -7,39 +7,39 @@
 
 #include "base/compiler_specific.h"
 #include "base/macros.h"
+#include "chrome/common/extensions/mojom/inline_install.mojom.h"
 #include "chrome/common/extensions/webstore_install_result.h"
-#include "chrome/renderer/extensions/chrome_v8_extension_handler.h"
 #include "extensions/renderer/object_backed_native_handler.h"
+#include "mojo/public/cpp/bindings/binding_set.h"
+#include "v8/include/v8.h"
 
 namespace blink {
 class WebLocalFrame;
 }
 
 namespace extensions {
+class ScriptContext;
 
 // A V8 extension that creates an object at window.chrome.webstore. This object
 // allows JavaScript to initiate inline installs of apps that are listed in the
 // Chrome Web Store (CWS).
 class WebstoreBindings : public ObjectBackedNativeHandler,
-                         public ChromeV8ExtensionHandler {
+                         public mojom::InlineInstallProgressListener {
  public:
   explicit WebstoreBindings(ScriptContext* context);
+  ~WebstoreBindings() override;
 
-  // IPC::Listener
-  bool OnMessageReceived(const IPC::Message& message) override;
+  // mojom::InlineInstallProgressListener:
+  void InlineInstallStageChanged(api::webstore::InstallStage stage) override;
+  void InlineInstallDownloadProgress(int percent_downloaded) override;
 
  private:
+  void InlineInstallResponse(int install_id,
+                             bool success,
+                             const std::string& error,
+                             webstore_install::Result result);
   void Install(const v8::FunctionCallbackInfo<v8::Value>& args);
 
-  void OnInlineWebstoreInstallResponse(int install_id,
-                                       bool success,
-                                       const std::string& error,
-                                       webstore_install::Result result);
-
-  void OnInlineInstallStageChanged(int stage);
-
-  void OnInlineInstallDownloadProgress(int percent_downloaded);
-
   // Extracts a Web Store item ID from a <link rel="chrome-webstore-item"
   // href="https://chrome.google.com/webstore/detail/id"> node found in the
   // frame. On success, true will be returned and the |webstore_item_id|
@@ -51,6 +51,11 @@
       std::string* webstore_item_id,
       std::string* error);
 
+  mojom::InlineInstallerAssociatedPtr inline_installer_;
+
+  mojo::BindingSet<mojom::InlineInstallProgressListener>
+      install_progress_listener_bindings_;
+
   DISALLOW_COPY_AND_ASSIGN(WebstoreBindings);
 };
 
diff --git a/chrome/renderer/searchbox/searchbox.cc b/chrome/renderer/searchbox/searchbox.cc
index 035d81a..b07e104 100644
--- a/chrome/renderer/searchbox/searchbox.cc
+++ b/chrome/renderer/searchbox/searchbox.cc
@@ -20,10 +20,8 @@
 #include "chrome/common/render_messages.h"
 #include "chrome/common/url_constants.h"
 #include "chrome/renderer/searchbox/searchbox_extension.h"
-#include "components/favicon_base/fallback_icon_url_parser.h"
 #include "components/favicon_base/favicon_types.h"
 #include "components/favicon_base/favicon_url_parser.h"
-#include "components/favicon_base/large_icon_url_parser.h"
 #include "components/omnibox/common/omnibox_focus_state.h"
 #include "content/public/common/associated_interface_provider.h"
 #include "content/public/common/associated_interface_registry.h"
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index 7f2ceae..1c6e150 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -3058,7 +3058,6 @@
     "../browser/engagement/site_engagement_score_unittest.cc",
     "../browser/engagement/site_engagement_service_unittest.cc",
     "../browser/external_protocol/external_protocol_handler_unittest.cc",
-    "../browser/favicon/chrome_fallback_icon_client_unittest.cc",
     "../browser/file_select_helper_unittest.cc",
     "../browser/gcm/fake_gcm_profile_service.cc",
     "../browser/gcm/fake_gcm_profile_service.h",
@@ -4745,7 +4744,6 @@
       "../browser/ui/views/global_error_bubble_view_unittest.cc",
       "../browser/ui/views/harmony/layout_provider_unittest.cc",
       "../browser/ui/views/page_info/page_info_bubble_view_unittest.cc",
-      "../browser/ui/views/payments/credit_card_editor_view_controller_unittest.cc",
       "../browser/ui/views/payments/payment_request_item_list_unittest.cc",
       "../browser/ui/views/payments/validating_textfield_unittest.cc",
       "../browser/ui/views/payments/view_stack_unittest.cc",
diff --git a/chrome/test/base/testing_profile.cc b/chrome/test/base/testing_profile.cc
index b9962788..bbff5e9c 100644
--- a/chrome/test/base/testing_profile.cc
+++ b/chrome/test/base/testing_profile.cc
@@ -27,8 +27,6 @@
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/chrome_notification_types.h"
 #include "chrome/browser/content_settings/host_content_settings_map_factory.h"
-#include "chrome/browser/favicon/chrome_fallback_icon_client_factory.h"
-#include "chrome/browser/favicon/fallback_icon_service_factory.h"
 #include "chrome/browser/favicon/favicon_service_factory.h"
 #include "chrome/browser/history/chrome_history_client.h"
 #include "chrome/browser/history/history_service_factory.h"
@@ -57,7 +55,6 @@
 #include "components/bookmarks/browser/bookmark_model.h"
 #include "components/bookmarks/common/bookmark_constants.h"
 #include "components/content_settings/core/browser/host_content_settings_map.h"
-#include "components/favicon/core/fallback_icon_service.h"
 #include "components/favicon/core/favicon_service.h"
 #include "components/history/content/browser/content_visit_delegate.h"
 #include "components/history/content/browser/history_database_helper.h"
diff --git a/chrome/test/base/ui_test_utils.cc b/chrome/test/base/ui_test_utils.cc
index dbfc6b7f..3214d5bb 100644
--- a/chrome/test/base/ui_test_utils.cc
+++ b/chrome/test/base/ui_test_utils.cc
@@ -40,7 +40,6 @@
 #include "chrome/common/chrome_paths.h"
 #include "chrome/common/pref_names.h"
 #include "chrome/test/base/find_in_page_observer.h"
-#include "components/app_modal/app_modal_dialog.h"
 #include "components/app_modal/app_modal_dialog_queue.h"
 #include "components/app_modal/javascript_app_modal_dialog.h"
 #include "components/bookmarks/browser/bookmark_model.h"
@@ -109,7 +108,7 @@
   AppModalDialogWaiter() : dialog_(nullptr) {}
   ~AppModalDialogWaiter() override {}
 
-  app_modal::AppModalDialog* Wait() {
+  app_modal::JavaScriptAppModalDialog* Wait() {
     if (dialog_)
       return dialog_;
     message_loop_runner_ = new content::MessageLoopRunner;
@@ -119,7 +118,7 @@
   }
 
   // AppModalDialogObserver:
-  void Notify(app_modal::AppModalDialog* dialog) override {
+  void Notify(app_modal::JavaScriptAppModalDialog* dialog) override {
     DCHECK(!dialog_);
     dialog_ = dialog;
     CheckForHangMonitorDisabling(dialog);
@@ -127,17 +126,14 @@
       message_loop_runner_->Quit();
   }
 
-  static void CheckForHangMonitorDisabling(app_modal::AppModalDialog* dialog) {
+  static void CheckForHangMonitorDisabling(
+      app_modal::JavaScriptAppModalDialog* dialog) {
     // If a test waits for a beforeunload dialog but hasn't disabled the
     // beforeunload hang timer before triggering it, there will be a race
     // between the dialog and the timer and the test will be flaky. We can't
     // disable the timer here, as it's too late, but we can tell when we've won
     // a race that we shouldn't have been in.
-    if (!dialog->IsJavaScriptModalDialog())
-      return;
-
-    auto* js_dialog = static_cast<app_modal::JavaScriptAppModalDialog*>(dialog);
-    if (!js_dialog->is_before_unload_dialog())
+    if (!dialog->is_before_unload_dialog())
       return;
 
     // Unfortunately we don't know which frame spawned this dialog and should
@@ -155,7 +151,7 @@
   }
 
  private:
-  app_modal::AppModalDialog* dialog_;
+  app_modal::JavaScriptAppModalDialog* dialog_;
   scoped_refptr<content::MessageLoopRunner> message_loop_runner_;
 
   DISALLOW_COPY_AND_ASSIGN(AppModalDialogWaiter);
@@ -336,7 +332,7 @@
   return true;
 }
 
-app_modal::AppModalDialog* WaitForAppModalDialog() {
+app_modal::JavaScriptAppModalDialog* WaitForAppModalDialog() {
   app_modal::AppModalDialogQueue* dialog_queue =
       app_modal::AppModalDialogQueue::GetInstance();
   if (dialog_queue->HasActiveDialog()) {
diff --git a/chrome/test/base/ui_test_utils.h b/chrome/test/base/ui_test_utils.h
index 24bd2d97..fac5d03 100644
--- a/chrome/test/base/ui_test_utils.h
+++ b/chrome/test/base/ui_test_utils.h
@@ -33,7 +33,7 @@
 class Profile;
 
 namespace app_modal {
-class AppModalDialog;
+class JavaScriptAppModalDialog;
 }
 
 namespace base {
@@ -130,8 +130,8 @@
 // Generate the path of the build directory, relative to the source root.
 bool GetRelativeBuildDirectory(base::FilePath* build_dir);
 
-// Blocks until an application modal dialog is showns and returns it.
-app_modal::AppModalDialog* WaitForAppModalDialog();
+// Blocks until an application modal dialog is shown and returns it.
+app_modal::JavaScriptAppModalDialog* WaitForAppModalDialog();
 
 // Performs a find in the page of the specified tab. Returns the number of
 // matches found.  |ordinal| is an optional parameter which is set to the index
diff --git a/chrome/test/chromedriver/chrome/stub_web_view.cc b/chrome/test/chromedriver/chrome/stub_web_view.cc
index 6df576da4..f833e8af 100644
--- a/chrome/test/chromedriver/chrome/stub_web_view.cc
+++ b/chrome/test/chromedriver/chrome/stub_web_view.cc
@@ -119,6 +119,17 @@
   return Status(kOk);
 }
 
+Status StubWebView::AddCookie(const std::string& name,
+                              const std::string& url,
+                              const std::string& value,
+                              const std::string& domain,
+                              const std::string& path,
+                              bool secure,
+                              bool httpOnly,
+                              double expiry) {
+  return Status(kOk);
+}
+
 Status StubWebView::WaitForPendingNavigations(const std::string& frame_id,
                                               const Timeout& timeout,
                                               bool stop_load_on_timeout) {
diff --git a/chrome/test/chromedriver/chrome/stub_web_view.h b/chrome/test/chromedriver/chrome/stub_web_view.h
index 34d596d..5e3db39 100644
--- a/chrome/test/chromedriver/chrome/stub_web_view.h
+++ b/chrome/test/chromedriver/chrome/stub_web_view.h
@@ -59,6 +59,14 @@
   Status DispatchKeyEvents(const std::list<KeyEvent>& events) override;
   Status GetCookies(std::unique_ptr<base::ListValue>* cookies) override;
   Status DeleteCookie(const std::string& name, const std::string& url) override;
+  Status AddCookie(const std::string& name,
+                   const std::string& url,
+                   const std::string& value,
+                   const std::string& domain,
+                   const std::string& path,
+                   bool secure,
+                   bool httpOnly,
+                   double expiry) override;
   Status WaitForPendingNavigations(const std::string& frame_id,
                                    const Timeout& timeout,
                                    bool stop_load_on_timeout) override;
diff --git a/chrome/test/chromedriver/chrome/web_view.h b/chrome/test/chromedriver/chrome/web_view.h
index 6bd3b7bb..b80ccb5 100644
--- a/chrome/test/chromedriver/chrome/web_view.h
+++ b/chrome/test/chromedriver/chrome/web_view.h
@@ -138,6 +138,15 @@
   virtual Status DeleteCookie(const std::string& name,
                               const std::string& url) = 0;
 
+  virtual Status AddCookie(const std::string& name,
+                           const std::string& url,
+                           const std::string& value,
+                           const std::string& domain,
+                           const std::string& path,
+                           bool secure,
+                           bool httpOnly,
+                           double expiry) = 0;
+
   // Waits until all pending navigations have completed in the given frame.
   // If |frame_id| is "", waits for navigations on the main frame.
   // If a modal dialog appears while waiting, kUnexpectedAlertOpen will be
diff --git a/chrome/test/chromedriver/chrome/web_view_impl.cc b/chrome/test/chromedriver/chrome/web_view_impl.cc
index ef4aee9..5ea721f 100644
--- a/chrome/test/chromedriver/chrome/web_view_impl.cc
+++ b/chrome/test/chromedriver/chrome/web_view_impl.cc
@@ -459,6 +459,26 @@
   return client_->SendCommand("Page.deleteCookie", params);
 }
 
+Status WebViewImpl::AddCookie(const std::string& name,
+                              const std::string& url,
+                              const std::string& value,
+                              const std::string& domain,
+                              const std::string& path,
+                              bool secure,
+                              bool httpOnly,
+                              double expiry) {
+  base::DictionaryValue params;
+  params.SetString("name", name);
+  params.SetString("url", url);
+  params.SetString("value", value);
+  params.SetString("domain", domain);
+  params.SetString("path", path);
+  params.SetBoolean("secure", secure);
+  params.SetBoolean("httpOnly", httpOnly);
+  params.SetDouble("expirationDate", expiry);
+  return client_->SendCommand("Network.setCookie", params);
+}
+
 Status WebViewImpl::WaitForPendingNavigations(const std::string& frame_id,
                                               const Timeout& timeout,
                                               bool stop_load_on_timeout) {
diff --git a/chrome/test/chromedriver/chrome/web_view_impl.h b/chrome/test/chromedriver/chrome/web_view_impl.h
index 7e80da1..21169aad 100644
--- a/chrome/test/chromedriver/chrome/web_view_impl.h
+++ b/chrome/test/chromedriver/chrome/web_view_impl.h
@@ -86,6 +86,14 @@
   Status DispatchKeyEvents(const std::list<KeyEvent>& events) override;
   Status GetCookies(std::unique_ptr<base::ListValue>* cookies) override;
   Status DeleteCookie(const std::string& name, const std::string& url) override;
+  Status AddCookie(const std::string& name,
+                   const std::string& url,
+                   const std::string& value,
+                   const std::string& domain,
+                   const std::string& path,
+                   bool secure,
+                   bool httpOnly,
+                   double expiry) override;
   Status WaitForPendingNavigations(const std::string& frame_id,
                                    const Timeout& timeout,
                                    bool stop_load_on_timeout) override;
diff --git a/chrome/test/chromedriver/window_commands.cc b/chrome/test/chromedriver/window_commands.cc
index e54307f..e58d70f 100644
--- a/chrome/test/chromedriver/window_commands.cc
+++ b/chrome/test/chromedriver/window_commands.cc
@@ -38,6 +38,9 @@
 
 const std::string kUnreachableWebDataURL = "data:text/html,chromewebdata";
 
+// Defaults to 20 years into the future when adding a cookie.
+const double kDefaultCookieExpiryTime = 20*365*24*60*60;
+
 Status GetMouseButton(const base::DictionaryValue& params,
                       MouseButton* button) {
   int button_num;
@@ -948,11 +951,30 @@
   const base::DictionaryValue* cookie;
   if (!params.GetDictionary("cookie", &cookie))
     return Status(kUnknownError, "missing 'cookie'");
-  base::ListValue args;
-  args.Append(cookie->CreateDeepCopy());
-  std::unique_ptr<base::Value> result;
-  return web_view->CallFunction(
-      session->GetCurrentFrameId(), kAddCookieScript, args, &result);
+  std::string name;
+  std::string cookie_value;
+  if (!cookie->GetString("name", &name))
+    return Status(kUnknownError, "missing 'name'");
+  if (!cookie->GetString("value", &cookie_value))
+    return Status(kUnknownError, "missing 'value'");
+  std::string url;
+  Status status = GetUrl(web_view, session->GetCurrentFrameId(), &url);
+  if (status.IsError())
+    return status;
+  std::string domain;
+  cookie->GetString("domain", &domain);
+  std::string path;
+  cookie->GetString("path", &path);
+  bool secure = false;
+  cookie->GetBoolean("secure", &secure);
+  bool httpOnly = false;
+  cookie->GetBoolean("httpOnly", &httpOnly);
+  double expiry;
+  if (!cookie->GetDouble("expiry", &expiry))
+    expiry = (base::Time::Now() - base::Time::UnixEpoch()).InSeconds() +
+              kDefaultCookieExpiryTime;
+  return web_view->AddCookie(name, url, cookie_value, domain, path,
+      secure, httpOnly, expiry);
 }
 
 Status ExecuteDeleteCookie(Session* session,
diff --git a/chrome/test/data/webui/BUILD.gn b/chrome/test/data/webui/BUILD.gn
index 17ab3fde..43e9261 100644
--- a/chrome/test/data/webui/BUILD.gn
+++ b/chrome/test/data/webui/BUILD.gn
@@ -151,6 +151,7 @@
     sources += [
       "print_preview/print_preview.js",
       "print_preview/print_preview_destination_search_test.js",
+      "print_preview/print_preview_ui_browsertest.js",
     ]
   }
   deps = [
diff --git a/chrome/test/data/webui/md_user_manager/user_manager_browsertest.js b/chrome/test/data/webui/md_user_manager/user_manager_browsertest.js
index 2ffff2c..e204d7c 100644
--- a/chrome/test/data/webui/md_user_manager/user_manager_browsertest.js
+++ b/chrome/test/data/webui/md_user_manager/user_manager_browsertest.js
@@ -37,7 +37,12 @@
   runAccessibilityChecks: false,
 };
 
-TEST_F('UserManagerBrowserTest', 'UserManagerTest', function() {
+GEN('#if defined(OS_WIN)');
+GEN('#define MAYBE_UserManagerTest DISABLED_UserManagerTest');
+GEN('#else');
+GEN('#define MAYBE_UserManagerTest UserManagerTest');
+GEN('#endif');
+TEST_F('UserManagerBrowserTest', 'MAYBE_UserManagerTest', function() {
   user_manager.control_bar_tests.registerTests();
   user_manager.create_profile_tests.registerTests();
   user_manager.import_supervised_user_tests.registerTests();
diff --git a/chrome/test/data/webui/print_preview/print_preview.js b/chrome/test/data/webui/print_preview/print_preview.js
index 55a52053..3986fba 100644
--- a/chrome/test/data/webui/print_preview/print_preview.js
+++ b/chrome/test/data/webui/print_preview/print_preview.js
@@ -18,27 +18,6 @@
   this.previewArea_ = null;
 }
 
-/**
- * Index of the "Save as PDF" printer.
- * @type {number}
- * @const
- */
-PrintPreviewWebUITest.PDF_INDEX = 0;
-
-/**
- * Index of the Foo printer.
- * @type {number}
- * @const
- */
-PrintPreviewWebUITest.FOO_INDEX = 1;
-
-/**
- * Index of the Bar printer.
- * @type {number}
- * @const
- */
-PrintPreviewWebUITest.BAR_INDEX = 2;
-
 PrintPreviewWebUITest.prototype = {
   __proto__: testing.Test.prototype,
 
@@ -248,96 +227,6 @@
   },
 };
 
-// Test some basic assumptions about the print preview WebUI.
-TEST_F('PrintPreviewWebUITest', 'TestPrinterList', function() {
-  this.setInitialSettings();
-  this.nativeLayer_.whenCalled('getInitialSettings').then(
-      function() {
-        this.setLocalDestinations();
-        var recentList =
-            $('destination-search').querySelector('.recent-list ul');
-        var localList =
-            $('destination-search').querySelector('.local-list ul');
-        assertNotEquals(null, recentList);
-        assertEquals(1, recentList.childNodes.length);
-        assertEquals('FooName',
-                     recentList.childNodes.item(0).querySelector(
-                         '.destination-list-item-name').textContent);
-        assertNotEquals(null, localList);
-        assertEquals(3, localList.childNodes.length);
-        assertEquals('Save as PDF',
-                     localList.childNodes.item(PrintPreviewWebUITest.PDF_INDEX).
-                     querySelector('.destination-list-item-name').textContent);
-        assertEquals('FooName',
-                     localList.childNodes.item(PrintPreviewWebUITest.FOO_INDEX).
-                     querySelector('.destination-list-item-name').textContent);
-        assertEquals('BarName',
-                     localList.childNodes.item(PrintPreviewWebUITest.BAR_INDEX).
-                     querySelector('.destination-list-item-name').textContent);
-        testDone();
-      }.bind(this));
-});
-
-// Test that the printer list is structured correctly after calling
-// addCloudPrinters with an empty list.
-TEST_F('PrintPreviewWebUITest', 'TestPrinterListCloudEmpty', function() {
-  this.setInitialSettings();
-
-  this.nativeLayer_.whenCalled('getInitialSettings').then(
-      function() {
-        this.setLocalDestinations();
-
-        var cloudPrintEnableEvent =
-            new Event(print_preview.NativeLayer.EventType.CLOUD_PRINT_ENABLE);
-        cloudPrintEnableEvent.baseCloudPrintUrl = 'cloudprint url';
-        this.nativeLayer_.getEventTarget().dispatchEvent(
-            cloudPrintEnableEvent);
-
-        var searchDoneEvent =
-            new Event(cloudprint.CloudPrintInterfaceEventType.SEARCH_DONE);
-        searchDoneEvent.printers = [];
-        searchDoneEvent.isRecent = true;
-        searchDoneEvent.email = 'foo@chromium.org';
-        this.printPreview_.cloudPrintInterface_.dispatchEvent(searchDoneEvent);
-
-        var recentList =
-            $('destination-search').querySelector('.recent-list ul');
-        var localList =
-            $('destination-search').querySelector('.local-list ul');
-        var cloudList =
-            $('destination-search').querySelector('.cloud-list ul');
-
-        assertNotEquals(null, recentList);
-        assertEquals(1, recentList.childNodes.length);
-        assertEquals('FooName',
-                     recentList.childNodes.item(0).
-                         querySelector('.destination-list-item-name').
-                         textContent);
-
-        assertNotEquals(null, localList);
-        assertEquals(3, localList.childNodes.length);
-        assertEquals('Save as PDF',
-                     localList.childNodes.item(
-                         PrintPreviewWebUITest.PDF_INDEX).
-                         querySelector('.destination-list-item-name').
-                         textContent);
-        assertEquals('FooName',
-                     localList.childNodes.
-                         item(PrintPreviewWebUITest.FOO_INDEX).
-                         querySelector('.destination-list-item-name').
-                         textContent);
-        assertEquals('BarName',
-                     localList.childNodes.
-                         item(PrintPreviewWebUITest.BAR_INDEX).
-                         querySelector('.destination-list-item-name').
-                         textContent);
-
-        assertNotEquals(null, cloudList);
-        assertEquals(0, cloudList.childNodes.length);
-
-        testDone();
-      }.bind(this));
-});
 
 /**
  * Verify that |section| visibility matches |visible|.
@@ -406,115 +295,6 @@
           loadTimeData.getBoolean('printPdfAsImageEnabled'));
 }
 
-// Test restore settings with one destination.
-TEST_F('PrintPreviewWebUITest', 'TestPrintPreviewRestoreLocalDestination',
-    function() {
-  this.initialSettings_.serializedAppStateStr_ =
-      '{"version":2,"recentDestinations":[{"id":"ID", "origin":"local",' +
-        '"account":"", "capabilities":0, "name":"", "extensionId":"",' +
-            '"extensionName":""}]}';
-
-  this.setInitialSettings();
-  this.nativeLayer_.whenCalled('getInitialSettings').then(
-      function() {
-        testDone();
-      });
-});
-
-// Test with multiple destinations
-TEST_F('PrintPreviewWebUITest', 'TestPrintPreviewRestoreMultipleDestinations',
-    function() {
-  var origin = cr.isChromeOS ? "chrome_os" : "local";
-
-  var appState = {
-    'version': 2,
-    'recentDestinations': [
-      {
-        'id': 'ID1',
-        'origin': origin,
-        'account': '',
-        'capabilities': 0,
-        'name': '',
-        'extensionId': '',
-        'extensionName': ''
-      },
-      {
-        'id': 'ID2',
-        'origin': origin,
-        'account': '',
-        'capabilities': 0,
-        'name': '',
-        'extensionId': '',
-        'extensionName': ''
-      },
-      {
-        'id': 'ID3',
-        'origin': origin,
-        'account': '',
-        'capabilities': 0,
-        'name': '',
-        'extensionId': '',
-        'extensionName': ''
-      }
-    ]
-  };
-
-  this.initialSettings_.serializedAppStateStr_ = JSON.stringify(appState);
-  this.setInitialSettings();
-
-  this.nativeLayer_.whenCalled('getInitialSettings').then(
-      function() {
-        // Set capabilities for the three recently used destinations + 1 more
-        this.setCapabilities(getCddTemplate('ID1'));
-        this.setCapabilities(getCddTemplate('ID2'));
-        this.setCapabilities(getCddTemplate('ID3'));
-        this.setCapabilities(getCddTemplate('ID4'));
-
-        // The most recently used destination should be the currently selected
-        // one. This is ID1.
-        assertEquals(
-            'ID1', this.printPreview_.destinationStore_.selectedDestination.id);
-
-        // Look through the destinations. ID1, ID2, and ID3 should all be
-        // recent.
-        var destinations = this.printPreview_.destinationStore_.destinations_;
-        var idsFound = [];
-
-        for (var i = 0; i < destinations.length; i++) {
-          if (!destinations[i])
-            continue;
-          if (destinations[i].isRecent)
-            idsFound.push(destinations[i].id);
-        }
-
-        // Make sure there were 3 recent destinations and that they are the
-        // correct IDs.
-        assertEquals(3, idsFound.length);
-        assertNotEquals(-1, idsFound.indexOf("ID1"));
-        assertNotEquals(-1, idsFound.indexOf("ID2"));
-        assertNotEquals(-1, idsFound.indexOf("ID3"));
-
-        testDone();
-      }.bind(this));
-});
-
-TEST_F('PrintPreviewWebUITest',
-    'TestPrintPreviewDefaultDestinationSelectionRules', function() {
-  // It also makes sure these rules do override system default destination.
-  this.initialSettings_.serializedDefaultDestinationSelectionRulesStr_ =
-      '{"namePattern":".*Bar.*"}';
-  this.setInitialSettings();
-  this.nativeLayer_.whenCalled('getInitialSettings').then(
-      function() {
-        this.setLocalDestinations();
-
-        assertEquals(
-            'BarDevice',
-            this.printPreview_.destinationStore_.selectedDestination.id);
-
-        testDone();
-      }.bind(this));
-});
 
 TEST_F('PrintPreviewWebUITest', 'TestSystemDialogLinkIsHiddenInAppKioskMode',
     function() {
diff --git a/chrome/test/data/webui/print_preview/print_preview_tests.js b/chrome/test/data/webui/print_preview/print_preview_tests.js
new file mode 100644
index 0000000..19acd3c
--- /dev/null
+++ b/chrome/test/data/webui/print_preview/print_preview_tests.js
@@ -0,0 +1,366 @@
+// 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.
+
+cr.define('print_preview_test', function() {
+  /**
+   * Index of the "Save as PDF" printer.
+   * @type {number}
+   * @const
+   */
+  var PDF_INDEX = 0;
+
+  /**
+   * Index of the Foo printer.
+   * @type {number}
+   * @const
+   */
+  var FOO_INDEX = 1;
+
+  /**
+   * Index of the Bar printer.
+   * @type {number}
+   * @const
+   */
+  var BAR_INDEX = 2;
+
+  var printPreview = null;
+  var nativeLayer = null;
+  var initialSettings = null;
+  var localDestinationInfos = null;
+  var previewArea = null;
+
+  /**
+   * Initialize print preview with the initial settings currently stored in
+   * |initialSettings|. Creates |printPreview| if it does not
+   * already exist.
+   */
+  function setInitialSettings() {
+    nativeLayer.setInitialSettings(initialSettings);
+    printPreview.initialize();
+  }
+
+  /**
+   * Dispatch the LOCAL_DESTINATIONS_SET event. This call is NOT async and will
+   * happen in the same thread.
+   */
+  function setLocalDestinations() {
+    var localDestsSetEvent =
+        new Event(print_preview.NativeLayer.EventType.LOCAL_DESTINATIONS_SET);
+    localDestsSetEvent.destinationInfos = localDestinationInfos;
+    nativeLayer.getEventTarget().dispatchEvent(localDestsSetEvent);
+  }
+
+  /**
+   * Dispatch the CAPABILITIES_SET event. This call is NOT async and will
+   * happen in the same thread.
+   * @param {!Object} device The device whose capabilities should be dispatched.
+   */
+  function setCapabilities(device) {
+    var capsSetEvent =
+        new Event(print_preview.NativeLayer.EventType.CAPABILITIES_SET);
+    capsSetEvent.settingsInfo = device;
+    nativeLayer.getEventTarget().dispatchEvent(capsSetEvent);
+  }
+
+  /**
+   * Verify that |section| visibility matches |visible|.
+   * @param {HTMLDivElement} section The section to check.
+   * @param {boolean} visible The expected state of visibility.
+   */
+  function checkSectionVisible(section, visible) {
+    assertNotEquals(null, section);
+    expectEquals(
+        visible,
+        section.classList.contains('visible'), 'section=' + section.id);
+  }
+
+  /**
+   * @param {string} printerId
+   * @return {!Object}
+   */
+  function getCddTemplate(printerId) {
+    return {
+      printerId: printerId,
+      capabilities: {
+        version: '1.0',
+        printer: {
+          supported_content_type: [{content_type: 'application/pdf'}],
+          collate: {},
+          color: {
+            option: [
+              {type: 'STANDARD_COLOR', is_default: true},
+              {type: 'STANDARD_MONOCHROME'}
+            ]
+          },
+          copies: {},
+          duplex: {
+            option: [
+              {type: 'NO_DUPLEX', is_default: true},
+              {type: 'LONG_EDGE'},
+              {type: 'SHORT_EDGE'}
+            ]
+          },
+          page_orientation: {
+            option: [
+              {type: 'PORTRAIT', is_default: true},
+              {type: 'LANDSCAPE'},
+              {type: 'AUTO'}
+            ]
+          },
+          media_size: {
+            option: [
+              { name: 'NA_LETTER',
+                width_microns: 215900,
+                height_microns: 279400,
+                is_default: true
+              }
+            ]
+          }
+        }
+      }
+    };
+  }
+
+  suite('PrintPreview', function() {
+    suiteSetup(function() {
+      function CloudPrintInterfaceStub() {
+        cr.EventTarget.call(this);
+      }
+      CloudPrintInterfaceStub.prototype = {
+        __proto__: cr.EventTarget.prototype,
+        search: function(isRecent) {}
+      };
+      var oldCpInterfaceEventType = cloudprint.CloudPrintInterfaceEventType;
+      cloudprint.CloudPrintInterface = CloudPrintInterfaceStub;
+      cloudprint.CloudPrintInterfaceEventType = oldCpInterfaceEventType;
+
+      print_preview.PreviewArea.prototype.checkPluginCompatibility_ =
+          function() {
+        return false;
+      };
+    });
+
+    setup(function() {
+      initialSettings = new print_preview.NativeInitialSettings(
+        false /*isInKioskAutoPrintMode*/,
+        false /*isInAppKioskMode*/,
+        ',' /*thousandsDelimeter*/,
+        '.' /*decimalDelimeter*/,
+        1 /*unitType*/,
+        true /*isDocumentModifiable*/,
+        'title' /*documentTitle*/,
+        true /*documentHasSelection*/,
+        false /*selectionOnly*/,
+        'FooDevice' /*systemDefaultDestinationId*/,
+        null /*serializedAppStateStr*/,
+        null /*serializedDefaultDestinationSelectionRulesStr*/);
+
+      localDestinationInfos = [
+        { printerName: 'FooName', deviceName: 'FooDevice' },
+        { printerName: 'BarName', deviceName: 'BarDevice' },
+      ];
+
+      nativeLayer = new print_preview.NativeLayerStub();
+      print_preview.NativeLayer.setInstance(nativeLayer);
+      printPreview = new print_preview.PrintPreview();
+      previewArea = printPreview.getPreviewArea();
+    });
+
+    // Test some basic assumptions about the print preview WebUI.
+    test('PrinterList', function() {
+      setInitialSettings();
+      return nativeLayer.whenCalled('getInitialSettings').then(
+          function() {
+            setLocalDestinations();
+            var recentList =
+                $('destination-search').querySelector('.recent-list ul');
+            var localList =
+                $('destination-search').querySelector('.local-list ul');
+            assertNotEquals(null, recentList);
+            assertEquals(1, recentList.childNodes.length);
+            assertEquals('FooName',
+                         recentList.childNodes.item(0).querySelector(
+                             '.destination-list-item-name').textContent);
+            assertNotEquals(null, localList);
+            assertEquals(3, localList.childNodes.length);
+            assertEquals(
+                'Save as PDF',
+                localList.childNodes.item(PDF_INDEX).
+                querySelector('.destination-list-item-name').textContent);
+            assertEquals(
+                'FooName',
+                localList.childNodes.item(FOO_INDEX).
+                querySelector('.destination-list-item-name').textContent);
+            assertEquals(
+                'BarName',
+                localList.childNodes.item(BAR_INDEX).
+                querySelector('.destination-list-item-name').textContent);
+          });
+    });
+
+    // Test that the printer list is structured correctly after calling
+    // addCloudPrinters with an empty list.
+    test('PrinterListCloudEmpty', function() {
+      setInitialSettings();
+
+      return nativeLayer.whenCalled('getInitialSettings').then(
+          function() {
+            setLocalDestinations();
+
+            var cloudPrintEnableEvent = new Event(
+                print_preview.NativeLayer.EventType.CLOUD_PRINT_ENABLE);
+            cloudPrintEnableEvent.baseCloudPrintUrl = 'cloudprint url';
+            nativeLayer.getEventTarget().dispatchEvent(
+                cloudPrintEnableEvent);
+
+            var searchDoneEvent =
+                new Event(cloudprint.CloudPrintInterfaceEventType.SEARCH_DONE);
+            searchDoneEvent.printers = [];
+            searchDoneEvent.isRecent = true;
+            searchDoneEvent.email = 'foo@chromium.org';
+            printPreview.cloudPrintInterface_.dispatchEvent(searchDoneEvent);
+
+            var recentList =
+                $('destination-search').querySelector('.recent-list ul');
+            var localList =
+                $('destination-search').querySelector('.local-list ul');
+            var cloudList =
+                $('destination-search').querySelector('.cloud-list ul');
+
+            assertNotEquals(null, recentList);
+            assertEquals(1, recentList.childNodes.length);
+            assertEquals('FooName',
+                         recentList.childNodes.item(0).
+                             querySelector('.destination-list-item-name').
+                             textContent);
+
+            assertNotEquals(null, localList);
+            assertEquals(3, localList.childNodes.length);
+            assertEquals('Save as PDF',
+                         localList.childNodes.item(PDF_INDEX).
+                             querySelector('.destination-list-item-name').
+                             textContent);
+            assertEquals('FooName',
+                         localList.childNodes.
+                             item(FOO_INDEX).
+                             querySelector('.destination-list-item-name').
+                             textContent);
+            assertEquals('BarName',
+                         localList.childNodes.
+                             item(BAR_INDEX).
+                             querySelector('.destination-list-item-name').
+                             textContent);
+
+            assertNotEquals(null, cloudList);
+            assertEquals(0, cloudList.childNodes.length);
+          });
+    });
+
+    // Test restore settings with one destination.
+    test('RestoreLocalDestination', function() {
+      initialSettings.serializedAppStateStr_ = JSON.stringify({
+        version: 2,
+        recentDestinations: [
+          {
+            id: 'ID',
+            origin: cr.isChromeOS ? 'chrome_os' : 'local',
+            account: '',
+            capabilities: 0,
+            name: '',
+            extensionId: '',
+            extensionName: '',
+          },
+        ],
+      });
+
+      setInitialSettings();
+      return nativeLayer.whenCalled('getInitialSettings');
+    });
+
+    test('RestoreMultipleDestinations', function() {
+      var origin = cr.isChromeOS ? 'chrome_os' : 'local';
+
+      initialSettings.serializedAppStateStr_ = JSON.stringify({
+        version: 2,
+        recentDestinations: [
+          {
+            id: 'ID1',
+            origin: origin,
+            account: '',
+            capabilities: 0,
+            name: '',
+            extensionId: '',
+            extensionName: '',
+          }, {
+            id: 'ID2',
+            origin: origin,
+            account: '',
+            capabilities: 0,
+            name: '',
+            extensionId: '',
+            extensionName: '',
+          }, {
+            id: 'ID3',
+            origin: origin,
+            account: '',
+            capabilities: 0,
+            name: '',
+            extensionId: '',
+            extensionName: '',
+          },
+        ],
+      });
+
+      setInitialSettings();
+
+      return nativeLayer.whenCalled('getInitialSettings').then(
+          function() {
+            // Set capabilities for the three recently used destinations + 1
+            // more.
+            setCapabilities(getCddTemplate('ID1'));
+            setCapabilities(getCddTemplate('ID2'));
+            setCapabilities(getCddTemplate('ID3'));
+            setCapabilities(getCddTemplate('ID4'));
+
+            // The most recently used destination should be the currently
+            // selected one. This is ID1.
+            assertEquals(
+                'ID1', printPreview.destinationStore_.selectedDestination.id);
+
+            // Look through the destinations. ID1, ID2, and ID3 should all be
+            // recent.
+            var destinations = printPreview.destinationStore_.destinations_;
+            var idsFound = [];
+
+            for (var i = 0; i < destinations.length; i++) {
+              if (!destinations[i])
+                continue;
+              if (destinations[i].isRecent)
+                idsFound.push(destinations[i].id);
+            }
+
+            // Make sure there were 3 recent destinations and that they are the
+            // correct IDs.
+            assertEquals(3, idsFound.length);
+            assertNotEquals(-1, idsFound.indexOf('ID1'));
+            assertNotEquals(-1, idsFound.indexOf('ID2'));
+            assertNotEquals(-1, idsFound.indexOf('ID3'));
+          });
+    });
+
+    test('DefaultDestinationSelectionRules', function() {
+      // It also makes sure these rules do override system default destination.
+      initialSettings.serializedDefaultDestinationSelectionRulesStr_ =
+          JSON.stringify({namePattern: '.*Bar.*'});
+      setInitialSettings();
+      return nativeLayer.whenCalled('getInitialSettings').then(
+          function() {
+            setLocalDestinations();
+            assertEquals(
+                'BarDevice',
+                printPreview.destinationStore_.selectedDestination.id);
+          });
+        });
+  });
+});
diff --git a/chrome/test/data/webui/print_preview/print_preview_ui_browsertest.js b/chrome/test/data/webui/print_preview/print_preview_ui_browsertest.js
new file mode 100644
index 0000000..3b678f60
--- /dev/null
+++ b/chrome/test/data/webui/print_preview/print_preview_ui_browsertest.js
@@ -0,0 +1,83 @@
+// 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.
+
+/** @fileoverview Runs the Print Preview tests. */
+
+var ROOT_PATH = '../../../../../';
+
+/**
+ * @constructor
+ * @extends {testing.Test}
+ */
+function PrintPreviewUIBrowserTest() {}
+
+PrintPreviewUIBrowserTest.prototype = {
+  __proto__: testing.Test.prototype,
+
+  /**
+   * Browse to the sample page, cause print preview & call preLoad().
+   * @override
+   */
+  browsePrintPreload: 'print_preview/print_preview_hello_world_test.html',
+
+  /** @override */
+  runAccessibilityChecks: true,
+
+  /** @override */
+  accessibilityIssuesAreErrors: false,
+
+  /** @override */
+  isAsync: true,
+
+  /** @override */
+  preLoad: function() {
+    window.isTest = true;
+    testing.Test.prototype.preLoad.call(this);
+
+  },
+
+  /** @override */
+  setUp: function() {
+    testing.Test.prototype.setUp.call(this);
+
+    testing.Test.disableAnimationsAndTransitions();
+    // Enable when failure is resolved.
+    // AX_TEXT_03: http://crbug.com/559209
+    this.accessibilityAuditConfig.ignoreSelectors(
+        'multipleLabelableElementsPerLabel',
+        '#page-settings > .right-column > *');
+  },
+
+  extraLibraries: [
+    ROOT_PATH + 'ui/webui/resources/js/cr.js',
+    ROOT_PATH + 'ui/webui/resources/js/promise_resolver.js',
+    ROOT_PATH + 'third_party/mocha/mocha.js',
+    ROOT_PATH + 'chrome/test/data/webui/mocha_adapter.js',
+    ROOT_PATH + 'ui/webui/resources/js/util.js',
+    ROOT_PATH + 'chrome/test/data/webui/settings/test_browser_proxy.js',
+    'print_preview_tests.js',
+    'native_layer_stub.js',
+  ],
+};
+
+TEST_F('PrintPreviewUIBrowserTest', 'PrinterList', function() {
+  mocha.grep(/PrinterList\b/).run();
+});
+
+TEST_F('PrintPreviewUIBrowserTest', 'PrinterListCloudEmpty', function() {
+  mocha.grep(/PrinterListCloudEmpty\b/).run();
+});
+
+TEST_F('PrintPreviewUIBrowserTest', 'RestoreLocalDestination', function() {
+  mocha.grep(/RestoreLocalDestination\b/).run();
+});
+
+TEST_F('PrintPreviewUIBrowserTest', 'RestoreMultipleDestinations', function() {
+  mocha.grep(/RestoreMultipleDestinations\b/).run();
+});
+
+TEST_F('PrintPreviewUIBrowserTest', 'DefaultDestinationSelectionRules',
+    function() {
+      mocha.grep(/DefaultDestinationSelectionRules\b/).run();
+    });
diff --git a/chromecast/base/BUILD.gn b/chromecast/base/BUILD.gn
index a4de4ee..2931dbd 100644
--- a/chromecast/base/BUILD.gn
+++ b/chromecast/base/BUILD.gn
@@ -51,6 +51,7 @@
     "error_codes.h",
     "file_utils.cc",
     "file_utils.h",
+    "observer.h",
     "path_utils.cc",
     "path_utils.h",
     "pref_names.cc",
@@ -128,6 +129,7 @@
     "bind_to_task_runner_unittest.cc",
     "device_capabilities_impl_unittest.cc",
     "error_codes_unittest.cc",
+    "observer_unittest.cc",
     "path_utils_unittest.cc",
     "process_utils_unittest.cc",
     "serializers_unittest.cc",
diff --git a/chromecast/base/chromecast_switches.cc b/chromecast/base/chromecast_switches.cc
index 9a94435..ce23b09 100644
--- a/chromecast/base/chromecast_switches.cc
+++ b/chromecast/base/chromecast_switches.cc
@@ -91,6 +91,11 @@
 // Calibrated max output volume dBa for voice content at 1 meter, if known.
 const char kMaxOutputVolumeDba1m[] = "max-output-volume-dba1m";
 
+// Number of audio output channels. This will be used to send audio buffer with
+// specific number of channels to ALSA and generate loopback audio. Default
+// value is 2.
+const char kAudioOutputChannels[] = "audio-output-channels";
+
 // Some platforms typically have very little 'free' memory, but plenty is
 // available in buffers+cached.  For such platforms, configure this amount
 // as the portion of buffers+cached memory that should be treated as
diff --git a/chromecast/base/chromecast_switches.h b/chromecast/base/chromecast_switches.h
index cee08a1..298452e 100644
--- a/chromecast/base/chromecast_switches.h
+++ b/chromecast/base/chromecast_switches.h
@@ -50,6 +50,7 @@
 extern const char kAlsaMuteDeviceName[];
 extern const char kAlsaMuteElementName[];
 extern const char kMaxOutputVolumeDba1m[];
+extern const char kAudioOutputChannels[];
 
 // Memory pressure switches
 extern const char kMemPressureSystemReservedKb[];
diff --git a/chromecast/base/observer.h b/chromecast/base/observer.h
new file mode 100644
index 0000000..a1b6d48
--- /dev/null
+++ b/chromecast/base/observer.h
@@ -0,0 +1,429 @@
+// 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.
+
+// Safe Observer/Observable implementation.
+//
+// When using ObserverListThreadSafe, we were running into issues since there
+// was no synchronization between getting the existing value, and registering
+// as an observer. See go/cast-platform-design-synchronized-value for more
+// details.
+//
+// To fix this issue, and to make observing values safer and simpler in general,
+// use the Observable/Observer pattern in this file. When you have a value that
+// other code wants to observe (ie, get the value of and receive any changes),
+// wrap that value in an Observable<T>. The value type T must be copyable and
+// copy-assignable. The Observable must be constructed with the initial observed
+// value, and the value may be updated at any time from any thread by calling
+// SetValue(). You can also get the value using GetValue(); but note that this
+// is not threadsafe (the value is returned without locking), so the caller must
+// ensure safety by other means. Calling GetValueThreadSafe() is threadsafe but
+// involves a mutex lock.
+//
+// Code that wants to observe the value calls Observe() on it at any point when
+// the value is alive. Note that Observe() may be called safely from any thread.
+// Observe() returns an Observer<T> instance, which MUST be used and destroyed
+// only on the thread that called Observe(). The Observer initially contains the
+// value that the Observable had when Observe() was called, and that value will
+// be updated asynchronously whenever the Observable's SetValue() method is
+// is called. NOTE: the initial value of the Observer is the value known to the
+// thread that created the Observer at the time; there may be an updated value
+// from the Observable that hasn't been handled by the Observer's thread yet.
+//
+// The Observer's view of the observed value is returned by GetValue(); this is
+// a low-cost call since there is no locking (the value is updated on the thread
+// that constructed the Observer). Note that Observers are always updated
+// asynchronously with PostTask(), even if they belong to the same thread that
+// calls SetValue(). All Observers on the same thread have the same consistent
+// view of the observed value.
+//
+// Observers may be copied freely; the copy also observes the original
+// Observable, and belongs to the thread that created the copy. Copying is safe
+// even when the original Observable has been destroyed.
+//
+// Code may register a callback that is called whenever an Observer's value is
+// updated, by calling SetOnUpdateCallback(). If you get an Observer by calling
+// Observe() and then immediately call SetOnUpdateCallback() to register a
+// a callback, you are guaranteed to get every value of the Observable starting
+// from when you called Observe() - you get the initial value by calling
+// GetValue() on the returned Observer, and any subsequent updates will trigger
+// the callback so you can call GetValue() to get the new value. You will not
+// receive any extra callbacks (exactly one callback per value update).
+//
+// Note that Observers are not default-constructible, since there is no way to
+// construct it in a default state. In cases where you need to instantiate an
+// Observer after your constructor, you can use a std::unique_ptr<Observer>
+// instead, and initialize it when needed.
+//
+// Example usage:
+//
+// class MediaManager {
+//  public:
+//   MediaManager() : volume_(0.0f) {}
+//
+//   Observer<float> ObserveVolume() { return volume_.Observe(); }
+//
+//   // ... other methods ...
+//
+//  private:
+//   // Assume this is called from some other internal code when the volume is
+//   // updated.
+//   void OnUpdateVolume(float new_volume) {
+//     volume_.SetValue(new_volume);  // All observers will get the new value.
+//   }
+//
+//   Observable<float> volume_;
+// }
+//
+// class VolumeFeedbackManager {
+//  public:
+//   VolumeFeedbackManager(MediaManager* media_manager)
+//       : volume_observer_(media_manager->ObserveVolume()) {
+//     volume_observer_.SetOnUpdateCallback(
+//         base::BindRepeating(&VolumeFeedbackManager::OnVolumeChanged,
+//                             base::Unretained(this)));
+//   }
+//
+//  private:
+//   void OnVolumeChanged() {
+//     ShowVolumeFeedback(volume_observer_.GetValue());
+//   }
+//
+//   void ShowVolumeFeedback(float volume) {
+//     // ... some implementation ...
+//   }
+// };
+//
+
+#ifndef CHROMECAST_BASE_OBSERVER_H_
+#define CHROMECAST_BASE_OBSERVER_H_
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <memory>
+#include <utility>
+#include <vector>
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/location.h"
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/memory/ptr_util.h"
+#include "base/memory/ref_counted.h"
+#include "base/sequence_checker.h"
+#include "base/sequenced_task_runner.h"
+#include "base/synchronization/lock.h"
+#include "base/threading/sequenced_task_runner_handle.h"
+
+namespace chromecast {
+
+namespace subtle {
+template <typename T>
+class ObservableInternals;
+}  // namespace subtle
+
+template <typename T>
+class Observable;
+
+template <typename T>
+class Observer {
+ public:
+  Observer(const Observer& other);
+
+  ~Observer();
+
+  void SetOnUpdateCallback(base::RepeatingClosure callback) {
+    on_update_callback_ = std::move(callback);
+  }
+
+  const T& GetValue() const {
+    DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+    return value_;
+  }
+
+ private:
+  friend class subtle::ObservableInternals<T>;
+  friend class Observable<T>;
+
+  explicit Observer(scoped_refptr<subtle::ObservableInternals<T>> internals);
+
+  void OnUpdate();
+
+  const scoped_refptr<subtle::ObservableInternals<T>> internals_;
+  // Note: value_ is a const ref to the value copy for this sequence, stored in
+  // SequenceOwnedInfo.
+  const T& value_;
+  base::RepeatingClosure on_update_callback_;
+  SEQUENCE_CHECKER(sequence_checker_);
+
+  DISALLOW_ASSIGN(Observer);
+};
+
+template <typename T>
+class Observable {
+  static_assert(std::is_copy_constructible<T>::value,
+                "Observable values must be copyable");
+  static_assert(std::is_copy_assignable<T>::value,
+                "Observable values must be copy-assignable");
+
+ public:
+  explicit Observable(const T& initial_value);
+  Observer<T> Observe();
+
+  void SetValue(const T& new_value);
+  const T& GetValue() const;  // NOT threadsafe!
+  T GetValueThreadSafe() const;
+
+ private:
+  // By using a refcounted object to store the value and observer list, we can
+  // avoid tying the lifetime of Observable to its Observers or vice versa.
+  const scoped_refptr<subtle::ObservableInternals<T>> internals_;
+
+  DISALLOW_COPY_AND_ASSIGN(Observable);
+};
+
+namespace subtle {
+
+template <typename T>
+class ObservableInternals
+    : public base::RefCountedThreadSafe<ObservableInternals<T>> {
+ public:
+  explicit ObservableInternals(const T& initial_value)
+      : value_(initial_value) {}
+
+  void SetValue(const T& new_value) {
+    base::AutoLock lock(lock_);
+    value_ = new_value;
+
+    for (auto& item : per_sequence_) {
+      item.SetValue(new_value);
+    }
+  }
+
+  const T& GetValue() const { return value_; }
+
+  T GetValueThreadSafe() const {
+    base::AutoLock lock(lock_);
+    return value_;
+  }
+
+  const T& AddObserver(Observer<T>* observer) {
+    DCHECK(observer);
+    DCHECK(base::SequencedTaskRunnerHandle::IsSet());
+    auto task_runner = base::SequencedTaskRunnerHandle::Get();
+
+    base::AutoLock lock(lock_);
+    auto it = per_sequence_.begin();
+    while (it != per_sequence_.end() && it->task_runner() != task_runner) {
+      ++it;
+    }
+    if (it == per_sequence_.end()) {
+      per_sequence_.emplace_back(std::move(task_runner), value_);
+      it = --per_sequence_.end();
+    }
+    it->AddObserver(observer);
+    return it->value();
+  }
+
+  void RemoveObserver(Observer<T>* observer) {
+    DCHECK(observer);
+    DCHECK(base::SequencedTaskRunnerHandle::IsSet());
+    auto task_runner = base::SequencedTaskRunnerHandle::Get();
+
+    base::AutoLock lock(lock_);
+    for (size_t i = 0; i < per_sequence_.size(); ++i) {
+      if (per_sequence_[i].task_runner() == task_runner) {
+        per_sequence_[i].RemoveObserver(observer);
+
+        if (per_sequence_[i].Empty()) {
+          per_sequence_[i].Swap(per_sequence_.back());
+          per_sequence_.pop_back();
+        }
+        return;
+      }
+    }
+
+    NOTREACHED() << "Tried to remove observer from unknown task runner";
+  }
+
+ private:
+  // Information owned by a particular sequence. Must be only accessed on that
+  // sequence, and must be deleted by posting a task to that sequence.
+  // This class MUST NOT contain a scoped_refptr to the task_runner, since if it
+  // did, there would be a reference cycle during cleanup, when the task to
+  // Destroy() is posted.
+  class SequenceOwnedInfo {
+   public:
+    SequenceOwnedInfo(const T& value) : value_(value) {}
+
+    const T& value() const {
+      DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+      return value_;
+    }
+
+    void AddObserver(Observer<T>* observer) {
+      DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+      DCHECK(observer);
+      DCHECK(std::find(observers_.begin(), observers_.end(), observer) ==
+             observers_.end());
+      observers_.push_back(observer);
+    }
+
+    void RemoveObserver(Observer<T>* observer) {
+      DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+      DCHECK(observer);
+      DCHECK(std::find(observers_.begin(), observers_.end(), observer) !=
+             observers_.end());
+      observers_.erase(
+          std::remove(observers_.begin(), observers_.end(), observer),
+          observers_.end());
+    }
+
+    bool Empty() const {
+      DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+      return observers_.empty();
+    }
+
+    void SetValue(const T& value) {
+      DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+      value_ = value;
+      for (auto* obs : observers_) {
+        obs->OnUpdate();
+      }
+    }
+
+    static void Destroy(std::unique_ptr<SequenceOwnedInfo> self) {
+      // The unique_ptr deletes automatically.
+    }
+
+   private:
+    std::vector<Observer<T>*> observers_;
+    T value_;
+    SEQUENCE_CHECKER(sequence_checker_);
+
+    DISALLOW_COPY_AND_ASSIGN(SequenceOwnedInfo);
+  };
+
+  class PerSequenceInfo {
+   public:
+    PerSequenceInfo(scoped_refptr<base::SequencedTaskRunner> task_runner,
+                    const T& value)
+        : task_runner_(std::move(task_runner)),
+          owned_info_(base::MakeUnique<SequenceOwnedInfo>(value)) {}
+
+    PerSequenceInfo(PerSequenceInfo&& other) = default;
+
+    ~PerSequenceInfo() {
+      if (!owned_info_) {
+        // Members have been moved out via move constructor.
+        return;
+      }
+
+      DCHECK(Empty());
+      // Must post a task to delete the owned info, since there may still be a
+      // pending task to call SequenceOwnedInfo::SetValue().
+      // Use manual PostNonNestableTask(), since DeleteSoon() does not
+      // guarantee deletion.
+      task_runner_->PostNonNestableTask(
+          FROM_HERE,
+          base::BindOnce(&SequenceOwnedInfo::Destroy, std::move(owned_info_)));
+    }
+
+    const T& value() const { return owned_info_->value(); }
+
+    const base::SequencedTaskRunner* task_runner() const {
+      return task_runner_.get();
+    }
+
+    void AddObserver(Observer<T>* observer) {
+      owned_info_->AddObserver(observer);
+    }
+
+    void RemoveObserver(Observer<T>* observer) {
+      owned_info_->RemoveObserver(observer);
+    }
+
+    bool Empty() const { return owned_info_->Empty(); }
+
+    void Swap(PerSequenceInfo& other) {
+      std::swap(task_runner_, other.task_runner_);
+      std::swap(owned_info_, other.owned_info_);
+    }
+
+    void SetValue(const T& value) {
+      task_runner_->PostTask(
+          FROM_HERE,
+          base::BindOnce(&SequenceOwnedInfo::SetValue,
+                         base::Unretained(owned_info_.get()), value));
+    }
+
+   private:
+    // Operations on |owned_info| do not need to be synchronized with a lock,
+    // since all operations must occur on |task_runner|.
+    scoped_refptr<base::SequencedTaskRunner> task_runner_;
+    std::unique_ptr<SequenceOwnedInfo> owned_info_;
+  };
+
+  friend class base::RefCountedThreadSafe<ObservableInternals>;
+  ~ObservableInternals() {}
+
+  mutable base::Lock lock_;
+  T value_;
+  std::vector<PerSequenceInfo> per_sequence_;
+
+  DISALLOW_COPY_AND_ASSIGN(ObservableInternals);
+};
+
+}  // namespace subtle
+
+template <typename T>
+Observer<T>::Observer(scoped_refptr<subtle::ObservableInternals<T>> internals)
+    : internals_(std::move(internals)), value_(internals_->AddObserver(this)) {}
+
+template <typename T>
+Observer<T>::Observer(const Observer& other) : Observer(other.internals_) {}
+
+template <typename T>
+Observer<T>::~Observer() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  internals_->RemoveObserver(this);
+}
+
+template <typename T>
+void Observer<T>::OnUpdate() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  if (on_update_callback_) {
+    on_update_callback_.Run();
+  }
+}
+
+template <typename T>
+Observable<T>::Observable(const T& initial_value)
+    : internals_(make_scoped_refptr(
+          new subtle::ObservableInternals<T>(initial_value))) {}
+
+template <typename T>
+Observer<T> Observable<T>::Observe() {
+  return Observer<T>(internals_);
+}
+
+template <typename T>
+void Observable<T>::SetValue(const T& new_value) {
+  internals_->SetValue(new_value);
+}
+
+template <typename T>
+const T& Observable<T>::GetValue() const {
+  return internals_->GetValue();
+}
+
+template <typename T>
+T Observable<T>::GetValueThreadSafe() const {
+  return internals_->GetValueThreadSafe();
+}
+
+}  // namespace chromecast
+
+#endif  // CHROMECAST_BASE_OBSERVER_H_
diff --git a/chromecast/base/observer_unittest.cc b/chromecast/base/observer_unittest.cc
new file mode 100644
index 0000000..87a6fafd
--- /dev/null
+++ b/chromecast/base/observer_unittest.cc
@@ -0,0 +1,354 @@
+// 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 <functional>
+#include <memory>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/location.h"
+#include "base/macros.h"
+#include "base/memory/ptr_util.h"
+#include "base/message_loop/message_loop.h"
+#include "base/run_loop.h"
+#include "base/synchronization/waitable_event.h"
+#include "base/threading/thread.h"
+#include "chromecast/base/observer.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace chromecast {
+
+class ObserverTest : public ::testing::Test {
+ protected:
+  ObserverTest() : message_loop_(base::MakeUnique<base::MessageLoop>()) {}
+
+  const std::unique_ptr<base::MessageLoop> message_loop_;
+};
+
+struct NoDefaultConstructor {
+  NoDefaultConstructor(int v) : value(v) {}
+
+  int value;
+};
+
+class ThreadedObservable {
+ public:
+  ThreadedObservable() : thread_("ThreadedObservable"), value_(0) {
+    thread_.Start();
+  }
+
+  Observer<int> Observe() { return value_.Observe(); }
+
+  void SetValue(int value) {
+    thread_.task_runner()->PostTask(
+        FROM_HERE, base::BindOnce(&ThreadedObservable::SetValueOnThread,
+                                  base::Unretained(this), value));
+  }
+
+ private:
+  void SetValueOnThread(int value) {
+    DCHECK(thread_.task_runner()->BelongsToCurrentThread());
+    value_.SetValue(value);
+  }
+
+  base::Thread thread_;
+  Observable<int> value_;
+
+  DISALLOW_COPY_AND_ASSIGN(ThreadedObservable);
+};
+
+class ThreadedObserver {
+ public:
+  ThreadedObserver()
+      : thread_("ThreadedObserver"),
+        observing_(base::WaitableEvent::ResetPolicy::MANUAL,
+                   base::WaitableEvent::InitialState::NOT_SIGNALED) {
+    thread_.Start();
+  }
+
+  ~ThreadedObserver() {
+    thread_.task_runner()->PostTask(
+        FROM_HERE, base::BindOnce(&ThreadedObserver::DestroyOnThread,
+                                  base::Unretained(this)));
+    thread_.Stop();
+  }
+
+  void Observe(Observable<int>* observable) {
+    thread_.task_runner()->PostTask(
+        FROM_HERE, base::BindOnce(&ThreadedObserver::ObserveOnThread,
+                                  base::Unretained(this), observable));
+    observing_.Wait();
+  }
+
+  void CheckValue(int value) {
+    thread_.task_runner()->PostTask(
+        FROM_HERE, base::BindOnce(&ThreadedObserver::CheckValueOnThread,
+                                  base::Unretained(this), value));
+  }
+
+ private:
+  void ObserveOnThread(Observable<int>* observable) {
+    DCHECK(thread_.task_runner()->BelongsToCurrentThread());
+    observer_ = base::MakeUnique<Observer<int>>(observable->Observe());
+    observing_.Signal();
+  }
+
+  void CheckValueOnThread(int value) {
+    DCHECK(thread_.task_runner()->BelongsToCurrentThread());
+    EXPECT_EQ(value, observer_->GetValue());
+  }
+
+  void DestroyOnThread() {
+    DCHECK(thread_.task_runner()->BelongsToCurrentThread());
+    observer_.reset();
+  }
+
+  base::Thread thread_;
+  std::unique_ptr<Observer<int>> observer_;
+  base::WaitableEvent observing_;
+
+  DISALLOW_COPY_AND_ASSIGN(ThreadedObserver);
+};
+
+void RunCallback(std::function<void()> callback) {
+  callback();
+}
+
+TEST_F(ObserverTest, SimpleValue) {
+  Observable<int> original(0);
+  Observer<int> observer = original.Observe();
+
+  EXPECT_EQ(0, observer.GetValue());
+
+  original.SetValue(1);
+  base::RunLoop().RunUntilIdle();
+  EXPECT_EQ(1, observer.GetValue());
+}
+
+TEST_F(ObserverTest, MultipleObservers) {
+  Observable<int> original(0);
+  Observer<int> observer1 = original.Observe();
+  Observer<int> observer2 = observer1;
+
+  EXPECT_EQ(0, observer1.GetValue());
+  EXPECT_EQ(0, observer2.GetValue());
+
+  original.SetValue(1);
+  base::RunLoop().RunUntilIdle();
+  EXPECT_EQ(1, observer1.GetValue());
+  EXPECT_EQ(1, observer2.GetValue());
+}
+
+TEST_F(ObserverTest, NoDefaultConstructor) {
+  Observable<NoDefaultConstructor> original(0);
+  Observer<NoDefaultConstructor> observer = original.Observe();
+
+  EXPECT_EQ(0, observer.GetValue().value);
+
+  original.SetValue(1);
+  base::RunLoop().RunUntilIdle();
+  EXPECT_EQ(1, observer.GetValue().value);
+}
+
+TEST_F(ObserverTest, NoMissingEvents) {
+  Observable<int> original(0);
+  Observer<int> observer = original.Observe();
+  original.SetValue(1);
+
+  std::vector<int> event_values;
+  std::function<void()> callback = [&]() {
+    event_values.push_back(observer.GetValue());
+  };
+  observer.SetOnUpdateCallback(base::BindRepeating(&RunCallback, callback));
+
+  EXPECT_EQ(0, observer.GetValue());
+
+  original.SetValue(2);
+  base::RunLoop().RunUntilIdle();
+  original.SetValue(3);
+  original.SetValue(4);
+  base::RunLoop().RunUntilIdle();
+
+  ASSERT_EQ(4u, event_values.size());
+  EXPECT_EQ(1, event_values[0]);
+  EXPECT_EQ(2, event_values[1]);
+  EXPECT_EQ(3, event_values[2]);
+  EXPECT_EQ(4, event_values[3]);
+
+  EXPECT_EQ(4, observer.GetValue());
+}
+
+TEST_F(ObserverTest, NoExtraEventsAfterChange) {
+  Observable<int> original(0);
+  original.SetValue(1);
+
+  Observer<int> observer = original.Observe();
+  EXPECT_EQ(1, observer.GetValue());
+
+  std::vector<int> event_values;
+  std::function<void()> callback = [&]() {
+    event_values.push_back(observer.GetValue());
+  };
+  observer.SetOnUpdateCallback(base::BindRepeating(&RunCallback, callback));
+
+  // Propagate the SetValue event; the observer shouldn't get it since it
+  // started observing after SetValue().
+  base::RunLoop().RunUntilIdle();
+  EXPECT_EQ(1, observer.GetValue());
+  EXPECT_EQ(0u, event_values.size());
+}
+
+TEST_F(ObserverTest, NoExtraEventsBetweenChanges) {
+  Observable<int> original(0);
+  original.SetValue(1);
+
+  Observer<int> observer = original.Observe();
+  EXPECT_EQ(1, observer.GetValue());
+
+  original.SetValue(2);
+
+  std::vector<int> event_values;
+  std::function<void()> callback = [&]() {
+    event_values.push_back(observer.GetValue());
+  };
+  observer.SetOnUpdateCallback(base::BindRepeating(&RunCallback, callback));
+
+  // Propagate the SetValue events; the observer should only get the second
+  // event, corresponding to the SetValue after the observer was created.
+  base::RunLoop().RunUntilIdle();
+  EXPECT_EQ(2, observer.GetValue());
+  ASSERT_EQ(1u, event_values.size());
+  EXPECT_EQ(2, event_values[0]);
+}
+
+TEST_F(ObserverTest, NoExtraEventsForCopy) {
+  Observable<int> original(0);
+  original.SetValue(1);
+
+  Observer<int> observer1 = original.Observe();
+  EXPECT_EQ(1, observer1.GetValue());
+
+  original.SetValue(2);
+
+  Observer<int> observer2 = observer1;
+  // All observers on the same thread observe the same value. The update hasn't
+  // propagated yet.
+  EXPECT_EQ(1, observer2.GetValue());
+
+  std::vector<int> event_values1;
+  std::function<void()> callback1 = [&]() {
+    event_values1.push_back(observer1.GetValue());
+  };
+  observer1.SetOnUpdateCallback(base::BindRepeating(&RunCallback, callback1));
+
+  std::vector<int> event_values2;
+  std::function<void()> callback2 = [&]() {
+    event_values2.push_back(observer2.GetValue());
+  };
+  observer2.SetOnUpdateCallback(base::BindRepeating(&RunCallback, callback2));
+
+  // Propagate the SetValue events; each observer should get just one callback
+  // for the new value.
+  base::RunLoop().RunUntilIdle();
+  EXPECT_EQ(2, observer1.GetValue());
+  EXPECT_EQ(2, observer2.GetValue());
+
+  ASSERT_EQ(1u, event_values1.size());
+  EXPECT_EQ(2, event_values1[0]);
+
+  ASSERT_EQ(1u, event_values2.size());
+  EXPECT_EQ(2, event_values2[0]);
+}
+
+TEST_F(ObserverTest, SetCallbackTwice) {
+  Observable<int> original(0);
+  original.SetValue(1);
+
+  Observer<int> observer = original.Observe();
+  EXPECT_EQ(1, observer.GetValue());
+
+  original.SetValue(2);
+
+  std::vector<int> event_values1;
+  std::function<void()> callback1 = [&]() {
+    event_values1.push_back(observer.GetValue());
+  };
+  observer.SetOnUpdateCallback(base::BindRepeating(&RunCallback, callback1));
+
+  std::vector<int> event_values2;
+  std::function<void()> callback2 = [&]() {
+    event_values2.push_back(observer.GetValue());
+  };
+  observer.SetOnUpdateCallback(base::BindRepeating(&RunCallback, callback2));
+
+  // Propagate the SetValue events; only the second callback should be run.
+  base::RunLoop().RunUntilIdle();
+  EXPECT_EQ(2, observer.GetValue());
+  EXPECT_EQ(0u, event_values1.size());
+  ASSERT_EQ(1u, event_values2.size());
+  EXPECT_EQ(2, event_values2[0]);
+}
+
+TEST_F(ObserverTest, ObserverOutlivesObservable) {
+  auto original = base::MakeUnique<Observable<int>>(0);
+  Observer<int> observer1 = original->Observe();
+
+  EXPECT_EQ(0, observer1.GetValue());
+
+  original->SetValue(1);
+  original.reset();
+
+  Observer<int> observer2 = observer1;
+
+  base::RunLoop().RunUntilIdle();
+
+  EXPECT_EQ(1, observer1.GetValue());
+  EXPECT_EQ(1, observer2.GetValue());
+}
+
+TEST_F(ObserverTest, ObserverOnDifferentThread) {
+  auto original = base::MakeUnique<ThreadedObservable>();
+  Observer<int> observer = original->Observe();
+  EXPECT_EQ(0, observer.GetValue());
+
+  std::vector<int> event_values;
+  std::function<void()> callback = [&]() {
+    event_values.push_back(observer.GetValue());
+  };
+  observer.SetOnUpdateCallback(base::BindRepeating(&RunCallback, callback));
+
+  original->SetValue(1);
+  original->SetValue(2);
+  original.reset();
+
+  base::RunLoop().RunUntilIdle();
+
+  EXPECT_EQ(2, observer.GetValue());
+  ASSERT_EQ(2u, event_values.size());
+  EXPECT_EQ(1, event_values[0]);
+  EXPECT_EQ(2, event_values[1]);
+}
+
+TEST_F(ObserverTest, ObserveOnManyThreads) {
+  auto original = base::MakeUnique<Observable<int>>(0);
+  std::vector<std::unique_ptr<ThreadedObserver>> observers;
+  for (int i = 0; i < 20; ++i) {
+    observers.push_back(base::MakeUnique<ThreadedObserver>());
+    observers.back()->Observe(original.get());
+  }
+
+  original->SetValue(1);
+  original.reset();
+
+  base::RunLoop().RunUntilIdle();
+  for (auto& observer : observers) {
+    observer->CheckValue(1);
+  }
+
+  // Deleting the observers should check the expectations, since all posted
+  // tasks on their internal threads will run.
+  observers.clear();
+}
+
+}  // chromecast
diff --git a/chromecast/browser/cast_content_browser_client.cc b/chromecast/browser/cast_content_browser_client.cc
index 3d7f353..f4700d5 100644
--- a/chromecast/browser/cast_content_browser_client.cc
+++ b/chromecast/browser/cast_content_browser_client.cc
@@ -312,12 +312,18 @@
     command_line->AppendSwitch(switches::kEnableCrashReporter);
   }
 
-  // Renderer process command-line
+  // Command-line for different processes.
   if (process_type == switches::kRendererProcess) {
     // Any browser command-line switches that should be propagated to
     // the renderer go here.
     if (browser_command_line->HasSwitch(switches::kAllowHiddenMediaPlayback))
       command_line->AppendSwitch(switches::kAllowHiddenMediaPlayback);
+  } else if (process_type == switches::kUtilityProcess) {
+    if (browser_command_line->HasSwitch(switches::kAudioOutputChannels)) {
+      command_line->AppendSwitchASCII(switches::kAudioOutputChannels,
+                                      browser_command_line->GetSwitchValueASCII(
+                                          switches::kAudioOutputChannels));
+    }
   }
 
 #if defined(OS_LINUX)
diff --git a/chromecast/media/cma/backend/alsa/BUILD.gn b/chromecast/media/cma/backend/alsa/BUILD.gn
index 63dfa36..4de9c9a 100644
--- a/chromecast/media/cma/backend/alsa/BUILD.gn
+++ b/chromecast/media/cma/backend/alsa/BUILD.gn
@@ -100,6 +100,7 @@
 
 test("cast_alsa_cma_backend_unittests") {
   sources = [
+    "filter_group_unittest.cc",
     "stream_mixer_alsa_unittest.cc",
   ]
 
diff --git a/chromecast/media/cma/backend/alsa/cast_media_shlib.cc b/chromecast/media/cma/backend/alsa/cast_media_shlib.cc
index cc29c7c..2dfe4c6 100644
--- a/chromecast/media/cma/backend/alsa/cast_media_shlib.cc
+++ b/chromecast/media/cma/backend/alsa/cast_media_shlib.cc
@@ -88,6 +88,8 @@
 }  // namespace
 
 void CastMediaShlib::Initialize(const std::vector<std::string>& argv) {
+  // Sets logging to display process and thread ID.
+  logging::SetLogItems(true, true, false, false);
   chromecast::InitCommandLineShlib(argv);
 
   g_video_plane = new DefaultVideoPlane();
diff --git a/chromecast/media/cma/backend/alsa/filter_group.cc b/chromecast/media/cma/backend/alsa/filter_group.cc
index 881a4cf28..6937c56 100644
--- a/chromecast/media/cma/backend/alsa/filter_group.cc
+++ b/chromecast/media/cma/backend/alsa/filter_group.cc
@@ -17,17 +17,25 @@
 namespace chromecast {
 namespace media {
 FilterGroup::FilterGroup(int num_channels,
+                         bool mix_to_mono,
                          const std::string& name,
                          const base::ListValue* filter_list,
                          const std::unordered_set<std::string>& device_ids,
                          const std::vector<FilterGroup*>& mixed_inputs)
     : num_channels_(num_channels),
+      mix_to_mono_(mix_to_mono),
       name_(name),
       device_ids_(device_ids),
       mixed_inputs_(mixed_inputs),
       output_samples_per_second_(0),
       post_processing_pipeline_(
-          PostProcessingPipeline::Create(name_, filter_list, num_channels_)) {}
+          PostProcessingPipeline::Create(name_, filter_list, num_channels_)) {
+  for (auto* const m : mixed_inputs)
+    DCHECK_EQ(m->GetOutputChannelCount(), num_channels);
+  // Don't need mono mixer if input is single channel.
+  if (num_channels == 1)
+    mix_to_mono_ = false;
+}
 
 FilterGroup::~FilterGroup() = default;
 
@@ -108,6 +116,17 @@
 
   delay_frames_ = post_processing_pipeline_->ProcessFrames(
       interleaved(), chunk_size, last_volume_, is_silence);
+
+  // Mono mixing after all processing if needed.
+  if (mix_to_mono_) {
+    for (int frame = 0; frame < chunk_size; ++frame) {
+      float sum = 0;
+      for (int c = 0; c < num_channels_; ++c)
+        sum += interleaved()[frame * num_channels_ + c];
+      interleaved()[frame] = sum / num_channels_;
+    }
+  }
+
   return last_volume_;
 }
 
@@ -120,6 +139,10 @@
   active_inputs_.clear();
 }
 
+int FilterGroup::GetOutputChannelCount() const {
+  return mix_to_mono_ ? 1 : num_channels_;
+}
+
 void FilterGroup::ResizeBuffersIfNecessary(int chunk_size) {
   if (!mixed_ || mixed_->frames() < chunk_size) {
     mixed_ = ::media::AudioBus::Create(num_channels_, chunk_size);
diff --git a/chromecast/media/cma/backend/alsa/filter_group.h b/chromecast/media/cma/backend/alsa/filter_group.h
index 6c9f2ae..b71a20e 100644
--- a/chromecast/media/cma/backend/alsa/filter_group.h
+++ b/chromecast/media/cma/backend/alsa/filter_group.h
@@ -36,6 +36,10 @@
 // MixAndFilter() is called (they must be added each time data is queried).
 class FilterGroup {
  public:
+  // |num_channels| indicates number of input audio channels.
+  // |mix_to_mono| enables mono mixing in the pipeline. The number of audio
+  //    output channels will be 1 if it is set to true, otherwise it remains
+  //    same as |num_channels|.
   // |name| is used for debug printing
   // |filter_list| is a list of {"processor": LIBRARY_NAME, "configs": CONFIG}
   //    that is used to create PostProcessingPipeline.
@@ -47,6 +51,7 @@
   // FilterGroups currently use either InputQueues OR FilterGroups as inputs,
   //   but there is no technical limitation preventing mixing input classes.
   FilterGroup(int num_channels,
+              bool mix_to_mono,
               const std::string& name,
               const base::ListValue* filter_list,
               const std::unordered_set<std::string>& device_ids,
@@ -85,10 +90,14 @@
 
   std::string name() const { return name_; }
 
+  // Returns number of audio output channels from the filter group.
+  int GetOutputChannelCount() const;
+
  private:
   void ResizeBuffersIfNecessary(int chunk_size);
 
   const int num_channels_;
+  bool mix_to_mono_;
   const std::string name_;
   const std::unordered_set<std::string> device_ids_;
   std::vector<FilterGroup*> mixed_inputs_;
diff --git a/chromecast/media/cma/backend/alsa/filter_group_unittest.cc b/chromecast/media/cma/backend/alsa/filter_group_unittest.cc
new file mode 100644
index 0000000..b3a920e3
--- /dev/null
+++ b/chromecast/media/cma/backend/alsa/filter_group_unittest.cc
@@ -0,0 +1,172 @@
+// 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 "chromecast/media/cma/backend/alsa/filter_group.h"
+
+#include "base/memory/ptr_util.h"
+#include "chromecast/media/cma/backend/alsa/stream_mixer_alsa.h"
+#include "media/base/audio_bus.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace chromecast {
+namespace media {
+
+// Total of Test damples including left and right channels.
+#define NUM_SAMPLES 64
+
+constexpr size_t kBytesPerSample = sizeof(int32_t);
+constexpr int kNumInputChannels = 2;
+constexpr int kInputSampleRate = 48000;
+
+// Note: Test data should be represented as 32-bit integers and copied into
+// ::media::AudioBus instances, rather than wrapping statically declared float
+// arrays.
+constexpr int32_t kTestData[NUM_SAMPLES] = {
+    74343736,    -1333200799, -1360871126, 1138806283,  1931811865,
+    1856308487,  649203634,   564640023,   1676630678,  23416591,
+    -1293255456, 547928305,   -976258952,  1840550252,  1714525174,
+    358704931,   983646295,   1264863573,  442473973,   1222979052,
+    317404525,   366912613,   1393280948,  -1022004648, -2054669405,
+    -159762261,  1127018745,  -1984491787, 1406988336,  -693327981,
+    -1549544744, 1232236854,  970338338,   -1750160519, -783213057,
+    1231504562,  1155296810,  -820018779,  1155689800,  -1108462340,
+    -150535168,  1033717023,  2121241397,  1829995370,  -1893006836,
+    -819097508,  -495186107,  1001768909,  -1441111852, 692174781,
+    1916569026,  -687787473,  -910565280,  1695751872,  994166817,
+    1775451433,  909418522,   492671403,   -761744663,  -2064315902,
+    1357716471,  -1580019684, 1872702377,  -1524457840,
+};
+
+// Return a scoped pointer filled with the data above.
+std::unique_ptr<::media::AudioBus> GetTestData() {
+  int samples = NUM_SAMPLES / kNumInputChannels;
+  auto data = ::media::AudioBus::Create(kNumInputChannels, samples);
+  data->FromInterleaved(kTestData, samples, kBytesPerSample);
+  return data;
+}
+
+// TODO(erickung): Consolidate this mock class with the one used in
+// StreamMixerAlsaTest.
+// Class to provide a fake 64 audio samples.
+class MockInputQueue : public StreamMixerAlsa::InputQueue {
+ public:
+  MockInputQueue() : data_(GetTestData()) {}
+  ~MockInputQueue() override = default;
+
+  // StreamMixerAlsa::InputQueue implementations. Most of them are dummy except
+  // for
+  int input_samples_per_second() const override { return kInputSampleRate; }
+  bool primary() const override { return true; }
+  std::string device_id() const override { return "test"; }
+  AudioContentType content_type() const override {
+    return AudioContentType::kMedia;
+  }
+  bool IsDeleting() const override { return false; }
+  void Initialize(const MediaPipelineBackendAlsa::RenderingDelay&
+                      mixer_rendering_delay) override {}
+  void set_filter_group(FilterGroup* filter_group) override {
+    filter_group_ = filter_group;
+  }
+  FilterGroup* filter_group() override { return filter_group_; }
+  int MaxReadSize() override { return NUM_SAMPLES; }
+  void GetResampledData(::media::AudioBus* dest, int frames) override {
+    DCHECK(data_);
+    data_->CopyPartialFramesTo(0, frames, 0, dest);
+  }
+  void VolumeScaleAccumulate(bool repeat_transition,
+                             const float* src,
+                             int frames,
+                             float* dest) override {
+    DCHECK(dest);
+    DCHECK(src);
+    // Bypass the original audio data.
+    std::memcpy(dest, src, frames * sizeof(float));
+  }
+  void OnSkipped() override {}
+  void AfterWriteFrames(const MediaPipelineBackendAlsa::RenderingDelay&
+                            mixer_rendering_delay) override {}
+  void SignalError(StreamMixerAlsaInput::MixerError error) override {}
+  void PrepareToDelete(const OnReadyToDeleteCb& delete_cb) override {}
+  void SetContentTypeVolume(float volume, int fade_ms) override {}
+  void SetMuted(bool muted) override {}
+  float EffectiveVolume() override { return 1.0; }
+
+  const ::media::AudioBus* data() const { return data_.get(); }
+
+ private:
+  FilterGroup* filter_group_ = nullptr;
+  std::unique_ptr<::media::AudioBus> data_;
+
+  DISALLOW_COPY_AND_ASSIGN(MockInputQueue);
+};
+
+class FilterGroupTest : public testing::Test {
+ protected:
+  FilterGroupTest() : message_loop_(new base::MessageLoop()) {}
+  ~FilterGroupTest() override {}
+
+ private:
+  const std::unique_ptr<base::MessageLoop> message_loop_;
+
+  DISALLOW_COPY_AND_ASSIGN(FilterGroupTest);
+};
+
+TEST_F(FilterGroupTest, Passthrough) {
+  const int num_output_channel = 2;
+  std::unique_ptr<MockInputQueue> input(new MockInputQueue());
+  base::ListValue empty_filter_list;
+  std::unordered_set<std::string> empty_device_ids;
+  std::unique_ptr<FilterGroup> filter_group(new FilterGroup(
+      kNumInputChannels, false /* mix to mono */, "pass_through_filter",
+      &empty_filter_list, empty_device_ids, std::vector<FilterGroup*>()));
+  input->set_filter_group(filter_group.get());
+  const int input_samples = NUM_SAMPLES / kNumInputChannels;
+  filter_group->Initialize(kInputSampleRate);
+
+  // Adds input queue into filter group.
+  filter_group->AddActiveInput(input.get());
+
+  // Mixing input in the filter group
+  filter_group->MixAndFilter(input_samples);
+
+  // Verify if the fiter group output matches the source.
+  float* interleaved_data = filter_group->interleaved();
+  const int output_sample_size = input_samples * num_output_channel;
+  for (int i = 0; i < output_sample_size; ++i) {
+    int channel = (i % 2 == 0) ? 0 : 1;
+    float sample = input->data()->channel(channel)[i >> 1];
+    ASSERT_EQ(sample, interleaved_data[i]);
+  }
+}
+
+TEST_F(FilterGroupTest, MonoMixer) {
+  const int num_output_channel = 1;
+  const int input_samples = NUM_SAMPLES / kNumInputChannels;
+  std::unique_ptr<MockInputQueue> input(new MockInputQueue());
+  base::ListValue empty_filter_list;
+  std::unordered_set<std::string> empty_device_ids;
+  std::unique_ptr<FilterGroup> filter_group(new FilterGroup(
+      kNumInputChannels, true /* mix to mono */, "mono_mix_filter",
+      &empty_filter_list, empty_device_ids, std::vector<FilterGroup*>()));
+  input->set_filter_group(filter_group.get());
+  filter_group->Initialize(kInputSampleRate);
+
+  // Adds input queue into filter group.
+  filter_group->AddActiveInput(input.get());
+
+  // Mixing input in the filter group
+  filter_group->MixAndFilter(input_samples);
+
+  // Verify if the fiter group output matches the source after down mixing.
+  float* interleaved_data = filter_group->interleaved();
+  const int output_chunk_size = input_samples * num_output_channel;
+  for (int i = 0; i < output_chunk_size; ++i) {
+    const float* left_input = input->data()->channel(0);
+    const float* right_input = input->data()->channel(1);
+    ASSERT_EQ((left_input[i] + right_input[i]) / 2, interleaved_data[i]);
+  }
+}
+
+}  // namespace media
+}  // namespace chromecast
diff --git a/chromecast/media/cma/backend/alsa/stream_mixer_alsa.cc b/chromecast/media/cma/backend/alsa/stream_mixer_alsa.cc
index b75ceae..3779dcd 100644
--- a/chromecast/media/cma/backend/alsa/stream_mixer_alsa.cc
+++ b/chromecast/media/cma/backend/alsa/stream_mixer_alsa.cc
@@ -72,7 +72,7 @@
 namespace {
 
 const char kOutputDeviceDefaultName[] = "default";
-const int kNumOutputChannels = 2;
+const int kNumInputChannels = 2;
 
 const int kDefaultOutputBufferSizeFrames = 4096;
 const bool kPcmRecoverIsSilent = false;
@@ -245,6 +245,11 @@
 
   fixed_output_samples_per_second_ = fixed_samples_per_second;
 
+  num_output_channels_ = GetSwitchValueNonNegativeInt(
+      switches::kAudioOutputChannels, kNumInputChannels);
+  DCHECK(num_output_channels_ == kNumInputChannels ||
+         num_output_channels_ == 1);
+
   low_sample_rate_cutoff_ =
       chromecast::GetSwitchValueBoolean(switches::kAlsaEnableUpsampling, false)
           ? kLowSampleRateCutoff
@@ -274,8 +279,8 @@
           << pipeline_parser->GetFilePath() << ".";
     }
     filter_groups_.push_back(base::MakeUnique<FilterGroup>(
-        kNumOutputChannels, *device_ids.begin() /* name */,
-        stream_pipeline.pipeline, device_ids,
+        kNumInputChannels, false /* mono_mixer */,
+        *device_ids.begin() /* name */, stream_pipeline.pipeline, device_ids,
         std::vector<FilterGroup*>() /* mixed_inputs */));
     if (device_ids.find(::media::AudioDeviceDescription::kDefaultDeviceId) !=
         device_ids.end()) {
@@ -288,8 +293,8 @@
     std::string kDefaultDeviceId =
         ::media::AudioDeviceDescription::kDefaultDeviceId;
     filter_groups_.push_back(base::MakeUnique<FilterGroup>(
-        kNumOutputChannels, kDefaultDeviceId /* name */, nullptr,
-        std::unordered_set<std::string>({kDefaultDeviceId}),
+        kNumInputChannels, false /* mono_mixer */, kDefaultDeviceId /* name */,
+        nullptr, std::unordered_set<std::string>({kDefaultDeviceId}),
         std::vector<FilterGroup*>() /* mixed_inputs */));
     default_filter_ = filter_groups_.back().get();
   }
@@ -299,13 +304,17 @@
       filter_groups_.begin(), filter_groups_.end(), filter_group_ptrs.begin(),
       [](const std::unique_ptr<FilterGroup>& group) { return group.get(); });
 
+  // Enable Mono mixer in |mix_filter_| if necessary.
+  bool enabled_mono_mixer = (num_output_channels_ == 1);
   filter_groups_.push_back(base::MakeUnique<FilterGroup>(
-      kNumOutputChannels, "mix", pipeline_parser->GetMixPipeline(),
+      kNumInputChannels, enabled_mono_mixer, "mix",
+      pipeline_parser->GetMixPipeline(),
       std::unordered_set<std::string>() /* device_ids */, filter_group_ptrs));
   mix_filter_ = filter_groups_.back().get();
 
   filter_groups_.push_back(base::MakeUnique<FilterGroup>(
-      kNumOutputChannels, "linearize", pipeline_parser->GetLinearizePipeline(),
+      num_output_channels_, false /* mono_mixer */, "linearize",
+      pipeline_parser->GetLinearizePipeline(),
       std::unordered_set<std::string>() /* device_ids */,
       std::vector<FilterGroup*>({mix_filter_})));
   linearize_filter_ = filter_groups_.back().get();
@@ -443,7 +452,7 @@
 
   RETURN_ERROR_CODE(PcmHwParamsSetFormat, pcm_, pcm_hw_params_, pcm_format_);
   RETURN_ERROR_CODE(PcmHwParamsSetChannels, pcm_, pcm_hw_params_,
-                    kNumOutputChannels);
+                    num_output_channels_);
 
   // Set output rate, allow resampling with a warning if the device doesn't
   // support the rate natively.
@@ -894,7 +903,7 @@
 
 size_t StreamMixerAlsa::InterleavedSize(int frames) {
   return BytesPerOutputFormatSample() *
-         static_cast<size_t>(frames * kNumOutputChannels);
+         static_cast<size_t>(frames * num_output_channels_);
 }
 
 ssize_t StreamMixerAlsa::BytesPerOutputFormatSample() {
@@ -906,7 +915,7 @@
   CHECK_PCM_INITIALIZED();
 
   // Resize interleaved if necessary.
-  size_t interleaved_size = static_cast<size_t>(frames) * kNumOutputChannels *
+  size_t interleaved_size = static_cast<size_t>(frames) * num_output_channels_ *
                             BytesPerOutputFormatSample();
 
   int64_t expected_playback_time;
@@ -919,7 +928,7 @@
   }
 
   // Hard limit to [1.0, -1.0]
-  for (int i = 0; i < frames * kNumOutputChannels; ++i) {
+  for (int i = 0; i < frames * num_output_channels_; ++i) {
     mix_filter_->interleaved()[i] =
         std::min(1.0f, std::max(-1.0f, mix_filter_->interleaved()[i]));
   }
@@ -927,7 +936,7 @@
   for (CastMediaShlib::LoopbackAudioObserver* observer : loopback_observers_) {
     observer->OnLoopbackAudio(
         expected_playback_time, kSampleFormatF32, output_samples_per_second_,
-        kNumOutputChannels,
+        num_output_channels_,
         reinterpret_cast<uint8_t*>(mix_filter_->interleaved()),
         InterleavedSize(frames));
   }
@@ -935,7 +944,7 @@
   uint8_t* data;
   if (pcm_format_ == SND_PCM_FORMAT_FLOAT) {
     // Hard limit to [1.0, -1.0]. ToFixedPoint handles this for other cases.
-    for (int i = 0; i < frames * kNumOutputChannels; ++i) {
+    for (int i = 0; i < frames * num_output_channels_; ++i) {
       linearize_filter_->interleaved()[i] =
           std::min(1.0f, std::max(-1.0f, linearize_filter_->interleaved()[i]));
     }
@@ -944,8 +953,9 @@
     if (interleaved_.size() < interleaved_size) {
       interleaved_.resize(interleaved_size);
     }
-    ToFixedPoint(linearize_filter_->interleaved(), frames * kNumOutputChannels,
-                 BytesPerOutputFormatSample(), interleaved_.data());
+    ToFixedPoint(linearize_filter_->interleaved(),
+                 frames * num_output_channels_, BytesPerOutputFormatSample(),
+                 interleaved_.data());
     data = interleaved_.data();
   }
 
@@ -967,7 +977,8 @@
     }
     frames_left -= frames_or_error;
     DCHECK_GE(frames_left, 0);
-    data += frames_or_error * kNumOutputChannels * BytesPerOutputFormatSample();
+    data +=
+        frames_or_error * num_output_channels_ * BytesPerOutputFormatSample();
   }
   UpdateRenderingDelay(frames);
   MediaPipelineBackendAlsa::RenderingDelay common_rendering_delay =
diff --git a/chromecast/media/cma/backend/alsa/stream_mixer_alsa.h b/chromecast/media/cma/backend/alsa/stream_mixer_alsa.h
index 3b8f181..4b54751e 100644
--- a/chromecast/media/cma/backend/alsa/stream_mixer_alsa.h
+++ b/chromecast/media/cma/backend/alsa/stream_mixer_alsa.h
@@ -257,6 +257,7 @@
   scoped_refptr<base::SingleThreadTaskRunner> mixer_task_runner_;
 
   unsigned int fixed_output_samples_per_second_;
+  int num_output_channels_;
   unsigned int low_sample_rate_cutoff_;
   int requested_output_samples_per_second_;
   int output_samples_per_second_;
diff --git a/components/app_modal/BUILD.gn b/components/app_modal/BUILD.gn
index 23b1b84..dfb72d1c 100644
--- a/components/app_modal/BUILD.gn
+++ b/components/app_modal/BUILD.gn
@@ -6,8 +6,6 @@
 
 static_library("app_modal") {
   sources = [
-    "app_modal_dialog.cc",
-    "app_modal_dialog.h",
     "app_modal_dialog_queue.cc",
     "app_modal_dialog_queue.h",
     "javascript_app_modal_dialog.cc",
diff --git a/components/app_modal/app_modal_dialog.cc b/components/app_modal/app_modal_dialog.cc
deleted file mode 100644
index 8689b7f..0000000
--- a/components/app_modal/app_modal_dialog.cc
+++ /dev/null
@@ -1,80 +0,0 @@
-// Copyright (c) 2011 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/app_modal/app_modal_dialog.h"
-
-#include "base/logging.h"
-#include "base/run_loop.h"
-#include "components/app_modal/app_modal_dialog_queue.h"
-#include "components/app_modal/native_app_modal_dialog.h"
-
-using content::WebContents;
-
-namespace app_modal {
-namespace {
-
-AppModalDialogObserver* app_modal_dialog_observer = NULL;
-
-}  // namespace
-
-AppModalDialogObserver::AppModalDialogObserver() {
-  DCHECK(!app_modal_dialog_observer);
-  app_modal_dialog_observer = this;
-}
-
-AppModalDialogObserver::~AppModalDialogObserver() {
-  DCHECK(app_modal_dialog_observer);
-  app_modal_dialog_observer = NULL;
-}
-
-AppModalDialog::AppModalDialog(WebContents* web_contents,
-                               const base::string16& title)
-    : title_(title),
-      completed_(false),
-      valid_(true),
-      native_dialog_(NULL),
-      web_contents_(web_contents) {
-}
-
-AppModalDialog::~AppModalDialog() {
-  CompleteDialog();
-}
-
-void AppModalDialog::ShowModalDialog() {
-  native_dialog_ = CreateNativeDialog();
-  native_dialog_->ShowAppModalDialog();
-  if (app_modal_dialog_observer)
-    app_modal_dialog_observer->Notify(this);
-}
-
-bool AppModalDialog::IsValid() {
-  return valid_;
-}
-
-void AppModalDialog::Invalidate() {
-  valid_ = false;
-}
-
-bool AppModalDialog::IsJavaScriptModalDialog() {
-  return false;
-}
-
-void AppModalDialog::ActivateModalDialog() {
-  DCHECK(native_dialog_);
-  native_dialog_->ActivateAppModalDialog();
-}
-
-void AppModalDialog::CloseModalDialog() {
-  DCHECK(native_dialog_);
-  native_dialog_->CloseAppModalDialog();
-}
-
-void AppModalDialog::CompleteDialog() {
-  if (!completed_) {
-    completed_ = true;
-    AppModalDialogQueue::GetInstance()->ShowNextDialog();
-  }
-}
-
-}  // namespace app_modal
diff --git a/components/app_modal/app_modal_dialog.h b/components/app_modal/app_modal_dialog.h
deleted file mode 100644
index c4923ea..0000000
--- a/components/app_modal/app_modal_dialog.h
+++ /dev/null
@@ -1,102 +0,0 @@
-// Copyright (c) 2011 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_APP_MODAL_APP_MODAL_DIALOG_H_
-#define COMPONENTS_APP_MODAL_APP_MODAL_DIALOG_H_
-
-#include <string>
-
-#include "base/macros.h"
-#include "base/strings/string16.h"
-#include "build/build_config.h"
-
-namespace content {
-class WebContents;
-}
-
-namespace app_modal {
-
-class NativeAppModalDialog;
-
-// A controller+model base class for modal dialogs.
-class AppModalDialog {
- public:
-  // A union of data necessary to determine the type of message box to
-  // show.
-  AppModalDialog(content::WebContents* web_contents,
-                 const base::string16& title);
-  virtual ~AppModalDialog();
-
-  // Called by the AppModalDialogQueue to show this dialog.
-  void ShowModalDialog();
-
-  // Called by the AppModalDialogQueue to activate the dialog.
-  void ActivateModalDialog();
-
-  // Closes the dialog if it is showing.
-  void CloseModalDialog();
-
-  // Completes dialog handling, shows next modal dialog from the queue.
-  // TODO(beng): Get rid of this method.
-  void CompleteDialog();
-
-  base::string16 title() const { return title_; }
-  NativeAppModalDialog* native_dialog() const { return native_dialog_; }
-  content::WebContents* web_contents() const { return web_contents_; }
-
-  // Returns true if the dialog is still valid. As dialogs are created they are
-  // added to the AppModalDialogQueue. When the current modal dialog finishes
-  // and it's time to show the next dialog in the queue IsValid is invoked.
-  // If IsValid returns false the dialog is deleted and not shown.
-  bool IsValid();
-
-  // Methods overridable by AppModalDialog subclasses:
-
-  // Invalidates the dialog, therefore causing it to not be shown when its turn
-  // to be shown comes around.
-  virtual void Invalidate();
-
-  // Used only for testing. Returns whether the dialog is a JavaScript modal
-  // dialog.
-  virtual bool IsJavaScriptModalDialog();
-
- protected:
-  // Overridden by subclasses to create the feature-specific native dialog box.
-  virtual NativeAppModalDialog* CreateNativeDialog() = 0;
-
- private:
-  // Information about the message box is held in the following variables.
-  base::string16 title_;
-
-  // True if CompleteDialog was called.
-  bool completed_;
-
-  // False if the dialog should no longer be shown, e.g. because the underlying
-  // tab navigated away while the dialog was queued.
-  bool valid_;
-
-  // The toolkit-specific implementation of the app modal dialog box.
-  NativeAppModalDialog* native_dialog_;
-
-  content::WebContents* web_contents_;
-
-  DISALLOW_COPY_AND_ASSIGN(AppModalDialog);
-};
-
-// An interface to observe that a modal dialog is shown.
-class AppModalDialogObserver {
- public:
-  AppModalDialogObserver();
-  virtual ~AppModalDialogObserver();
-
-  // Called when the modal dialog is shown.
-  virtual void Notify(AppModalDialog* dialog) = 0;
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(AppModalDialogObserver);
-};
-
-}  // namespace app_modal
-
-#endif  // COMPONENTS_APP_MODAL_APP_MODAL_DIALOG_H_
diff --git a/components/app_modal/app_modal_dialog_queue.cc b/components/app_modal/app_modal_dialog_queue.cc
index ab041eb..ce1fc2b 100644
--- a/components/app_modal/app_modal_dialog_queue.cc
+++ b/components/app_modal/app_modal_dialog_queue.cc
@@ -5,7 +5,7 @@
 #include "components/app_modal/app_modal_dialog_queue.h"
 
 #include "base/memory/singleton.h"
-#include "components/app_modal/app_modal_dialog.h"
+#include "components/app_modal/javascript_app_modal_dialog.h"
 
 namespace app_modal {
 
@@ -14,7 +14,7 @@
   return base::Singleton<AppModalDialogQueue>::get();
 }
 
-void AppModalDialogQueue::AddDialog(AppModalDialog* dialog) {
+void AppModalDialogQueue::AddDialog(JavaScriptAppModalDialog* dialog) {
   if (!active_dialog_) {
     ShowModalDialog(dialog);
     return;
@@ -23,11 +23,11 @@
 }
 
 void AppModalDialogQueue::ShowNextDialog() {
-  AppModalDialog* dialog = GetNextDialog();
+  JavaScriptAppModalDialog* dialog = GetNextDialog();
   if (dialog)
     ShowModalDialog(dialog);
   else
-    active_dialog_ = NULL;
+    active_dialog_ = nullptr;
 }
 
 void AppModalDialogQueue::ActivateModalDialog() {
@@ -43,7 +43,7 @@
 }
 
 bool AppModalDialogQueue::HasActiveDialog() const {
-  return active_dialog_ != NULL;
+  return active_dialog_ != nullptr;
 }
 
 AppModalDialogQueue::AppModalDialogQueue()
@@ -54,11 +54,11 @@
 AppModalDialogQueue::~AppModalDialogQueue() {
 }
 
-void AppModalDialogQueue::ShowModalDialog(AppModalDialog* dialog) {
+void AppModalDialogQueue::ShowModalDialog(JavaScriptAppModalDialog* dialog) {
   // Be sure and set the active_dialog_ field first, otherwise if
   // ShowModalDialog triggers a call back to the queue they'll get the old
   // dialog. Also, if the dialog calls |ShowNextDialog()| before returning, that
-  // would write NULL into |active_dialog_| and this function would then undo
+  // would write nullptr into |active_dialog_| and this function would then undo
   // that.
   active_dialog_ = dialog;
   showing_modal_dialog_ = true;
@@ -66,15 +66,15 @@
   showing_modal_dialog_ = false;
 }
 
-AppModalDialog* AppModalDialogQueue::GetNextDialog() {
+JavaScriptAppModalDialog* AppModalDialogQueue::GetNextDialog() {
   while (!app_modal_dialog_queue_.empty()) {
-    AppModalDialog* dialog = app_modal_dialog_queue_.front();
+    JavaScriptAppModalDialog* dialog = app_modal_dialog_queue_.front();
     app_modal_dialog_queue_.pop_front();
     if (dialog->IsValid())
       return dialog;
     delete dialog;
   }
-  return NULL;
+  return nullptr;
 }
 
 }  // namespace app_modal
diff --git a/components/app_modal/app_modal_dialog_queue.h b/components/app_modal/app_modal_dialog_queue.h
index 96a582e..3b945b6b 100644
--- a/components/app_modal/app_modal_dialog_queue.h
+++ b/components/app_modal/app_modal_dialog_queue.h
@@ -15,14 +15,14 @@
 
 namespace app_modal {
 
-class AppModalDialog;
+class JavaScriptAppModalDialog;
 
-// Keeps a queue of AppModalDialogs, making sure only one app modal
+// Keeps a queue of JavaScriptAppModalDialogs, making sure only one app modal
 // dialog is shown at a time.
 // This class is a singleton.
 class AppModalDialogQueue {
  public:
-  typedef std::deque<AppModalDialog*>::iterator iterator;
+  typedef std::deque<JavaScriptAppModalDialog*>::iterator iterator;
 
   // Returns the singleton instance.
   static AppModalDialogQueue* GetInstance();
@@ -32,9 +32,9 @@
   // most recently active browser window (or whichever is currently active)
   // will be app modal, meaning it will be activated if the user tries to
   // activate any other browser windows.
-  // Note: The AppModalDialog |dialog| must be window modal before it
+  // Note: The JavaScriptAppModalDialog |dialog| must be window modal before it
   // can be added as app modal.
-  void AddDialog(AppModalDialog* dialog);
+  void AddDialog(JavaScriptAppModalDialog* dialog);
 
   // Removes the current dialog in the queue (the one that is being shown).
   // Shows the next dialog in the queue, if any is present. This does not
@@ -53,7 +53,7 @@
   // Returns true if there is currently an active app modal dialog box.
   bool HasActiveDialog() const;
 
-  AppModalDialog* active_dialog() { return active_dialog_; }
+  JavaScriptAppModalDialog* active_dialog() { return active_dialog_; }
 
   // Iterators to walk the queue. The queue does not include the currently
   // active app modal dialog box.
@@ -67,21 +67,21 @@
   ~AppModalDialogQueue();
 
   // Shows |dialog| and notifies the BrowserList that a modal dialog is showing.
-  void ShowModalDialog(AppModalDialog* dialog);
+  void ShowModalDialog(JavaScriptAppModalDialog* dialog);
 
   // Returns the next dialog to show. This removes entries from
   // app_modal_dialog_queue_ until one is valid or the queue is empty. This
-  // returns NULL if there are no more dialogs, or all the dialogs in the queue
-  // are not valid.
-  AppModalDialog* GetNextDialog();
+  // returns nullptr if there are no more dialogs, or all the dialogs in the
+  // queue are not valid.
+  JavaScriptAppModalDialog* GetNextDialog();
 
   // Contains all app modal dialogs which are waiting to be shown. The currently
   // active modal dialog is not included.
-  std::deque<AppModalDialog*> app_modal_dialog_queue_;
+  std::deque<JavaScriptAppModalDialog*> app_modal_dialog_queue_;
 
-  // The currently active app-modal dialog box's delegate. NULL if there is no
-  // active app-modal dialog box.
-  AppModalDialog* active_dialog_;
+  // The currently active app-modal dialog box. nullptr if there is no active
+  // app-modal dialog box.
+  JavaScriptAppModalDialog* active_dialog_;
 
   // Stores if |ShowModalDialog()| is currently being called on an app-modal
   // dialog.
diff --git a/components/app_modal/javascript_app_modal_dialog.cc b/components/app_modal/javascript_app_modal_dialog.cc
index dd58080..2e2ec520 100644
--- a/components/app_modal/javascript_app_modal_dialog.cc
+++ b/components/app_modal/javascript_app_modal_dialog.cc
@@ -7,13 +7,17 @@
 #include "base/metrics/histogram_macros.h"
 #include "base/time/time.h"
 #include "build/build_config.h"
+#include "components/app_modal/app_modal_dialog_queue.h"
 #include "components/app_modal/javascript_dialog_manager.h"
 #include "components/app_modal/javascript_native_dialog_factory.h"
+#include "components/app_modal/native_app_modal_dialog.h"
 #include "ui/gfx/text_elider.h"
 
 namespace app_modal {
 namespace {
 
+AppModalDialogObserver* app_modal_dialog_observer = nullptr;
+
 // Control maximum sizes of various texts passed to us from javascript.
 #if defined(OS_POSIX) && !defined(OS_MACOSX)
 // Two-dimensional eliding.  Reformat the text of the message dialog
@@ -66,7 +70,11 @@
     bool is_before_unload_dialog,
     bool is_reload,
     const content::JavaScriptDialogManager::DialogClosedCallback& callback)
-    : AppModalDialog(web_contents, title),
+    : title_(title),
+      completed_(false),
+      valid_(true),
+      native_dialog_(nullptr),
+      web_contents_(web_contents),
       extra_data_map_(extra_data_map),
       javascript_dialog_type_(javascript_dialog_type),
       display_suppress_checkbox_(display_suppress_checkbox),
@@ -80,25 +88,46 @@
 }
 
 JavaScriptAppModalDialog::~JavaScriptAppModalDialog() {
+  CompleteDialog();
 }
 
-NativeAppModalDialog* JavaScriptAppModalDialog::CreateNativeDialog() {
-  return JavaScriptDialogManager::GetInstance()
-      ->native_dialog_factory()
-      ->CreateNativeJavaScriptDialog(this);
+void JavaScriptAppModalDialog::ShowModalDialog() {
+  native_dialog_ = JavaScriptDialogManager::GetInstance()
+                       ->native_dialog_factory()
+                       ->CreateNativeJavaScriptDialog(this);
+  native_dialog_->ShowAppModalDialog();
+  if (app_modal_dialog_observer)
+    app_modal_dialog_observer->Notify(this);
 }
 
-bool JavaScriptAppModalDialog::IsJavaScriptModalDialog() {
-  return true;
+void JavaScriptAppModalDialog::ActivateModalDialog() {
+  DCHECK(native_dialog_);
+  native_dialog_->ActivateAppModalDialog();
+}
+
+void JavaScriptAppModalDialog::CloseModalDialog() {
+  DCHECK(native_dialog_);
+  native_dialog_->CloseAppModalDialog();
+}
+
+void JavaScriptAppModalDialog::CompleteDialog() {
+  if (!completed_) {
+    completed_ = true;
+    AppModalDialogQueue::GetInstance()->ShowNextDialog();
+  }
+}
+
+bool JavaScriptAppModalDialog::IsValid() {
+  return valid_;
 }
 
 void JavaScriptAppModalDialog::Invalidate() {
-  if (!IsValid())
+  if (!valid_)
     return;
 
-  AppModalDialog::Invalidate();
+  valid_ = false;
   CallDialogClosedCallback(false, base::string16());
-  if (native_dialog())
+  if (native_dialog_)
     CloseModalDialog();
 }
 
@@ -138,7 +167,7 @@
 void JavaScriptAppModalDialog::NotifyDelegate(bool success,
                                               const base::string16& user_input,
                                               bool suppress_js_messages) {
-  if (!IsValid())
+  if (!valid_)
     return;
 
   CallDialogClosedCallback(success, user_input);
@@ -146,7 +175,7 @@
   // The close callback above may delete web_contents_, thus removing the extra
   // data from the map owned by ::JavaScriptDialogManager. Make sure
   // to only use the data if still present. http://crbug.com/236476
-  ExtraDataMap::iterator extra_data = extra_data_map_->find(web_contents());
+  ExtraDataMap::iterator extra_data = extra_data_map_->find(web_contents_);
   if (extra_data != extra_data_map_->end()) {
     extra_data->second.has_already_shown_a_dialog_ = true;
     extra_data->second.suppress_javascript_messages_ = suppress_js_messages;
@@ -154,14 +183,14 @@
 
   // On Views, we can end up coming through this code path twice :(.
   // See crbug.com/63732.
-  AppModalDialog::Invalidate();
+  valid_ = false;
 }
 
 void JavaScriptAppModalDialog::CallDialogClosedCallback(bool success,
     const base::string16& user_input) {
   // TODO(joenotcharles): Both the callers of this function also check IsValid
-  // and call AppModalDialog::Invalidate, but in different orders. If the
-  // difference is not significant, more common code could be moved here.
+  // and call Invalidate, but in different orders. If the difference is not
+  // significant, more common code could be moved here.
   UMA_HISTOGRAM_MEDIUM_TIMES(
       "JSDialogs.FineTiming.TimeBetweenDialogCreatedAndSameDialogClosed",
       base::TimeTicks::Now() - creation_time_);
@@ -171,4 +200,14 @@
   }
 }
 
+AppModalDialogObserver::AppModalDialogObserver() {
+  DCHECK(!app_modal_dialog_observer);
+  app_modal_dialog_observer = this;
+}
+
+AppModalDialogObserver::~AppModalDialogObserver() {
+  DCHECK(app_modal_dialog_observer);
+  app_modal_dialog_observer = nullptr;
+}
+
 }  // namespace app_modal
diff --git a/components/app_modal/javascript_app_modal_dialog.h b/components/app_modal/javascript_app_modal_dialog.h
index a87febde9f..06269e1 100644
--- a/components/app_modal/javascript_app_modal_dialog.h
+++ b/components/app_modal/javascript_app_modal_dialog.h
@@ -10,11 +10,12 @@
 #include "base/compiler_specific.h"
 #include "base/macros.h"
 #include "base/time/time.h"
-#include "components/app_modal/app_modal_dialog.h"
 #include "content/public/browser/javascript_dialog_manager.h"
 
 namespace app_modal {
 
+class NativeAppModalDialog;
+
 // Extra data for JavaScript dialogs to add Chrome-only features.
 class ChromeJavaScriptDialogExtraData {
  public:
@@ -32,7 +33,7 @@
 
 // A controller + model class for JavaScript alert, confirm, prompt, and
 // onbeforeunload dialog boxes.
-class JavaScriptAppModalDialog : public AppModalDialog {
+class JavaScriptAppModalDialog {
  public:
   typedef std::map<void*, ChromeJavaScriptDialogExtraData> ExtraDataMap;
 
@@ -47,12 +48,26 @@
       bool is_before_unload_dialog,
       bool is_reload,
       const content::JavaScriptDialogManager::DialogClosedCallback& callback);
-  ~JavaScriptAppModalDialog() override;
+  ~JavaScriptAppModalDialog();
 
-  // Overridden from AppModalDialog:
-  NativeAppModalDialog* CreateNativeDialog() override;
-  bool IsJavaScriptModalDialog() override;
-  void Invalidate() override;
+  // Called by the AppModalDialogQueue to show this dialog.
+  void ShowModalDialog();
+
+  // Called by the AppModalDialogQueue to activate the dialog.
+  void ActivateModalDialog();
+
+  // Closes the dialog if it is showing.
+  void CloseModalDialog();
+
+  // Returns true if the dialog is still valid. As dialogs are created they are
+  // added to the AppModalDialogQueue. When the current modal dialog finishes
+  // and it's time to show the next dialog in the queue IsValid is invoked.
+  // If IsValid returns false the dialog is deleted and not shown.
+  bool IsValid();
+
+  // Invalidates the dialog, therefore causing it to not be shown when its turn
+  // to be shown comes around.
+  void Invalidate();
 
   // Callbacks from NativeDialog when the user accepts or cancels the dialog.
   void OnCancel(bool suppress_js_messages);
@@ -66,7 +81,10 @@
   // its delegate instead of whatever the UI reports.
   void SetOverridePromptText(const base::string16& prompt_text);
 
-  // Accessors
+  // Accessors.
+  base::string16 title() const { return title_; }
+  NativeAppModalDialog* native_dialog() const { return native_dialog_; }
+  content::WebContents* web_contents() const { return web_contents_; }
   content::JavaScriptDialogType javascript_dialog_type() const {
     return javascript_dialog_type_;
   }
@@ -84,6 +102,26 @@
   void CallDialogClosedCallback(bool success,
                                 const base::string16& prompt_text);
 
+  // Completes dialog handling, shows next modal dialog from the queue.
+  // TODO(beng): Get rid of this method.
+  void CompleteDialog();
+
+  // The title of the dialog.
+  base::string16 title_;
+
+  // // True if CompleteDialog was called.
+  bool completed_;
+
+  // False if the dialog should no longer be shown, e.g. because the underlying
+  // tab navigated away while the dialog was queued.
+  bool valid_;
+
+  // // The toolkit-specific implementation of the app modal dialog box.
+  NativeAppModalDialog* native_dialog_;
+
+  // The WebContents that opened this dialog.
+  content::WebContents* web_contents_;
+
   // A map of extra Chrome-only data associated with the delegate_. Can be
   // inspected via |extra_data_map_[web_contents_]|.
   ExtraDataMap* extra_data_map_;
@@ -108,6 +146,19 @@
   DISALLOW_COPY_AND_ASSIGN(JavaScriptAppModalDialog);
 };
 
+// An interface to observe that a modal dialog is shown.
+class AppModalDialogObserver {
+ public:
+  AppModalDialogObserver();
+  virtual ~AppModalDialogObserver();
+
+  // Called when the modal dialog is shown.
+  virtual void Notify(JavaScriptAppModalDialog* dialog) = 0;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(AppModalDialogObserver);
+};
+
 }  // namespace app_modal
 
 #endif  // COMPONENTS_APP_MODAL_JAVASCRIPT_APP_MODAL_DIALOG_H_
diff --git a/components/app_modal/javascript_dialog_manager.cc b/components/app_modal/javascript_dialog_manager.cc
index c5a3683..de3674c 100644
--- a/components/app_modal/javascript_dialog_manager.cc
+++ b/components/app_modal/javascript_dialog_manager.cc
@@ -12,7 +12,6 @@
 #include "base/macros.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/strings/utf_string_conversions.h"
-#include "components/app_modal/app_modal_dialog.h"
 #include "components/app_modal/app_modal_dialog_queue.h"
 #include "components/app_modal/javascript_dialog_extensions_client.h"
 #include "components/app_modal/javascript_native_dialog_factory.h"
@@ -244,7 +243,6 @@
     const base::string16* prompt_override) {
   AppModalDialogQueue* dialog_queue = AppModalDialogQueue::GetInstance();
   if (!dialog_queue->HasActiveDialog() ||
-      !dialog_queue->active_dialog()->IsJavaScriptModalDialog() ||
       dialog_queue->active_dialog()->web_contents() != web_contents) {
     return false;
   }
@@ -272,15 +270,14 @@
 void JavaScriptDialogManager::CancelDialogs(content::WebContents* web_contents,
                                             bool reset_state) {
   AppModalDialogQueue* queue = AppModalDialogQueue::GetInstance();
-  AppModalDialog* active_dialog = queue->active_dialog();
-  for (AppModalDialogQueue::iterator i = queue->begin();
-       i != queue->end(); ++i) {
+  JavaScriptAppModalDialog* active_dialog = queue->active_dialog();
+  for (auto* dialog : *queue) {
     // Invalidating the active dialog might trigger showing a not-yet
     // invalidated dialog, so invalidate the active dialog last.
-    if ((*i) == active_dialog)
+    if (dialog == active_dialog)
       continue;
-    if ((*i)->web_contents() == web_contents)
-      (*i)->Invalidate();
+    if (dialog->web_contents() == web_contents)
+      dialog->Invalidate();
   }
   if (active_dialog && active_dialog->web_contents() == web_contents)
     active_dialog->Invalidate();
diff --git a/components/autofill/core/browser/credit_card.h b/components/autofill/core/browser/credit_card.h
index 6dd2b3e..5b1ba15 100644
--- a/components/autofill/core/browser/credit_card.h
+++ b/components/autofill/core/browser/credit_card.h
@@ -215,14 +215,14 @@
   base::string16 GetLastUsedDateForDisplay(const std::string& app_locale) const;
   // Formatted expiration date (e.g., 05/2020).
   base::string16 ExpirationDateForDisplay() const;
+  // Expiration functions.
+  base::string16 ExpirationMonthAsString() const;
+  base::string16 Expiration4DigitYearAsString() const;
 
  private:
   FRIEND_TEST_ALL_PREFIXES(CreditCardTest, SetExpirationDateFromString);
   FRIEND_TEST_ALL_PREFIXES(CreditCardTest, SetExpirationYearFromString);
 
-  // Private display functions.
-  base::string16 ExpirationMonthAsString() const;
-  base::string16 Expiration4DigitYearAsString() const;
   base::string16 Expiration2DigitYearAsString() const;
 
   // FormGroup:
diff --git a/components/autofill/core/browser/validation.cc b/components/autofill/core/browser/validation.cc
index a2d3e25f..4588a13b 100644
--- a/components/autofill/core/browser/validation.cc
+++ b/components/autofill/core/browser/validation.cc
@@ -154,11 +154,11 @@
 
 base::string16 GetCompletionMessageForCard(CreditCardCompletionStatus status) {
   switch (status) {
+    // No message is shown for complete or expired card (which will be fixable)
+    // in the CVC screen.
     case CREDIT_CARD_COMPLETE:
-      return base::string16();
     case CREDIT_CARD_EXPIRED:
-      return l10n_util::GetStringUTF16(
-          IDS_PAYMENTS_VALIDATION_INVALID_CREDIT_CARD_EXPIRED);
+      return base::string16();
     case CREDIT_CARD_NO_CARDHOLDER:
       return l10n_util::GetStringUTF16(IDS_PAYMENTS_NAME_ON_CARD_REQUIRED);
     case CREDIT_CARD_NO_NUMBER:
diff --git a/components/component_updater/default_component_installer.cc b/components/component_updater/default_component_installer.cc
index 6cdca7ed..62842079 100644
--- a/components/component_updater/default_component_installer.cc
+++ b/components/component_updater/default_component_installer.cc
@@ -11,7 +11,9 @@
 #include "base/files/file_enumerator.h"
 #include "base/files/file_path.h"
 #include "base/files/file_util.h"
+#include "base/files/scoped_temp_dir.h"
 #include "base/location.h"
+#include "base/macros.h"
 #include "base/path_service.h"
 #include "base/sequenced_task_runner.h"
 #include "base/single_thread_task_runner.h"
@@ -116,6 +118,11 @@
   VLOG(1) << "Install: version=" << version.GetString()
           << " current version=" << current_version_.GetString();
 
+  // Take the ownership of the |unpack_path| to enforce its deletion.
+  DCHECK(DirectoryExists(unpack_path));
+  base::ScopedTempDir unpack_path_owner;
+  ignore_result(unpack_path_owner.Set(unpack_path));
+
   if (!version.IsValid())
     return Result(InstallError::INVALID_VERSION);
   if (current_version_.CompareTo(version) > 0)
@@ -134,6 +141,10 @@
     base::DeleteFile(install_path, true);
     return result;
   }
+
+  // If install has been successful, the installer has deleted the unpack path.
+  DCHECK(!base::PathExists(unpack_path));
+
   current_version_ = version;
   current_install_dir_ = install_path;
   // TODO(ddorwin): Change parameter to std::unique_ptr<base::DictionaryValue>
diff --git a/components/component_updater/default_component_installer_unittest.cc b/components/component_updater/default_component_installer_unittest.cc
index 278e3aa..d012be1 100644
--- a/components/component_updater/default_component_installer_unittest.cc
+++ b/components/component_updater/default_component_installer_unittest.cc
@@ -8,16 +8,23 @@
 #include <vector>
 
 #include "base/callback.h"
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
 #include "base/macros.h"
+#include "base/memory/ptr_util.h"
 #include "base/memory/ref_counted.h"
+#include "base/path_service.h"
 #include "base/run_loop.h"
 #include "base/task_scheduler/post_task.h"
+#include "base/test/scoped_path_override.h"
 #include "base/test/scoped_task_environment.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "base/version.h"
+#include "components/component_updater/component_updater_paths.h"
 #include "components/component_updater/component_updater_service.h"
 #include "components/component_updater/component_updater_service_internal.h"
 #include "components/component_updater/default_component_installer.h"
+#include "components/update_client/component_unpacker.h"
 #include "components/update_client/crx_update_item.h"
 #include "components/update_client/test_configurator.h"
 #include "components/update_client/update_client.h"
@@ -25,6 +32,7 @@
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
+using ComponentUnpacker = update_client::ComponentUnpacker;
 using Configurator = update_client::Configurator;
 using CrxUpdateItem = update_client::CrxUpdateItem;
 using TestConfigurator = update_client::TestConfigurator;
@@ -43,6 +51,19 @@
                                0x6e, 0x05, 0x6b, 0xe8, 0x73, 0x47, 0xf6, 0xc4,
                                0x11, 0x9f, 0xbc, 0xb3, 0x09, 0xb3, 0x5b, 0x40};
 
+constexpr base::FilePath::CharType relative_install_dir[] =
+    FILE_PATH_LITERAL("fake");
+
+base::FilePath test_file(const char* file) {
+  base::FilePath path;
+  PathService::Get(base::DIR_SOURCE_ROOT, &path);
+  return path.AppendASCII("components")
+      .AppendASCII("test")
+      .AppendASCII("data")
+      .AppendASCII("update_client")
+      .AppendASCII(file);
+}
+
 class MockUpdateClient : public UpdateClient {
  public:
   MockUpdateClient() {}
@@ -72,6 +93,7 @@
 
 class FakeInstallerTraits : public ComponentInstallerTraits {
  public:
+  FakeInstallerTraits() {}
   ~FakeInstallerTraits() override {}
 
   bool VerifyInstallation(const base::DictionaryValue& manifest,
@@ -97,7 +119,7 @@
       std::unique_ptr<base::DictionaryValue> manifest) override {}
 
   base::FilePath GetRelativeInstallDir() const override {
-    return base::FilePath(FILE_PATH_LITERAL("fake"));
+    return base::FilePath(relative_install_dir);
   }
 
   void GetHash(std::vector<uint8_t>* hash) const override { GetPkHash(hash); }
@@ -135,15 +157,22 @@
 
  protected:
   void RunThreads();
+  void Unpack(const base::FilePath& crx_path);
+  ComponentUnpacker::Result result() const { return result_; }
 
  private:
+  void UnpackComplete(const ComponentUnpacker::Result& result);
+
   base::test::ScopedTaskEnvironment scoped_task_environment_;
+  const scoped_refptr<base::SingleThreadTaskRunner> main_thread_task_runner_ =
+      base::ThreadTaskRunnerHandle::Get();
   base::RunLoop runloop_;
   base::Closure quit_closure_;
 
   scoped_refptr<TestConfigurator> config_;
   scoped_refptr<MockUpdateClient> update_client_;
   std::unique_ptr<ComponentUpdateService> component_updater_;
+  ComponentUnpacker::Result result_;
 };
 
 DefaultComponentInstallerTest::DefaultComponentInstallerTest()
@@ -151,13 +180,14 @@
           base::test::ScopedTaskEnvironment::MainThreadType::UI) {
   quit_closure_ = runloop_.QuitClosure();
 
-  config_ = new TestConfigurator(
+  config_ = base::MakeRefCounted<TestConfigurator>(
       base::CreateSequencedTaskRunnerWithTraits({base::MayBlock()}),
       base::ThreadTaskRunnerHandle::Get());
 
-  update_client_ = new MockUpdateClient();
+  update_client_ = base::MakeRefCounted<MockUpdateClient>();
   EXPECT_CALL(update_client(), AddObserver(_)).Times(1);
-  component_updater_.reset(new CrxUpdateService(config_, update_client_));
+  component_updater_ =
+      base::MakeUnique<CrxUpdateService>(config_, update_client_);
 }
 
 DefaultComponentInstallerTest::~DefaultComponentInstallerTest() {
@@ -169,6 +199,25 @@
   runloop_.Run();
 }
 
+void DefaultComponentInstallerTest::Unpack(const base::FilePath& crx_path) {
+  auto component_unpacker = base::MakeRefCounted<ComponentUnpacker>(
+      std::vector<uint8_t>(std::begin(kSha256Hash), std::end(kSha256Hash)),
+      crx_path, nullptr, nullptr, config_->GetSequencedTaskRunner());
+  component_unpacker->Unpack(base::Bind(
+      &DefaultComponentInstallerTest::UnpackComplete, base::Unretained(this)));
+  RunThreads();
+}
+
+void DefaultComponentInstallerTest::UnpackComplete(
+    const ComponentUnpacker::Result& result) {
+  result_ = result;
+
+  EXPECT_EQ(update_client::UnpackerError::kNone, result_.error);
+  EXPECT_EQ(0, result_.extended_error);
+
+  main_thread_task_runner_->PostTask(FROM_HERE, quit_closure_);
+}
+
 }  // namespace
 
 // Tests that the component metadata is propagated from the default
@@ -205,10 +254,8 @@
   EXPECT_CALL(update_client(), GetCrxUpdateState(id, _)).Times(1);
   EXPECT_CALL(update_client(), Stop()).Times(1);
 
-  std::unique_ptr<ComponentInstallerTraits> traits(new FakeInstallerTraits());
-
-  DefaultComponentInstaller* installer =
-      new DefaultComponentInstaller(std::move(traits));
+  auto installer = base::MakeRefCounted<DefaultComponentInstaller>(
+      base::MakeUnique<FakeInstallerTraits>());
   installer->Register(component_updater(), base::Closure());
 
   RunThreads();
@@ -232,4 +279,55 @@
   EXPECT_TRUE(component.supports_group_policy_enable_component_updates);
 }
 
+// Tests that the unpack path is removed when the install succeeded.
+TEST_F(DefaultComponentInstallerTest, UnpackPathInstallSuccess) {
+  auto installer = base::MakeRefCounted<DefaultComponentInstaller>(
+      base::MakeUnique<FakeInstallerTraits>());
+
+  Unpack(test_file("jebgalgnebhfojomionfpkfelancnnkf.crx"));
+
+  const auto unpack_path = result().unpack_path;
+  EXPECT_TRUE(base::DirectoryExists(unpack_path));
+
+  const auto manifest = update_client::ReadManifest(unpack_path);
+
+  base::ScopedPathOverride scoped_path_override(DIR_COMPONENT_USER);
+  base::FilePath base_dir;
+  EXPECT_TRUE(PathService::Get(DIR_COMPONENT_USER, &base_dir));
+  base_dir = base_dir.Append(relative_install_dir);
+  EXPECT_TRUE(base::CreateDirectory(base_dir));
+  const auto result = installer->Install(*manifest, unpack_path);
+  EXPECT_EQ(0, result.error);
+  EXPECT_FALSE(base::PathExists(unpack_path));
+
+  EXPECT_CALL(update_client(), Stop()).Times(1);
+}
+
+// Tests that the unpack path is removed when the install failed.
+TEST_F(DefaultComponentInstallerTest, UnpackPathInstallError) {
+  auto installer = base::MakeRefCounted<DefaultComponentInstaller>(
+      base::MakeUnique<FakeInstallerTraits>());
+
+  Unpack(test_file("jebgalgnebhfojomionfpkfelancnnkf.crx"));
+
+  const auto unpack_path = result().unpack_path;
+  EXPECT_TRUE(base::DirectoryExists(unpack_path));
+
+  const auto manifest = update_client::ReadManifest(unpack_path);
+
+  // Test the precondition that DIR_COMPONENT_USER is not registered with
+  // the path service.
+  base::FilePath base_dir;
+  EXPECT_FALSE(PathService::Get(DIR_COMPONENT_USER, &base_dir));
+
+  // Calling |Install| fails since DIR_COMPONENT_USER does not exist.
+  const auto result = installer->Install(*manifest, unpack_path);
+  EXPECT_EQ(
+      static_cast<int>(update_client::InstallError::NO_DIR_COMPONENT_USER),
+      result.error);
+  EXPECT_FALSE(base::PathExists(unpack_path));
+
+  EXPECT_CALL(update_client(), Stop()).Times(1);
+}
+
 }  // namespace component_updater
diff --git a/components/favicon/core/BUILD.gn b/components/favicon/core/BUILD.gn
index b4d4a79e..ecccff0c 100644
--- a/components/favicon/core/BUILD.gn
+++ b/components/favicon/core/BUILD.gn
@@ -43,14 +43,6 @@
     "//ui/gfx",
     "//url",
   ]
-  if (!is_ios) {
-    deps += [ "//cc/paint" ]
-    sources += [
-      "fallback_icon_client.h",
-      "fallback_icon_service.cc",
-      "fallback_icon_service.h",
-    ]
-  }
 }
 
 source_set("unit_tests") {
diff --git a/components/favicon/core/fallback_icon_client.h b/components/favicon/core/fallback_icon_client.h
deleted file mode 100644
index 8efa28c2..0000000
--- a/components/favicon/core/fallback_icon_client.h
+++ /dev/null
@@ -1,28 +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 COMPONENTS_FAVICON_CORE_FALLBACK_ICON_CLIENT_H_
-#define COMPONENTS_FAVICON_CORE_FALLBACK_ICON_CLIENT_H_
-
-#include <string>
-#include <vector>
-
-#include "components/keyed_service/core/keyed_service.h"
-
-namespace favicon {
-
-// This class abstracts operations that depend on the embedder's environment,
-// e.g. Chrome.
-class FallbackIconClient : public KeyedService {
- public:
-  // Returns a list of font names for fallback icon rendering.
-  virtual const std::vector<std::string>& GetFontNameList() const = 0;
-
- protected:
-  ~FallbackIconClient() override {}
-};
-
-}  // namespace favicon
-
-#endif  // COMPONENTS_FAVICON_CORE_FALLBACK_ICON_CLIENT_H_
diff --git a/components/favicon/core/fallback_icon_service.cc b/components/favicon/core/fallback_icon_service.cc
deleted file mode 100644
index 291e1fa..0000000
--- a/components/favicon/core/fallback_icon_service.cc
+++ /dev/null
@@ -1,90 +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 "components/favicon/core/fallback_icon_service.h"
-
-#include <stddef.h>
-
-#include <algorithm>
-
-#include "cc/paint/skia_paint_canvas.h"
-#include "components/favicon/core/fallback_icon_client.h"
-#include "components/favicon/core/fallback_url_util.h"
-#include "components/favicon_base/fallback_icon_style.h"
-#include "third_party/skia/include/core/SkPaint.h"
-#include "ui/gfx/canvas.h"
-#include "ui/gfx/codec/png_codec.h"
-#include "ui/gfx/font_list.h"
-#include "ui/gfx/geometry/rect.h"
-#include "ui/gfx/geometry/size.h"
-#include "url/gurl.h"
-
-namespace favicon {
-namespace {
-
-// Arbitrary maximum icon size, can be reasonably increased if needed.
-const int kMaxFallbackFaviconSize = 288;
-
-}  // namespace
-
-FallbackIconService::FallbackIconService(
-    FallbackIconClient* fallback_icon_client)
-    : fallback_icon_client_(fallback_icon_client) {
-}
-
-FallbackIconService::~FallbackIconService() {
-}
-
-std::vector<unsigned char> FallbackIconService::RenderFallbackIconBitmap(
-    const GURL& icon_url,
-    int size,
-    const favicon_base::FallbackIconStyle& style) {
-  int size_to_use = std::min(kMaxFallbackFaviconSize, size);
-  SkBitmap bitmap;
-  bitmap.allocN32Pixels(size_to_use, size_to_use, false);
-  cc::SkiaPaintCanvas paint_canvas(bitmap);
-  gfx::Canvas canvas(&paint_canvas, 1.f);
-
-  DrawFallbackIcon(icon_url, size_to_use, style, &canvas);
-
-  std::vector<unsigned char> bitmap_data;
-  if (!gfx::PNGCodec::EncodeBGRASkBitmap(bitmap, false, &bitmap_data))
-    bitmap_data.clear();
-  return bitmap_data;
-}
-
-void FallbackIconService::DrawFallbackIcon(
-    const GURL& icon_url,
-    int size,
-    const favicon_base::FallbackIconStyle& style,
-    gfx::Canvas* canvas) {
-  const int kOffsetX = 0;
-  const int kOffsetY = 0;
-  cc::PaintFlags flags;
-  flags.setStyle(cc::PaintFlags::kFill_Style);
-  flags.setAntiAlias(true);
-
-  // Draw a filled, colored rounded square.
-  flags.setColor(style.background_color);
-  int corner_radius = static_cast<int>(size * style.roundness * 0.5 + 0.5);
-  canvas->DrawRoundRect(gfx::Rect(kOffsetX, kOffsetY, size, size),
-                        corner_radius, flags);
-
-  // Draw text.
-  base::string16 icon_text = GetFallbackIconText(icon_url);
-  if (icon_text.empty())
-    return;
-  int font_size = static_cast<int>(size * style.font_size_ratio);
-  if (font_size <= 0)
-    return;
-
-  canvas->DrawStringRectWithFlags(
-      icon_text,
-      gfx::FontList(fallback_icon_client_->GetFontNameList(), gfx::Font::NORMAL,
-                    font_size, gfx::Font::Weight::NORMAL),
-      style.text_color, gfx::Rect(kOffsetX, kOffsetY, size, size),
-      gfx::Canvas::TEXT_ALIGN_CENTER);
-}
-
-}  // namespace favicon
diff --git a/components/favicon/core/fallback_icon_service.h b/components/favicon/core/fallback_icon_service.h
deleted file mode 100644
index 56b155e..0000000
--- a/components/favicon/core/fallback_icon_service.h
+++ /dev/null
@@ -1,55 +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 COMPONENTS_FAVICON_CORE_FALLBACK_ICON_SERVICE_H_
-#define COMPONENTS_FAVICON_CORE_FALLBACK_ICON_SERVICE_H_
-
-#include <vector>
-
-#include "base/macros.h"
-#include "components/keyed_service/core/keyed_service.h"
-
-class GURL;
-
-namespace gfx {
-class Canvas;
-}
-
-namespace favicon_base {
-struct FallbackIconStyle;
-}
-
-namespace favicon {
-
-class FallbackIconClient;
-
-// A service to provide methods to render fallback favicons.
-class FallbackIconService : public KeyedService {
- public:
-  explicit FallbackIconService(FallbackIconClient* fallback_icon_client);
-  ~FallbackIconService() override;
-
-  // Renders a fallback icon synchronously and returns the bitmap. Returns an
-  // empty std::vector on failure. |size| is icon width and height in pixels.
-  std::vector<unsigned char> RenderFallbackIconBitmap(
-      const GURL& icon_url,
-      int size,
-      const favicon_base::FallbackIconStyle& style);
-
- private:
-  // Renders a fallback icon on |canvas| at position (|x|, |y|). |size| is icon
-  // width and height in pixels.
-  void DrawFallbackIcon(const GURL& icon_url,
-                        int size,
-                        const favicon_base::FallbackIconStyle& style,
-                        gfx::Canvas* canvas);
-
-  FallbackIconClient* fallback_icon_client_;
-
-  DISALLOW_COPY_AND_ASSIGN(FallbackIconService);
-};
-
-}  // namespace favicon
-
-#endif  // COMPONENTS_FAVICON_CORE_FALLBACK_ICON_SERVICE_H_
diff --git a/components/favicon_base/BUILD.gn b/components/favicon_base/BUILD.gn
index 3050e5ca..57e743e 100644
--- a/components/favicon_base/BUILD.gn
+++ b/components/favicon_base/BUILD.gn
@@ -6,8 +6,6 @@
   sources = [
     "fallback_icon_style.cc",
     "fallback_icon_style.h",
-    "fallback_icon_url_parser.cc",
-    "fallback_icon_url_parser.h",
     "favicon_callback.h",
     "favicon_types.cc",
     "favicon_types.h",
@@ -40,7 +38,6 @@
 source_set("unit_tests") {
   testonly = true
   sources = [
-    "fallback_icon_url_parser_unittest.cc",
     "favicon_url_parser_unittest.cc",
     "large_icon_url_parser_unittest.cc",
     "select_favicon_frames_unittest.cc",
diff --git a/components/favicon_base/fallback_icon_style.cc b/components/favicon_base/fallback_icon_style.cc
index cbb478b1..c85d768 100644
--- a/components/favicon_base/fallback_icon_style.cc
+++ b/components/favicon_base/fallback_icon_style.cc
@@ -13,10 +13,6 @@
 
 namespace {
 
-// Luma threshold for background color determine whether to use dark or light
-// text color.
-const uint8_t kDarkTextLumaThreshold = 190;
-
 // The maximum lightness of the background color to ensure light text is
 // readable.
 const double kMaxBackgroundColorLightness = 0.67;
@@ -24,20 +20,14 @@
 
 // Default values for FallbackIconStyle.
 const SkColor kDefaultBackgroundColor = SkColorSetRGB(0x78, 0x78, 0x78);
-const SkColor kDefaultTextColorDark = SK_ColorBLACK;
-const SkColor kDefaultTextColorLight = SK_ColorWHITE;
-const double kDefaultFontSizeRatio = 0.44;
-const double kDefaultRoundness = 0;  // Square. Round corners are applied
-                                     // externally (Javascript or Java).
+const SkColor kDefaultTextColor = SK_ColorWHITE;
 
 }  // namespace
 
 FallbackIconStyle::FallbackIconStyle()
     : background_color(kDefaultBackgroundColor),
       is_default_background_color(true),
-      text_color(kDefaultTextColorLight),
-      font_size_ratio(kDefaultFontSizeRatio),
-      roundness(kDefaultRoundness) {}
+      text_color(kDefaultTextColor) {}
 
 FallbackIconStyle::~FallbackIconStyle() {
 }
@@ -45,21 +35,7 @@
 bool FallbackIconStyle::operator==(const FallbackIconStyle& other) const {
   return background_color == other.background_color &&
          is_default_background_color == other.is_default_background_color &&
-         text_color == other.text_color &&
-         font_size_ratio == other.font_size_ratio &&
-         roundness == other.roundness;
-}
-
-void MatchFallbackIconTextColorAgainstBackgroundColor(
-    FallbackIconStyle* style) {
-  const uint8_t luma = color_utils::GetLuma(style->background_color);
-  style->text_color = (luma >= kDarkTextLumaThreshold) ?
-      kDefaultTextColorDark : kDefaultTextColorLight;
-}
-
-bool ValidateFallbackIconStyle(const FallbackIconStyle& style) {
-  return style.font_size_ratio >= 0.0 && style.font_size_ratio <= 1.0 &&
-      style.roundness >= 0.0 && style.roundness <= 1.0;
+         text_color == other.text_color;
 }
 
 void SetDominantColorAsBackground(
diff --git a/components/favicon_base/fallback_icon_style.h b/components/favicon_base/fallback_icon_style.h
index 9257730..0680d54 100644
--- a/components/favicon_base/fallback_icon_style.h
+++ b/components/favicon_base/fallback_icon_style.h
@@ -17,8 +17,6 @@
   FallbackIconStyle();
   ~FallbackIconStyle();
 
-  // If any member changes, also update FallbackIconStyleBuilder.
-
   // Icon background fill color.
   SkColor background_color;
   bool is_default_background_color;
@@ -26,21 +24,9 @@
   // Icon text color.
   SkColor text_color;
 
-  // Ratio in [0.0, 1.0] of the text font size (pixels) to the icon size.
-  double font_size_ratio;
-
-  // The roundness of the icon's corners. 0 => square icon, 1 => circle icon.
-  double roundness;
-
   bool operator==(const FallbackIconStyle& other) const;
 };
 
-// Reassigns |style|'s |text_color| to matches well against |background_color|.
-void MatchFallbackIconTextColorAgainstBackgroundColor(FallbackIconStyle* style);
-
-// Returns whether |style| values are within bounds.
-bool ValidateFallbackIconStyle(const FallbackIconStyle& style);
-
 // Set |style|'s background color to the dominant color of |bitmap_data|,
 // clamping luminance down to a reasonable maximum value so that light text is
 // readable.
diff --git a/components/favicon_base/fallback_icon_url_parser.cc b/components/favicon_base/fallback_icon_url_parser.cc
deleted file mode 100644
index 214c88ff9..0000000
--- a/components/favicon_base/fallback_icon_url_parser.cc
+++ /dev/null
@@ -1,142 +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 "components/favicon_base/fallback_icon_url_parser.h"
-
-#include <algorithm>
-
-#include "base/logging.h"
-#include "base/macros.h"
-#include "base/strings/string_number_conversions.h"
-#include "base/strings/string_split.h"
-#include "base/strings/string_util.h"
-#include "third_party/skia/include/utils/SkParse.h"
-#include "ui/gfx/favicon_size.h"
-
-namespace {
-
-// List of sizes corresponding to RGB, ARGB, RRGGBB, AARRGGBB.
-const size_t kValidHexColorSizes[] = {3, 4, 6, 8};
-
-// Returns whether |color_str| is a valid CSS color in hex format if we prepend
-// '#', i.e., whether |color_str| matches /^[0-9A-Fa-f]{3,4,6,8}$/.
-bool IsHexColorString(const std::string& color_str) {
-  size_t len = color_str.length();
-  const size_t* end = kValidHexColorSizes + arraysize(kValidHexColorSizes);
-  if (std::find(kValidHexColorSizes, end, len) == end)
-    return false;
-  for (auto ch : color_str) {
-    if (!base::IsHexDigit(ch))
-      return false;
-  }
-  return true;
-}
-
-}  // namespace
-
-namespace chrome {
-
-ParsedFallbackIconPath::ParsedFallbackIconPath()
-    : size_in_pixels_(gfx::kFaviconSize) {
-}
-
-ParsedFallbackIconPath::~ParsedFallbackIconPath() {
-}
-
-bool ParsedFallbackIconPath::Parse(const std::string& path) {
-  if (path.empty())
-    return false;
-
-  size_t slash = path.find("/", 0);
-  if (slash == std::string::npos)
-    return false;
-  std::string spec_str = path.substr(0, slash);
-  if (!ParseSpecs(spec_str, &size_in_pixels_, &style_))
-    return false;  // Parse failed.
-
-  // Need to store the index of the URL field, so Instant Extended can translate
-  // fallback icon URLs using advanced parameters.
-  // Example:
-  //   "chrome-search://fallback-icon/48/<renderer-id>/<most-visited-id>"
-  // would be translated to:
-  //   "chrome-search://fallback-icon/48/<most-visited-item-with-given-id>".
-  path_index_ = slash + 1;
-  url_string_ = path.substr(path_index_);
-  return true;
-}
-
-// static
-bool ParsedFallbackIconPath::ParseSpecs(
-    const std::string& specs_str,
-    int *size,
-    favicon_base::FallbackIconStyle* style) {
-  DCHECK(size);
-  DCHECK(style);
-
-  std::vector<std::string> tokens = base::SplitString(
-      specs_str, ",", base::KEEP_WHITESPACE, base::SPLIT_WANT_ALL);
-  if (tokens.size() != 5)  // Force "," for empty fields.
-    return false;
-
-  *size = gfx::kFaviconSize;
-  if (!tokens[0].empty() && !base::StringToInt(tokens[0], size))
-    return false;
-  if (*size <= 0)
-    return false;
-
-  *style = favicon_base::FallbackIconStyle();
-
-  if (!tokens[1].empty()) {
-    style->is_default_background_color = false;
-    if (!ParseColor(tokens[1], &style->background_color))
-      return false;
-  }
-
-  if (tokens[2].empty())
-    favicon_base::MatchFallbackIconTextColorAgainstBackgroundColor(style);
-  else if (!ParseColor(tokens[2], &style->text_color))
-    return false;
-
-  if (!tokens[3].empty() &&
-      !base::StringToDouble(tokens[3], &style->font_size_ratio))
-    return false;
-
-  if (!tokens[4].empty() && !base::StringToDouble(tokens[4], &style->roundness))
-    return false;
-
-  return favicon_base::ValidateFallbackIconStyle(*style);
-}
-
-// static
-bool ParsedFallbackIconPath::ParseColor(const std::string& color_str,
-                                        SkColor* color) {
-  DCHECK(color);
-  // Exclude the empty case. Also disallow the '#' prefix, since we want color
-  // to be part of an URL, but in URL '#' is used for ref fragment.
-  if (color_str.empty() || color_str[0] == '#')
-    return false;
-
-  // If a valid color hex string is given, prepend '#' and parse (always works).
-  // This is unambiguous since named color never only use leters 'a' to 'f'.
-  if (IsHexColorString(color_str)) {
-    // Default alpha to 0xFF since FindColor() preserves unspecified alpha.
-    *color = SK_ColorWHITE;
-    // Need temp variable to avoid use-after-free of returned pointer.
-    std::string color_str_with_hash = "#" + color_str;
-    const char* end = SkParse::FindColor(color_str_with_hash.c_str(), color);
-    DCHECK(end && !*end);  // Call should succeed and consume string.
-    return true;
-  }
-
-  // Default alpha to 0xFF.
-  SkColor temp_color = SK_ColorWHITE;
-  const char* end = SkParse::FindColor(color_str.c_str(), &temp_color);
-  if (end && !*end) {  // Successful if call succeeds and string is consumed.
-    *color = temp_color;
-    return true;
-  }
-  return false;
-}
-
-}  // namespace chrome
diff --git a/components/favicon_base/fallback_icon_url_parser.h b/components/favicon_base/fallback_icon_url_parser.h
deleted file mode 100644
index 39a79fc3..0000000
--- a/components/favicon_base/fallback_icon_url_parser.h
+++ /dev/null
@@ -1,74 +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 COMPONENTS_FAVICON_BASE_FALLBACK_ICON_URL_PARSER_H_
-#define COMPONENTS_FAVICON_BASE_FALLBACK_ICON_URL_PARSER_H_
-
-#include <stddef.h>
-
-#include <string>
-
-#include "base/gtest_prod_util.h"
-#include "base/macros.h"
-#include "components/favicon_base/fallback_icon_style.h"
-#include "third_party/skia/include/core/SkColor.h"
-
-namespace chrome {
-
-class ParsedFallbackIconPath {
- public:
-  ParsedFallbackIconPath();
-  ~ParsedFallbackIconPath();
-
-  const std::string& url_string() const { return url_string_; }
-
-  int size_in_pixels() const { return size_in_pixels_; }
-
-  const favicon_base::FallbackIconStyle& style() const { return style_; }
-
-  size_t path_index() const { return path_index_; }
-
-  // Parses |path|, which should be in the format described at the top of the
-  // file "chrome/browser/ui/webui/fallback_icon_source.h".
-  bool Parse(const std::string& path);
-
- private:
-  // Parses |specs_str|, which should be the comma-separated value portion
-  // in the format described at the top of the file
-  // "chrome/browser/ui/webui/fallback_icon_source.h".
-  static bool ParseSpecs(const std::string& specs_str,
-                         int *size,
-                         favicon_base::FallbackIconStyle* style);
-
-  // Helper to parse color string (e.g., "red", "#f00", "#aB0137"). On success,
-  // returns true and writes te result to |*color|.
-  static bool ParseColor(const std::string& color_str, SkColor* color);
-
-  friend class FallbackIconUrlParserTest;
-  FRIEND_TEST_ALL_PREFIXES(FallbackIconUrlParserTest, ParseColorSuccess);
-  FRIEND_TEST_ALL_PREFIXES(FallbackIconUrlParserTest, ParseColorFailure);
-  FRIEND_TEST_ALL_PREFIXES(FallbackIconUrlParserTest, ParseSpecsEmpty);
-  FRIEND_TEST_ALL_PREFIXES(FallbackIconUrlParserTest, ParseSpecsPartial);
-  FRIEND_TEST_ALL_PREFIXES(FallbackIconUrlParserTest, ParseSpecsFull);
-  FRIEND_TEST_ALL_PREFIXES(FallbackIconUrlParserTest, ParseSpecsFailure);
-
-  // The page URL string the fallback icon is requested for.
-  std::string url_string_;
-
-  // The size of the requested fallback icon in pixels.
-  int size_in_pixels_;
-
-  // Styling specifications of fallback icon.
-  favicon_base::FallbackIconStyle style_;
-
-  // The index of the first character (relative to the path) where the the URL
-  // from which the fallback icon is being requested is located.
-  size_t path_index_;
-
-  DISALLOW_COPY_AND_ASSIGN(ParsedFallbackIconPath);
-};
-
-}  // namespace chrome
-
-#endif  // COMPONENTS_FAVICON_BASE_FALLBACK_ICON_URL_PARSER_H_
diff --git a/components/favicon_base/fallback_icon_url_parser_unittest.cc b/components/favicon_base/fallback_icon_url_parser_unittest.cc
deleted file mode 100644
index 4dcb236a..0000000
--- a/components/favicon_base/fallback_icon_url_parser_unittest.cc
+++ /dev/null
@@ -1,294 +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 "components/favicon_base/fallback_icon_url_parser.h"
-
-#include <stddef.h>
-
-#include "base/macros.h"
-#include "components/favicon_base/fallback_icon_style.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/skia/include/core/SkColor.h"
-#include "ui/gfx/favicon_size.h"
-#include "url/gurl.h"
-
-using chrome::ParsedFallbackIconPath;
-using favicon_base::FallbackIconStyle;
-
-namespace chrome {
-
-namespace {
-
-// Default values for FallbackIconStyle, from
-// /components/favicon_base/fallback_icon_style.h
-const SkColor kDefaultBackgroundColor = SkColorSetRGB(0x78, 0x78, 0x78);
-const SkColor kDefaultTextColorDark = SK_ColorBLACK;
-const SkColor kDefaultTextColorLight = SK_ColorWHITE;
-const double kDefaultFontSizeRatio = 0.44;
-const double kDefaultRoundness = 0;
-
-const char kTestUrlStr[] = "https://www.google.ca/imghp?hl=en&tab=wi";
-
-}  // namespace
-
-class FallbackIconUrlParserTest : public testing::Test {
- public:
-  FallbackIconUrlParserTest() {
-  }
-
-  bool ParseSpecs(const std::string& specs_str,
-                  int *size,
-                  favicon_base::FallbackIconStyle* style) {
-    return ParsedFallbackIconPath::ParseSpecs(specs_str, size, style);
-  }
-
-  bool ParseColor(const std::string& color_str, SkColor* color) {
-    return ParsedFallbackIconPath::ParseColor(color_str, color);
-  }
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(FallbackIconUrlParserTest);
-};
-
-TEST_F(FallbackIconUrlParserTest, ParseColorSuccess) {
-  SkColor c;
-  EXPECT_TRUE(ParseColor("31aBf0f4", &c));
-  EXPECT_EQ(SkColorSetARGB(0x31, 0xAB, 0xF0, 0xF4), c);
-  EXPECT_TRUE(ParseColor("01aBf0", &c));
-  EXPECT_EQ(SkColorSetRGB(0x01, 0xAB, 0xF0), c);
-  EXPECT_TRUE(ParseColor("501a", &c));
-  EXPECT_EQ(SkColorSetARGB(0x55, 0x00, 0x11, 0xAA), c);
-  EXPECT_TRUE(ParseColor("01a", &c));
-  EXPECT_EQ(SkColorSetRGB(0x00, 0x11, 0xAA), c);
-  EXPECT_TRUE(ParseColor("000000", &c));
-  EXPECT_EQ(SkColorSetARGB(0xFF, 0x00, 0x00, 0x00), c);
-  EXPECT_TRUE(ParseColor("red", &c));
-  EXPECT_EQ(SkColorSetARGB(0xFF, 0xFF, 0x00, 0x00), c);
-}
-
-TEST_F(FallbackIconUrlParserTest, ParseColorFailure) {
-  const char* test_cases[] = {
-    "",
-    "00000",
-    "000000000",
-    " 000000",
-    "ABCDEFG",
-    "#000",
-    "#000000",
-    "000000 ",
-    "ABCDEFH",
-    "#ABCDEF",
-  };
-  for (size_t i = 0; i < arraysize(test_cases); ++i) {
-    SkColor c;
-    EXPECT_FALSE(ParseColor(test_cases[i], &c))
-         << "test_cases[" << i << "]";
-  }
-}
-
-TEST_F(FallbackIconUrlParserTest, ParseSpecsEmpty) {
-  int size;
-  FallbackIconStyle style;
-  EXPECT_TRUE(ParseSpecs(",,,,", &size, &style));
-  EXPECT_EQ(16, size);
-  EXPECT_EQ(kDefaultBackgroundColor, style.background_color);
-  EXPECT_TRUE(style.is_default_background_color);
-  EXPECT_EQ(kDefaultTextColorLight, style.text_color);
-  EXPECT_EQ(kDefaultFontSizeRatio, style.font_size_ratio);
-  EXPECT_EQ(kDefaultRoundness, style.roundness);
-}
-
-TEST_F(FallbackIconUrlParserTest, ParseSpecsPartial) {
-  int size;
-  FallbackIconStyle style;
-  EXPECT_TRUE(ParseSpecs(",,aCE,,0.1", &size, &style));
-  EXPECT_EQ(16, size);
-  EXPECT_EQ(kDefaultBackgroundColor, style.background_color);
-  EXPECT_TRUE(style.is_default_background_color);
-  EXPECT_EQ(SkColorSetRGB(0xAA, 0xCC, 0xEE), style.text_color);
-  EXPECT_EQ(kDefaultFontSizeRatio, style.font_size_ratio);
-  EXPECT_EQ(0.1, style.roundness);
-}
-
-TEST_F(FallbackIconUrlParserTest, ParseSpecsFull) {
-  int size;
-
-  {
-    FallbackIconStyle style;
-    EXPECT_TRUE(ParseSpecs("16,000,f01,0.75,0.25", &size, &style));
-    EXPECT_EQ(16, size);
-    EXPECT_EQ(SkColorSetRGB(0x00, 0x00, 0x00), style.background_color);
-    EXPECT_FALSE(style.is_default_background_color);
-    EXPECT_EQ(SkColorSetRGB(0xff, 0x00, 0x11), style.text_color);
-    EXPECT_EQ(0.75, style.font_size_ratio);
-    EXPECT_EQ(0.25, style.roundness);
-  }
-
-  {
-    FallbackIconStyle style;
-    EXPECT_TRUE(ParseSpecs("48,black,123456,0.5,0.3", &size, &style));
-    EXPECT_EQ(48, size);
-    EXPECT_EQ(SkColorSetRGB(0x00, 0x00, 0x00), style.background_color);
-    EXPECT_FALSE(style.is_default_background_color);
-    EXPECT_EQ(SkColorSetRGB(0x12, 0x34, 0x56), style.text_color);
-    EXPECT_EQ(0.5, style.font_size_ratio);
-    EXPECT_EQ(0.3, style.roundness);
-  }
-
-  {
-    FallbackIconStyle style;
-    EXPECT_TRUE(ParseSpecs("1,000,red,0,0", &size, &style));
-    EXPECT_EQ(1, size);
-    EXPECT_EQ(SkColorSetRGB(0x00, 0x00, 0x00), style.background_color);
-    EXPECT_FALSE(style.is_default_background_color);
-    EXPECT_EQ(SkColorSetRGB(0xFF, 0x00, 0x00), style.text_color);
-    EXPECT_EQ(0, style.font_size_ratio);
-    EXPECT_EQ(0, style.roundness);
-  }
-}
-
-TEST_F(FallbackIconUrlParserTest, ParseSpecsDefaultTextColor) {
-  int size;
-
-  {
-    // Dark background -> Light text.
-    FallbackIconStyle style;
-    EXPECT_TRUE(ParseSpecs(",000,,,", &size, &style));
-    EXPECT_EQ(kDefaultTextColorLight, style.text_color);
-  }
-
-  {
-    // Light background -> Dark text.
-    FallbackIconStyle style;
-    EXPECT_TRUE(ParseSpecs(",fff,,,", &size, &style));
-    EXPECT_EQ(kDefaultTextColorDark, style.text_color);
-  }
-
-  {
-    // Light background -> Dark text, more params don't matter.
-    FallbackIconStyle style;
-    EXPECT_TRUE(ParseSpecs("107,fff,,0.3,0.5", &size, &style));
-    EXPECT_EQ(kDefaultTextColorDark, style.text_color);
-  }
-}
-
-TEST_F(FallbackIconUrlParserTest, ParseSpecsFailure) {
-  const char* test_cases[] = {
-    // Need exactly 5 params.
-    "",
-    "16",
-    "16,black",
-    "16,black,fff",
-    "16,black,fff,0.75",
-    ",,,"
-    ",,,,,",
-    "16,black,fff,0.75,0.25,junk",
-    // Don't allow any space.
-    "16,black,fff, 0.75,0.25",
-    "16,black ,fff,0.75,0.25",
-    "16,black,fff,0.75,0.25 ",
-    // Adding junk text.
-    "16,black,fff,0.75,0.25junk",
-    "junk,black,fff,0.75,0.25",
-    "16,#junk,fff,0.75,0.25",
-    "16,black,junk,0.75,0.25",
-    "16,black,fff,junk,0.25",
-    "16,black,fff,0.75,junk",
-    // Out of bound.
-    "0,black,fff,0.75,0.25",  // size.
-    "4294967296,black,fff,0.75,0.25",  // size.
-    "-1,black,fff,0.75,0.25",  // size.
-    "16,black,fff,-0.1,0.25",  // font_size_ratio.
-    "16,black,fff,1.1,0.25",  // font_size_ratio.
-    "16,black,fff,0.75,-0.1",  // roundness.
-    "16,black,fff,0.75,1.1",  // roundness.
-  };
-  for (size_t i = 0; i < arraysize(test_cases); ++i) {
-    int size;
-    FallbackIconStyle style;
-    EXPECT_FALSE(ParseSpecs(test_cases[i], &size, &style))
-        << "test_cases[" << i << "]";
-
-  }
-}
-
-TEST_F(FallbackIconUrlParserTest, ParseFallbackIconPathSuccess) {
-  const std::string specs = "31,black,fff,0.75,0.25";
-
-  // Everything populated.
-  {
-    chrome::ParsedFallbackIconPath parsed;
-    EXPECT_TRUE(parsed.Parse(specs + "/" + kTestUrlStr));
-    EXPECT_EQ(31, parsed.size_in_pixels());
-    const favicon_base::FallbackIconStyle& style = parsed.style();
-    EXPECT_EQ(SkColorSetRGB(0x00, 0x00, 0x00), style.background_color);
-    EXPECT_FALSE(style.is_default_background_color);
-    EXPECT_EQ(SkColorSetRGB(0xFF, 0xFF, 0xFF), style.text_color);
-    EXPECT_EQ(0.75, style.font_size_ratio);
-    EXPECT_EQ(0.25, style.roundness);
-    EXPECT_EQ(GURL(kTestUrlStr), GURL(parsed.url_string()));
-    EXPECT_EQ(specs.length() + 1, parsed.path_index());
-  }
-
-  // Empty URL.
-  {
-    chrome::ParsedFallbackIconPath parsed;
-    EXPECT_TRUE(parsed.Parse(specs + "/"));
-    EXPECT_EQ(31, parsed.size_in_pixels());
-    const favicon_base::FallbackIconStyle& style = parsed.style();
-    EXPECT_EQ(SkColorSetRGB(0x00, 0x00, 0x00), style.background_color);
-    EXPECT_FALSE(style.is_default_background_color);
-    EXPECT_EQ(SkColorSetRGB(0xFF, 0xFF, 0xFF), style.text_color);
-    EXPECT_EQ(0.75, style.font_size_ratio);
-    EXPECT_EQ(0.25, style.roundness);
-    EXPECT_EQ(GURL(), GURL(parsed.url_string()));
-    EXPECT_EQ(specs.length() + 1, parsed.path_index());
-  }
-
-  // Tolerate invalid URL.
-  {
-    chrome::ParsedFallbackIconPath parsed;
-    EXPECT_TRUE(parsed.Parse(specs + "/NOT A VALID URL"));
-    EXPECT_EQ(31, parsed.size_in_pixels());
-    const favicon_base::FallbackIconStyle& style = parsed.style();
-    EXPECT_EQ(SkColorSetRGB(0x00, 0x00, 0x00), style.background_color);
-    EXPECT_FALSE(style.is_default_background_color);
-    EXPECT_EQ(SkColorSetRGB(0xFF, 0xFF, 0xFF), style.text_color);
-    EXPECT_EQ(0.75, style.font_size_ratio);
-    EXPECT_EQ(0.25, style.roundness);
-    EXPECT_EQ("NOT A VALID URL", parsed.url_string());
-    EXPECT_EQ(specs.length() + 1, parsed.path_index());
-  }
-
-  // Size and style are default.
-  {
-    std::string specs2 = ",,,,";
-    chrome::ParsedFallbackIconPath parsed;
-    EXPECT_TRUE(parsed.Parse(specs2 + "/" + kTestUrlStr));
-    EXPECT_EQ(gfx::kFaviconSize, parsed.size_in_pixels());
-    const favicon_base::FallbackIconStyle& style = parsed.style();
-    EXPECT_EQ(kDefaultBackgroundColor, style.background_color);
-    EXPECT_TRUE(style.is_default_background_color);
-    EXPECT_EQ(kDefaultTextColorLight, style.text_color);
-    EXPECT_EQ(kDefaultFontSizeRatio, style.font_size_ratio);
-    EXPECT_EQ(kDefaultRoundness, style.roundness);
-    EXPECT_EQ(GURL(kTestUrlStr), GURL(parsed.url_string()));
-    EXPECT_EQ(specs2.length() + 1, parsed.path_index());
-  }
-}
-
-TEST_F(FallbackIconUrlParserTest, ParseFallbackIconPathFailure) {
-  const char* test_cases[] = {
-    // Bad size.
-    "-1,000,fff,0.75,0.25/http://www.google.com/",
-    // Bad specs.
-    "32,#junk,fff,0.75,0.25/http://www.google.com/",
-  };
-  for (size_t i = 0; i < arraysize(test_cases); ++i) {
-    chrome::ParsedFallbackIconPath parsed;
-    EXPECT_FALSE(parsed.Parse(test_cases[i])) << "test_cases[" << i << "]";
-  }
-}
-
-}  // namespace chrome
diff --git a/components/nacl/loader/BUILD.gn b/components/nacl/loader/BUILD.gn
index 74edc91..1eea0cea 100644
--- a/components/nacl/loader/BUILD.gn
+++ b/components/nacl/loader/BUILD.gn
@@ -120,7 +120,7 @@
     deps = [
       ":loader",
       "//base",
-      "//build/config/sanitizers:deps_no_options",
+      "//build/config:exe_and_shlib_deps",
       "//components/nacl/common:switches",
       "//components/nacl/loader/sandbox_linux",
       "//content/public/common",
diff --git a/components/nacl/loader/nacl_helper_linux.cc b/components/nacl/loader/nacl_helper_linux.cc
index df24f6d..fbce5f9 100644
--- a/components/nacl/loader/nacl_helper_linux.cc
+++ b/components/nacl/loader/nacl_helper_linux.cc
@@ -411,21 +411,8 @@
 // Do not install the SIGSEGV handler in ASan. This should make the NaCl
 // platform qualification test pass.
 // detect_odr_violation=0: http://crbug.com/376306
-static const char kAsanDefaultOptionsNaCl[] =
-    "handle_segv=0:detect_odr_violation=0";
-
-// Override the default ASan options for the NaCl helper.
-// __asan_default_options should not be instrumented, because it is called
-// before ASan is initialized.
-extern "C"
-__attribute__((no_sanitize_address))
-// The function isn't referenced from the executable itself. Make sure it isn't
-// stripped by the linker.
-__attribute__((used))
-__attribute__((visibility("default")))
-const char* __asan_default_options() {
-  return kAsanDefaultOptionsNaCl;
-}
+extern const char* kAsanDefaultOptionsNaCl;
+const char* kAsanDefaultOptionsNaCl = "handle_segv=0:detect_odr_violation=0";
 #endif
 
 int main(int argc, char* argv[]) {
diff --git a/components/omnibox/browser/omnibox_field_trial.cc b/components/omnibox/browser/omnibox_field_trial.cc
index f736254..1e40681 100644
--- a/components/omnibox/browser/omnibox_field_trial.cc
+++ b/components/omnibox/browser/omnibox_field_trial.cc
@@ -87,6 +87,10 @@
     base::FEATURE_DISABLED_BY_DEFAULT};
 
 // Feature used for the vertical margin UI experiment.
+const base::Feature kUIExperimentVerticalLayout{
+    "OmniboxUIExperimentVerticalLayout", base::FEATURE_DISABLED_BY_DEFAULT};
+
+// Feature used for the vertical margin UI experiment.
 const base::Feature kUIExperimentVerticalMargin{
     "OmniboxUIExperimentVerticalMargin", base::FEATURE_DISABLED_BY_DEFAULT};
 
diff --git a/components/omnibox/browser/omnibox_field_trial.h b/components/omnibox/browser/omnibox_field_trial.h
index 5bee37a..2033183 100644
--- a/components/omnibox/browser/omnibox_field_trial.h
+++ b/components/omnibox/browser/omnibox_field_trial.h
@@ -33,6 +33,7 @@
 extern const base::Feature kZeroSuggestSwapTitleAndUrl;
 extern const base::Feature kDisplayTitleForCurrentUrl;
 extern const base::Feature kUIExperimentMaxAutocompleteMatches;
+extern const base::Feature kUIExperimentVerticalLayout;
 extern const base::Feature kUIExperimentVerticalMargin;
 }
 
diff --git a/components/payments/core/autofill_payment_instrument.cc b/components/payments/core/autofill_payment_instrument.cc
index 75c4484..82ff24d 100644
--- a/components/payments/core/autofill_payment_instrument.cc
+++ b/components/payments/core/autofill_payment_instrument.cc
@@ -78,9 +78,11 @@
 }
 
 bool AutofillPaymentInstrument::IsCompleteForPayment() {
+  // COMPLETE or EXPIRED cards are considered valid for payment. The user will
+  // be prompted to enter the new expiration at the CVC step.
   return autofill::GetCompletionStatusForCard(credit_card_, app_locale_,
-                                              billing_profiles_) ==
-         autofill::CREDIT_CARD_COMPLETE;
+                                              billing_profiles_) <=
+         autofill::CREDIT_CARD_EXPIRED;
 }
 
 base::string16 AutofillPaymentInstrument::GetMissingInfoLabel() {
diff --git a/components/payments/core/autofill_payment_instrument_unittest.cc b/components/payments/core/autofill_payment_instrument_unittest.cc
index 6b09937..7f84726 100644
--- a/components/payments/core/autofill_payment_instrument_unittest.cc
+++ b/components/payments/core/autofill_payment_instrument_unittest.cc
@@ -170,16 +170,14 @@
   EXPECT_TRUE(instrument.GetMissingInfoLabel().empty());
 }
 
-// An expired local card is not a valid instrument for payment.
+// An expired local card is still a valid instrument for payment.
 TEST_F(AutofillPaymentInstrumentTest, IsCompleteForPayment_Expired) {
   autofill::CreditCard& card = local_credit_card();
   card.SetExpirationYear(2016);  // Expired.
   AutofillPaymentInstrument instrument("visa", card, billing_profiles(),
                                        "en-US", nullptr);
-  EXPECT_FALSE(instrument.IsCompleteForPayment());
-  EXPECT_EQ(l10n_util::GetStringUTF16(
-                IDS_PAYMENTS_VALIDATION_INVALID_CREDIT_CARD_EXPIRED),
-            instrument.GetMissingInfoLabel());
+  EXPECT_TRUE(instrument.IsCompleteForPayment());
+  EXPECT_EQ(base::string16(), instrument.GetMissingInfoLabel());
 }
 
 // A local card with no name is not a valid instrument for payment.
@@ -263,7 +261,7 @@
   EXPECT_TRUE(instrument.GetMissingInfoLabel().empty());
 }
 
-// An expired masked (server) card is not a valid instrument for payment.
+// An expired masked (server) card is still a valid instrument for payment.
 TEST_F(AutofillPaymentInstrumentTest, IsCompleteForPayment_ExpiredMaskedCard) {
   autofill::CreditCard card = autofill::test::GetMaskedServerCard();
   ASSERT_GT(billing_profiles().size(), 0UL);
@@ -271,10 +269,8 @@
   card.SetExpirationYear(2016);  // Expired.
   AutofillPaymentInstrument instrument("visa", card, billing_profiles(),
                                        "en-US", nullptr);
-  EXPECT_FALSE(instrument.IsCompleteForPayment());
-  EXPECT_EQ(l10n_util::GetStringUTF16(
-                IDS_PAYMENTS_VALIDATION_INVALID_CREDIT_CARD_EXPIRED),
-            instrument.GetMissingInfoLabel());
+  EXPECT_TRUE(instrument.IsCompleteForPayment());
+  EXPECT_EQ(base::string16(), instrument.GetMissingInfoLabel());
 }
 
 // An expired card is a valid instrument for canMakePayment.
diff --git a/components/user_manager/user.h b/components/user_manager/user.h
index fecc33be..032e9841 100644
--- a/components/user_manager/user.h
+++ b/components/user_manager/user.h
@@ -190,6 +190,10 @@
   // True if the user Profile is created.
   bool is_profile_created() const { return profile_is_created_; }
 
+  static User* CreatePublicAccountUserForTesting(const AccountId& account_id) {
+    return CreatePublicAccountUser(account_id);
+  }
+
  protected:
   friend class UserManagerBase;
   friend class chromeos::ChromeUserManagerImpl;
diff --git a/components/user_manager/user_manager_base.h b/components/user_manager/user_manager_base.h
index 70776a72..2e44e71 100644
--- a/components/user_manager/user_manager_base.h
+++ b/components/user_manager/user_manager_base.h
@@ -132,6 +132,10 @@
   // and ephemeral users are enabled.
   virtual bool AreEphemeralUsersEnabled() const = 0;
 
+  void AddUserRecordForTesting(User* user) {
+    return AddUserRecord(user);
+  }
+
  protected:
   // Adds |user| to users list, and adds it to front of LRU list. It is assumed
   // that there is no user with same id.
diff --git a/components/viz/frame_sinks/gpu_root_compositor_frame_sink.cc b/components/viz/frame_sinks/gpu_root_compositor_frame_sink.cc
index 393f53f..9156032b 100644
--- a/components/viz/frame_sinks/gpu_root_compositor_frame_sink.cc
+++ b/components/viz/frame_sinks/gpu_root_compositor_frame_sink.cc
@@ -43,7 +43,6 @@
       base::Bind(&GpuRootCompositorFrameSink::OnPrivateConnectionLost,
                  base::Unretained(this)));
   display_->Initialize(this, surface_manager);
-  display_->SetVisible(true);
 }
 
 GpuRootCompositorFrameSink::~GpuRootCompositorFrameSink() = default;
diff --git a/content/browser/BUILD.gn b/content/browser/BUILD.gn
index eb28c584..501316c 100644
--- a/content/browser/BUILD.gn
+++ b/content/browser/BUILD.gn
@@ -692,6 +692,8 @@
     "frame_host/frame_tree_node.h",
     "frame_host/frame_tree_node_blame_context.cc",
     "frame_host/frame_tree_node_blame_context.h",
+    "frame_host/input/legacy_ipc_frame_input_handler.cc",
+    "frame_host/input/legacy_ipc_frame_input_handler.h",
     "frame_host/interstitial_page_impl.cc",
     "frame_host/interstitial_page_impl.h",
     "frame_host/interstitial_page_navigator_impl.cc",
diff --git a/content/browser/android/ime_adapter_android.cc b/content/browser/android/ime_adapter_android.cc
index bbb0ec9..2b5904cf 100644
--- a/content/browser/android/ime_adapter_android.cc
+++ b/content/browser/android/ime_adapter_android.cc
@@ -293,8 +293,7 @@
   if (!rfh)
     return;
 
-  rfh->Send(new InputMsg_SetEditableSelectionOffsets(rfh->GetRoutingID(), start,
-                                                     end));
+  rfh->GetFrameInputHandler()->SetEditableSelectionOffsets(start, end);
 }
 
 void ImeAdapterAndroid::SetCharacterBounds(
@@ -328,12 +327,12 @@
   if (!rfh)
     return;
 
-  std::vector<blink::WebCompositionUnderline> underlines;
-  underlines.push_back(blink::WebCompositionUnderline(
-      0, end - start, SK_ColorBLACK, false, SK_ColorTRANSPARENT));
+  std::vector<ui::CompositionUnderline> underlines;
+  underlines.push_back(ui::CompositionUnderline(0, end - start, SK_ColorBLACK,
+                                                false, SK_ColorTRANSPARENT));
 
-  rfh->Send(new InputMsg_SetCompositionFromExistingText(
-      rfh->GetRoutingID(), start, end, underlines));
+  rfh->GetFrameInputHandler()->SetCompositionFromExistingText(start, end,
+                                                              underlines);
 }
 
 void ImeAdapterAndroid::DeleteSurroundingText(JNIEnv*,
@@ -343,7 +342,7 @@
   RenderFrameHostImpl* rfh =
       static_cast<RenderFrameHostImpl*>(GetFocusedFrame());
   if (rfh)
-    rfh->DeleteSurroundingText(before, after);
+    rfh->GetFrameInputHandler()->DeleteSurroundingText(before, after);
 }
 
 void ImeAdapterAndroid::DeleteSurroundingTextInCodePoints(
@@ -353,8 +352,10 @@
     int after) {
   RenderFrameHostImpl* rfh =
       static_cast<RenderFrameHostImpl*>(GetFocusedFrame());
-  if (rfh)
-    rfh->DeleteSurroundingTextInCodePoints(before, after);
+  if (rfh) {
+    rfh->GetFrameInputHandler()->DeleteSurroundingTextInCodePoints(before,
+                                                                   after);
+  }
 }
 
 bool ImeAdapterAndroid::RequestTextInputStateUpdate(
diff --git a/content/browser/android/render_widget_host_connector_browsertest.cc b/content/browser/android/render_widget_host_connector_browsertest.cc
index e1840ff7..fc9f24b 100644
--- a/content/browser/android/render_widget_host_connector_browsertest.cc
+++ b/content/browser/android/render_widget_host_connector_browsertest.cc
@@ -119,18 +119,4 @@
   EXPECT_EQ(nullptr, connector->GetRWHVAForTesting());
 }
 
-IN_PROC_BROWSER_TEST_F(RenderWidgetHostConnectorTest,
-                       CleanUpConnectorReferenceAtWebContentsDestroyed) {
-  GURL http_url(embedded_test_server()->GetURL("/title1.html"));
-
-  EXPECT_TRUE(NavigateToURL(shell(), http_url));
-  RenderWidgetHostViewAndroid* rwhva = render_widget_host_view_android();
-  RenderWidgetHostConnector* connector = render_widget_host_connector();
-  EXPECT_EQ(connector, connector_in_rwhva(rwhva));
-
-  // Generate WebContentsObserver::WebContentsDestroyed by closing the contents.
-  web_contents()->Close();
-  EXPECT_EQ(nullptr, connector_in_rwhva(rwhva));
-}
-
 }  // namespace content
diff --git a/content/browser/browser_plugin/browser_plugin_guest.cc b/content/browser/browser_plugin/browser_plugin_guest.cc
index 1f985ec5..d2ba7285 100644
--- a/content/browser/browser_plugin/browser_plugin_guest.cc
+++ b/content/browser/browser_plugin/browser_plugin_guest.cc
@@ -898,8 +898,8 @@
   if (!focused_frame)
     return;
 
-  focused_frame->Send(new InputMsg_ExecuteNoValueEditCommand(
-      focused_frame->GetRoutingID(), name));
+  focused_frame->GetFrameInputHandler()->ExecuteEditCommand(name,
+                                                            base::nullopt);
 }
 
 void BrowserPluginGuest::OnImeSetComposition(
@@ -934,7 +934,7 @@
   RenderFrameHostImpl* rfh = static_cast<RenderFrameHostImpl*>(
       web_contents()->GetFocusedFrame());
   if (rfh)
-    rfh->ExtendSelectionAndDelete(before, after);
+    rfh->GetFrameInputHandler()->ExtendSelectionAndDelete(before, after);
 }
 
 void BrowserPluginGuest::OnLockMouse(bool user_gesture,
diff --git a/content/browser/frame_host/input/OWNERS b/content/browser/frame_host/input/OWNERS
new file mode 100644
index 0000000..cf2b318
--- /dev/null
+++ b/content/browser/frame_host/input/OWNERS
@@ -0,0 +1,7 @@
+aelias@chromium.org
+dtapuska@chromium.org
+tdresser@chromium.org
+
+
+# TEAM: input-dev@chromium.org
+# COMPONENT: Blink>Input
diff --git a/content/browser/frame_host/input/legacy_ipc_frame_input_handler.cc b/content/browser/frame_host/input/legacy_ipc_frame_input_handler.cc
new file mode 100644
index 0000000..0cfd482
--- /dev/null
+++ b/content/browser/frame_host/input/legacy_ipc_frame_input_handler.cc
@@ -0,0 +1,146 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/frame_host/input/legacy_ipc_frame_input_handler.h"
+
+#include "base/strings/utf_string_conversions.h"
+#include "content/browser/renderer_host/render_widget_host_impl.h"
+#include "content/common/input_messages.h"
+
+namespace content {
+
+LegacyIPCFrameInputHandler::LegacyIPCFrameInputHandler(
+    RenderFrameHostImpl* frame_host,
+    int routing_id)
+    : frame_host_(frame_host), routing_id_(routing_id) {
+  DCHECK(frame_host);
+}
+
+LegacyIPCFrameInputHandler::~LegacyIPCFrameInputHandler() {}
+
+void LegacyIPCFrameInputHandler::SetCompositionFromExistingText(
+    int32_t start,
+    int32_t end,
+    const std::vector<ui::CompositionUnderline>& ui_underlines) {
+  std::vector<blink::WebCompositionUnderline> underlines;
+  for (const auto& underline : ui_underlines) {
+    blink::WebCompositionUnderline blink_underline(
+        underline.start_offset, underline.end_offset, underline.color,
+        underline.thick, underline.background_color);
+    underlines.push_back(blink_underline);
+  }
+
+  SendInput(base::MakeUnique<InputMsg_SetCompositionFromExistingText>(
+      routing_id_, start, end, underlines));
+}
+
+void LegacyIPCFrameInputHandler::ExtendSelectionAndDelete(int32_t before,
+                                                          int32_t after) {
+  SendInput(base::MakeUnique<InputMsg_ExtendSelectionAndDelete>(routing_id_,
+                                                                before, after));
+}
+
+void LegacyIPCFrameInputHandler::DeleteSurroundingText(int32_t before,
+                                                       int32_t after) {
+  SendInput(base::MakeUnique<InputMsg_DeleteSurroundingText>(routing_id_,
+                                                             before, after));
+}
+
+void LegacyIPCFrameInputHandler::DeleteSurroundingTextInCodePoints(
+    int32_t before,
+    int32_t after) {
+  SendInput(base::MakeUnique<InputMsg_DeleteSurroundingTextInCodePoints>(
+      routing_id_, before, after));
+}
+
+void LegacyIPCFrameInputHandler::SetEditableSelectionOffsets(int32_t start,
+                                                             int32_t end) {
+  SendInput(base::MakeUnique<InputMsg_SetEditableSelectionOffsets>(routing_id_,
+                                                                   start, end));
+}
+
+void LegacyIPCFrameInputHandler::ExecuteEditCommand(
+    const std::string& command,
+    const base::Optional<base::string16>& value) {
+  if (!value) {
+    SendInput(base::MakeUnique<InputMsg_ExecuteNoValueEditCommand>(routing_id_,
+                                                                   command));
+  }
+}
+
+void LegacyIPCFrameInputHandler::Undo() {
+  SendInput(base::MakeUnique<InputMsg_Undo>(routing_id_));
+}
+
+void LegacyIPCFrameInputHandler::Redo() {
+  SendInput(base::MakeUnique<InputMsg_Redo>(routing_id_));
+}
+
+void LegacyIPCFrameInputHandler::Cut() {
+  SendInput(base::MakeUnique<InputMsg_Cut>(routing_id_));
+}
+
+void LegacyIPCFrameInputHandler::Copy() {
+  SendInput(base::MakeUnique<InputMsg_Copy>(routing_id_));
+}
+
+void LegacyIPCFrameInputHandler::CopyToFindPboard() {
+#if defined(OS_MACOSX)
+  SendInput(base::MakeUnique<InputMsg_CopyToFindPboard>(routing_id_));
+#endif
+}
+
+void LegacyIPCFrameInputHandler::Paste() {
+  SendInput(base::MakeUnique<InputMsg_Paste>(routing_id_));
+}
+
+void LegacyIPCFrameInputHandler::PasteAndMatchStyle() {
+  SendInput(base::MakeUnique<InputMsg_PasteAndMatchStyle>(routing_id_));
+}
+
+void LegacyIPCFrameInputHandler::Replace(const base::string16& word) {
+  SendInput(base::MakeUnique<InputMsg_Replace>(routing_id_, word));
+}
+
+void LegacyIPCFrameInputHandler::ReplaceMisspelling(
+    const base::string16& word) {
+  SendInput(base::MakeUnique<InputMsg_ReplaceMisspelling>(routing_id_, word));
+}
+
+void LegacyIPCFrameInputHandler::Delete() {
+  SendInput(base::MakeUnique<InputMsg_Delete>(routing_id_));
+}
+
+void LegacyIPCFrameInputHandler::SelectAll() {
+  SendInput(base::MakeUnique<InputMsg_SelectAll>(routing_id_));
+}
+
+void LegacyIPCFrameInputHandler::CollapseSelection() {
+  SendInput(base::MakeUnique<InputMsg_CollapseSelection>(routing_id_));
+}
+
+void LegacyIPCFrameInputHandler::SelectRange(const gfx::Point& point,
+                                             const gfx::Point& extent) {
+  SendInput(base::MakeUnique<InputMsg_SelectRange>(routing_id_, point, extent));
+}
+
+void LegacyIPCFrameInputHandler::AdjustSelectionByCharacterOffset(int32_t start,
+                                                                  int32_t end) {
+  SendInput(base::MakeUnique<InputMsg_AdjustSelectionByCharacterOffset>(
+      routing_id_, start, end));
+}
+
+void LegacyIPCFrameInputHandler::MoveRangeSelectionExtent(
+    const gfx::Point& extent) {
+  SendInput(
+      base::MakeUnique<InputMsg_MoveRangeSelectionExtent>(routing_id_, extent));
+}
+
+void LegacyIPCFrameInputHandler::SendInput(
+    std::unique_ptr<IPC::Message> message) {
+  frame_host_->GetRenderWidgetHost()->input_router()->SendInput(
+      std::move(message));
+}
+
+}  // namespace content
diff --git a/content/browser/frame_host/input/legacy_ipc_frame_input_handler.h b/content/browser/frame_host/input/legacy_ipc_frame_input_handler.h
new file mode 100644
index 0000000..46436e0
--- /dev/null
+++ b/content/browser/frame_host/input/legacy_ipc_frame_input_handler.h
@@ -0,0 +1,62 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_BROWSER_FRAME_HOST_INPUT_LEGACY_IPC_FRAME_INPUT_HANDLER_H_
+#define CONTENT_BROWSER_FRAME_HOST_INPUT_LEGACY_IPC_FRAME_INPUT_HANDLER_H_
+
+#include "content/browser/frame_host/render_frame_host_impl.h"
+#include "content/browser/renderer_host/input/input_router_impl.h"
+#include "content/common/input/input_handler.mojom.h"
+
+namespace content {
+
+// An instance of a mojom::FrameInputHandler based on chrome IPC.
+// This class is a temporary class to allow the input messages to
+// remain as Chrome IPC messages but progressively work at moving
+// them to mojo.
+class CONTENT_EXPORT LegacyIPCFrameInputHandler
+    : public mojom::FrameInputHandler {
+ public:
+  LegacyIPCFrameInputHandler(RenderFrameHostImpl* frame_host, int routing_id);
+  ~LegacyIPCFrameInputHandler() override;
+
+  void SetCompositionFromExistingText(
+      int32_t start,
+      int32_t end,
+      const std::vector<ui::CompositionUnderline>& underlines) override;
+  void ExtendSelectionAndDelete(int32_t before, int32_t after) override;
+  void DeleteSurroundingText(int32_t before, int32_t after) override;
+  void DeleteSurroundingTextInCodePoints(int32_t before,
+                                         int32_t after) override;
+  void SetEditableSelectionOffsets(int32_t start, int32_t end) override;
+  void ExecuteEditCommand(const std::string& command,
+                          const base::Optional<base::string16>& value) override;
+  void Undo() override;
+  void Redo() override;
+  void Cut() override;
+  void Copy() override;
+  void CopyToFindPboard() override;
+  void Paste() override;
+  void PasteAndMatchStyle() override;
+  void Replace(const base::string16& word) override;
+  void ReplaceMisspelling(const base::string16& word) override;
+  void Delete() override;
+  void SelectAll() override;
+  void CollapseSelection() override;
+  void SelectRange(const gfx::Point& base, const gfx::Point& extent) override;
+  void AdjustSelectionByCharacterOffset(int32_t start, int32_t end) override;
+  void MoveRangeSelectionExtent(const gfx::Point& extent) override;
+
+ private:
+  void SendInput(std::unique_ptr<IPC::Message> message);
+
+  RenderFrameHostImpl* frame_host_;
+  int routing_id_;
+
+  DISALLOW_COPY_AND_ASSIGN(LegacyIPCFrameInputHandler);
+};
+
+}  // namespace content
+
+#endif  // CONTENT_BROWSER_FRAME_HOST_INPUT_LEGACY_IPC_FRAME_INPUT_HANDLER_H_
diff --git a/content/browser/frame_host/interstitial_page_impl.cc b/content/browser/frame_host/interstitial_page_impl.cc
index d890e64..465086c 100644
--- a/content/browser/frame_host/interstitial_page_impl.cc
+++ b/content/browser/frame_host/interstitial_page_impl.cc
@@ -438,8 +438,7 @@
   if (!focused_node)
     return;
 
-  focused_node->current_frame_host()->Send(
-      new InputMsg_Cut(focused_node->current_frame_host()->GetRoutingID()));
+  focused_node->current_frame_host()->GetFrameInputHandler()->Cut();
   RecordAction(base::UserMetricsAction("Cut"));
 }
 
@@ -448,8 +447,7 @@
   if (!focused_node)
     return;
 
-  focused_node->current_frame_host()->Send(
-      new InputMsg_Copy(focused_node->current_frame_host()->GetRoutingID()));
+  focused_node->current_frame_host()->GetFrameInputHandler()->Copy();
   RecordAction(base::UserMetricsAction("Copy"));
 }
 
@@ -458,8 +456,7 @@
   if (!focused_node)
     return;
 
-  focused_node->current_frame_host()->Send(
-      new InputMsg_Paste(focused_node->current_frame_host()->GetRoutingID()));
+  focused_node->current_frame_host()->GetFrameInputHandler()->Paste();
   RecordAction(base::UserMetricsAction("Paste"));
 }
 
@@ -468,8 +465,7 @@
   if (!focused_node)
     return;
 
-  focused_node->current_frame_host()->Send(new InputMsg_SelectAll(
-      focused_node->current_frame_host()->GetRoutingID()));
+  focused_node->current_frame_host()->GetFrameInputHandler()->SelectAll();
   RecordAction(base::UserMetricsAction("SelectAll"));
 }
 
diff --git a/content/browser/frame_host/render_frame_host_impl.cc b/content/browser/frame_host/render_frame_host_impl.cc
index 55f8a8b..92a8c11 100644
--- a/content/browser/frame_host/render_frame_host_impl.cc
+++ b/content/browser/frame_host/render_frame_host_impl.cc
@@ -30,6 +30,7 @@
 #include "content/browser/frame_host/debug_urls.h"
 #include "content/browser/frame_host/frame_tree.h"
 #include "content/browser/frame_host/frame_tree_node.h"
+#include "content/browser/frame_host/input/legacy_ipc_frame_input_handler.h"
 #include "content/browser/frame_host/navigation_entry_impl.h"
 #include "content/browser/frame_host/navigation_handle_impl.h"
 #include "content/browser/frame_host/navigation_request.h"
@@ -69,6 +70,7 @@
 #include "content/common/content_security_policy/content_security_policy.h"
 #include "content/common/frame_messages.h"
 #include "content/common/frame_owner_properties.h"
+#include "content/common/input/input_handler.mojom.h"
 #include "content/common/input_messages.h"
 #include "content/common/inter_process_time_ticks_converter.h"
 #include "content/common/navigation_params.h"
@@ -1035,6 +1037,12 @@
                    scheme) != bypassing_schemes.end();
 }
 
+mojom::FrameInputHandler* RenderFrameHostImpl::GetFrameInputHandler() {
+  if (legacy_frame_input_handler_)
+    return legacy_frame_input_handler_.get();
+  return frame_input_handler_.get();
+}
+
 bool RenderFrameHostImpl::CreateRenderFrame(int proxy_routing_id,
                                             int opener_routing_id,
                                             int parent_routing_id,
@@ -3065,21 +3073,6 @@
       new FrameMsg_AdvanceFocus(GetRoutingID(), type, source_proxy_routing_id));
 }
 
-void RenderFrameHostImpl::ExtendSelectionAndDelete(size_t before,
-                                                   size_t after) {
-  Send(new InputMsg_ExtendSelectionAndDelete(routing_id_, before, after));
-}
-
-void RenderFrameHostImpl::DeleteSurroundingText(size_t before, size_t after) {
-  Send(new InputMsg_DeleteSurroundingText(routing_id_, before, after));
-}
-
-void RenderFrameHostImpl::DeleteSurroundingTextInCodePoints(int before,
-                                                            int after) {
-  Send(new InputMsg_DeleteSurroundingTextInCodePoints(routing_id_, before,
-                                                      after));
-}
-
 void RenderFrameHostImpl::JavaScriptDialogClosed(
     IPC::Message* reply_msg,
     bool success,
@@ -3235,6 +3228,14 @@
   frame_->GetInterfaceProvider(mojo::MakeRequest(&remote_interfaces));
   remote_interfaces_.reset(new service_manager::InterfaceProvider);
   remote_interfaces_->Bind(std::move(remote_interfaces));
+
+  if (base::CommandLine::ForCurrentProcess()->HasSwitch(
+          switches::kMojoInputMessages)) {
+    GetRemoteInterfaces()->GetInterface(&frame_input_handler_);
+  } else {
+    legacy_frame_input_handler_.reset(
+        new LegacyIPCFrameInputHandler(this, routing_id_));
+  }
 }
 
 void RenderFrameHostImpl::InvalidateMojoConnection() {
diff --git a/content/browser/frame_host/render_frame_host_impl.h b/content/browser/frame_host/render_frame_host_impl.h
index 1d1de6e..7e7ca69f 100644
--- a/content/browser/frame_host/render_frame_host_impl.h
+++ b/content/browser/frame_host/render_frame_host_impl.h
@@ -85,6 +85,7 @@
 namespace content {
 class AssociatedInterfaceProviderImpl;
 class AssociatedInterfaceRegistryImpl;
+class LegacyIPCFrameInputHandler;
 class FeaturePolicy;
 class FrameTree;
 class FrameTreeNode;
@@ -229,6 +230,8 @@
       GURL* blocked_url,
       SourceLocation* source_location) const override;
 
+  mojom::FrameInputHandler* GetFrameInputHandler() override;
+
   // Creates a RenderFrame in the renderer process.
   bool CreateRenderFrame(int proxy_routing_id,
                          int opener_routing_id,
@@ -446,21 +449,6 @@
   void AdvanceFocus(blink::WebFocusType type,
                     RenderFrameProxyHost* source_proxy);
 
-  // Deletes the current selection plus the specified number of characters
-  // before and after the selection or caret.
-  void ExtendSelectionAndDelete(size_t before, size_t after);
-
-  // Deletes text before and after the current cursor position, excluding the
-  // selection. The lengths are supplied in Java chars (UTF-16 Code Unit), not
-  // in code points or in glyphs.
-  void DeleteSurroundingText(size_t before, size_t after);
-
-  // Deletes text before and after the current cursor position, excluding the
-  // selection. The lengths are supplied in code points, not in Java chars
-  // (UTF-16 Code Unit) or in glyphs. Do nothing if there are one or more
-  // invalid surrogate pairs in the requested range.
-  void DeleteSurroundingTextInCodePoints(int before, int after);
-
   // Notifies the RenderFrame that the JavaScript message that was shown was
   // closed by the user.
   void JavaScriptDialogClosed(IPC::Message* reply_msg,
@@ -1245,6 +1233,9 @@
   // crbug.com/715541
   std::string untrusted_devtools_frame_id_;
 
+  mojom::FrameInputHandlerPtr frame_input_handler_;
+  std::unique_ptr<LegacyIPCFrameInputHandler> legacy_frame_input_handler_;
+
   // NOTE: This must be the last member.
   base::WeakPtrFactory<RenderFrameHostImpl> weak_ptr_factory_;
 
diff --git a/content/browser/loader/resource_dispatcher_host_impl.cc b/content/browser/loader/resource_dispatcher_host_impl.cc
index e9178424..a4b99cd 100644
--- a/content/browser/loader/resource_dispatcher_host_impl.cc
+++ b/content/browser/loader/resource_dispatcher_host_impl.cc
@@ -1155,31 +1155,35 @@
                                request_id, std::move(url_loader_client));
     return;
   }
-  // Check if we have a registered interceptor for the headers passed in. If
-  // yes then we need to mark the current request as pending and wait for the
-  // interceptor to invoke the callback with a status code indicating whether
-  // the request needs to be aborted or continued.
-  for (net::HttpRequestHeaders::Iterator it(headers); it.GetNext();) {
-    HeaderInterceptorMap::iterator index =
-        http_header_interceptor_map_.find(it.name());
-    if (index != http_header_interceptor_map_.end()) {
-      HeaderInterceptorInfo& interceptor_info = index->second;
 
-      bool call_interceptor = true;
-      if (!interceptor_info.starts_with.empty()) {
-        call_interceptor =
-            base::StartsWith(it.value(), interceptor_info.starts_with,
-                             base::CompareCase::INSENSITIVE_ASCII);
-      }
-      if (call_interceptor) {
-        interceptor_info.interceptor.Run(
-            it.name(), it.value(), child_id, resource_context,
-            base::Bind(&ResourceDispatcherHostImpl::ContinuePendingBeginRequest,
-                       base::Unretained(this), requester_info, request_id,
-                       request_data, sync_result_handler, route_id, headers,
-                       base::Passed(std::move(mojo_request)),
-                       base::Passed(std::move(url_loader_client))));
-        return;
+  if (!is_navigation_stream_request) {
+    // Check if we have a registered interceptor for the headers passed in. If
+    // yes then we need to mark the current request as pending and wait for the
+    // interceptor to invoke the callback with a status code indicating whether
+    // the request needs to be aborted or continued.
+    for (net::HttpRequestHeaders::Iterator it(headers); it.GetNext();) {
+      HeaderInterceptorMap::iterator index =
+          http_header_interceptor_map_.find(it.name());
+      if (index != http_header_interceptor_map_.end()) {
+        HeaderInterceptorInfo& interceptor_info = index->second;
+
+        bool call_interceptor = true;
+        if (!interceptor_info.starts_with.empty()) {
+          call_interceptor =
+              base::StartsWith(it.value(), interceptor_info.starts_with,
+                               base::CompareCase::INSENSITIVE_ASCII);
+        }
+        if (call_interceptor) {
+          interceptor_info.interceptor.Run(
+              it.name(), it.value(), child_id, resource_context,
+              base::Bind(
+                  &ResourceDispatcherHostImpl::ContinuePendingBeginRequest,
+                  base::Unretained(this), requester_info, request_id,
+                  request_data, sync_result_handler, route_id, headers,
+                  base::Passed(std::move(mojo_request)),
+                  base::Passed(std::move(url_loader_client))));
+          return;
+        }
       }
     }
   }
@@ -1214,7 +1218,12 @@
     return;
   }
   int child_id = requester_info->child_id();
-
+  storage::BlobStorageContext* blob_context = nullptr;
+  bool allow_download = false;
+  bool do_not_prompt_for_login = false;
+  bool report_raw_headers = false;
+  bool is_sync_load = !!sync_result_handler;
+  int load_flags = BuildLoadFlagsForRequest(request_data, is_sync_load);
   bool is_navigation_stream_request =
       IsBrowserSideNavigationEnabled() &&
       IsResourceTypeFrame(request_data.resource_type);
@@ -1240,128 +1249,128 @@
                                    : request_data.url,
       request_data.priority, nullptr, kTrafficAnnotation);
 
-  // Log that this request is a service worker navigation preload request here,
-  // since navigation preload machinery has no access to netlog.
-  // TODO(falken): Figure out how mojom::URLLoaderClient can
-  // access the request's netlog.
-  if (requester_info->IsNavigationPreload()) {
-    new_request->net_log().AddEvent(
-        net::NetLogEventType::SERVICE_WORKER_NAVIGATION_PRELOAD_REQUEST);
-  }
-
-  // PlzNavigate: Always set the method to GET when gaining access to the
-  // stream that contains the response body of a navigation. Otherwise the data
-  // that was already fetched by the browser will not be transmitted to the
-  // renderer.
-  if (is_navigation_stream_request)
+  if (is_navigation_stream_request) {
+    // PlzNavigate: Always set the method to GET when gaining access to the
+    // stream that contains the response body of a navigation. Otherwise the
+    // data that was already fetched by the browser will not be transmitted to
+    // the renderer.
     new_request->set_method("GET");
-  else
+  } else {
+    // Log that this request is a service worker navigation preload request
+    // here, since navigation preload machinery has no access to netlog.
+    // TODO(falken): Figure out how mojom::URLLoaderClient can
+    // access the request's netlog.
+    if (requester_info->IsNavigationPreload()) {
+      new_request->net_log().AddEvent(
+          net::NetLogEventType::SERVICE_WORKER_NAVIGATION_PRELOAD_REQUEST);
+    }
+
     new_request->set_method(request_data.method);
 
-  new_request->set_first_party_for_cookies(
-      request_data.first_party_for_cookies);
+    new_request->set_first_party_for_cookies(
+        request_data.first_party_for_cookies);
 
-  // The initiator should normally be present, unless this is a navigation in a
-  // top-level frame. It may be null for some top-level navigations (eg:
-  // browser-initiated ones).
-  DCHECK(request_data.request_initiator.has_value() ||
-         request_data.resource_type == RESOURCE_TYPE_MAIN_FRAME);
-  new_request->set_initiator(request_data.request_initiator);
+    // The initiator should normally be present, unless this is a navigation in
+    // a top-level frame. It may be null for some top-level navigations (eg:
+    // browser-initiated ones).
+    DCHECK(request_data.request_initiator.has_value() ||
+           request_data.resource_type == RESOURCE_TYPE_MAIN_FRAME);
+    new_request->set_initiator(request_data.request_initiator);
 
-  if (request_data.originated_from_service_worker) {
-    new_request->SetUserData(URLRequestServiceWorkerData::kUserDataKey,
-                             base::MakeUnique<URLRequestServiceWorkerData>());
-  }
+    if (request_data.originated_from_service_worker) {
+      new_request->SetUserData(URLRequestServiceWorkerData::kUserDataKey,
+                               base::MakeUnique<URLRequestServiceWorkerData>());
+    }
 
-  // If the request is a MAIN_FRAME request, the first-party URL gets updated on
-  // redirects.
-  if (request_data.resource_type == RESOURCE_TYPE_MAIN_FRAME) {
-    new_request->set_first_party_url_policy(
-        net::URLRequest::UPDATE_FIRST_PARTY_URL_ON_REDIRECT);
-  }
+    // If the request is a MAIN_FRAME request, the first-party URL gets updated
+    // on redirects.
+    if (request_data.resource_type == RESOURCE_TYPE_MAIN_FRAME) {
+      new_request->set_first_party_url_policy(
+          net::URLRequest::UPDATE_FIRST_PARTY_URL_ON_REDIRECT);
+    }
 
-  // For PlzNavigate, this request has already been made and the referrer was
-  // checked previously. So don't set the referrer for this stream request, or
-  // else it will fail for SSL redirects since net/ will think the blob:https
-  // for the stream is not a secure scheme (specifically, in the call to
-  // ComputeReferrerForRedirect).
-  if (!is_navigation_stream_request) {
+    // For PlzNavigate, this request has already been made and the referrer was
+    // checked previously. So don't set the referrer for this stream request, or
+    // else it will fail for SSL redirects since net/ will think the blob:https
+    // for the stream is not a secure scheme (specifically, in the call to
+    // ComputeReferrerForRedirect).
     const Referrer referrer(
         request_data.referrer, request_data.referrer_policy);
     Referrer::SetReferrerForRequest(new_request.get(), referrer);
-  }
 
-  new_request->SetExtraRequestHeaders(headers);
+    new_request->SetExtraRequestHeaders(headers);
 
-  storage::BlobStorageContext* blob_context =
-      GetBlobStorageContext(requester_info->blob_storage_context());
-  // Resolve elements from request_body and prepare upload data.
-  if (request_data.request_body.get()) {
-    // |blob_context| could be null when the request is from the plugins because
-    // ResourceMessageFilters created in PluginProcessHost don't have the blob
-    // context.
-    if (blob_context) {
-      // Attaches the BlobDataHandles to request_body not to free the blobs and
-      // any attached shareable files until upload completion. These data will
-      // be used in UploadDataStream and ServiceWorkerURLRequestJob.
-      AttachRequestBodyBlobDataHandles(request_data.request_body.get(),
-                                       resource_context);
+    blob_context =
+        GetBlobStorageContext(requester_info->blob_storage_context());
+    // Resolve elements from request_body and prepare upload data.
+    if (request_data.request_body.get()) {
+      // |blob_context| could be null when the request is from the plugins
+      // because ResourceMessageFilters created in PluginProcessHost don't have
+      // the blob context.
+      if (blob_context) {
+        // Attaches the BlobDataHandles to request_body not to free the blobs
+        // and any attached shareable files until upload completion. These data
+        // will be used in UploadDataStream and ServiceWorkerURLRequestJob.
+        AttachRequestBodyBlobDataHandles(request_data.request_body.get(),
+                                         resource_context);
+      }
+      new_request->set_upload(UploadDataStreamBuilder::Build(
+          request_data.request_body.get(), blob_context,
+          requester_info->file_system_context(),
+          BrowserThread::GetTaskRunnerForThread(BrowserThread::FILE).get()));
     }
-    new_request->set_upload(UploadDataStreamBuilder::Build(
-        request_data.request_body.get(), blob_context,
-        requester_info->file_system_context(),
-        BrowserThread::GetTaskRunnerForThread(BrowserThread::FILE).get()));
+
+    allow_download = request_data.allow_download &&
+                     IsResourceTypeFrame(request_data.resource_type);
+    do_not_prompt_for_login = request_data.do_not_prompt_for_login;
+
+    // Raw headers are sensitive, as they include Cookie/Set-Cookie, so only
+    // allow requesting them if requester has ReadRawCookies permission.
+    ChildProcessSecurityPolicyImpl* policy =
+        ChildProcessSecurityPolicyImpl::GetInstance();
+    report_raw_headers = request_data.report_raw_headers;
+    if (report_raw_headers && !policy->CanReadRawCookies(child_id) &&
+        !requester_info->IsNavigationPreload()) {
+      // For navigation preload, the child_id is -1 so CanReadRawCookies would
+      // return false. But |report_raw_headers| of the navigation preload
+      // request was copied from the original request, so this check has already
+      // been carried out.
+      // TODO: crbug.com/523063 can we call bad_message::ReceivedBadMessage
+      // here?
+      VLOG(1) << "Denied unauthorized request for raw headers";
+      report_raw_headers = false;
+    }
+
+    if (request_data.resource_type == RESOURCE_TYPE_PREFETCH ||
+        request_data.resource_type == RESOURCE_TYPE_FAVICON) {
+      do_not_prompt_for_login = true;
+    }
+    if (request_data.resource_type == RESOURCE_TYPE_IMAGE &&
+        HTTP_AUTH_RELATION_BLOCKED_CROSS ==
+            HttpAuthRelationTypeOf(request_data.url,
+                                   request_data.first_party_for_cookies)) {
+      // Prevent third-party image content from prompting for login, as this
+      // is often a scam to extract credentials for another domain from the
+      // user. Only block image loads, as the attack applies largely to the
+      // "src" property of the <img> tag. It is common for web properties to
+      // allow untrusted values for <img src>; this is considered a fair thing
+      // for an HTML sanitizer to do. Conversely, any HTML sanitizer that didn't
+      // filter sources for <script>, <link>, <embed>, <object>, <iframe> tags
+      // would be considered vulnerable in and of itself.
+      do_not_prompt_for_login = true;
+      load_flags |= net::LOAD_DO_NOT_USE_EMBEDDED_IDENTITY;
+    }
+
+    // Sync loads should have maximum priority and should be the only
+    // requets that have the ignore limits flag set.
+    if (is_sync_load) {
+      DCHECK_EQ(request_data.priority, net::MAXIMUM_PRIORITY);
+      DCHECK_NE(load_flags & net::LOAD_IGNORE_LIMITS, 0);
+    } else {
+      DCHECK_EQ(load_flags & net::LOAD_IGNORE_LIMITS, 0);
+    }
   }
 
-  bool allow_download = request_data.allow_download &&
-      IsResourceTypeFrame(request_data.resource_type);
-  bool do_not_prompt_for_login = request_data.do_not_prompt_for_login;
-  bool is_sync_load = !!sync_result_handler;
-
-  // Raw headers are sensitive, as they include Cookie/Set-Cookie, so only
-  // allow requesting them if requester has ReadRawCookies permission.
-  ChildProcessSecurityPolicyImpl* policy =
-      ChildProcessSecurityPolicyImpl::GetInstance();
-  bool report_raw_headers = request_data.report_raw_headers;
-  if (report_raw_headers && !policy->CanReadRawCookies(child_id) &&
-      !requester_info->IsNavigationPreload()) {
-    // For navigation preload, the child_id is -1 so CanReadRawCookies would
-    // return false. But |report_raw_headers| of the navigation preload request
-    // was copied from the original request, so this check has already been
-    // carried out.
-    // TODO: crbug.com/523063 can we call bad_message::ReceivedBadMessage here?
-    VLOG(1) << "Denied unauthorized request for raw headers";
-    report_raw_headers = false;
-  }
-  int load_flags = BuildLoadFlagsForRequest(request_data, is_sync_load);
-  if (request_data.resource_type == RESOURCE_TYPE_PREFETCH ||
-      request_data.resource_type == RESOURCE_TYPE_FAVICON) {
-    do_not_prompt_for_login = true;
-  }
-  if (request_data.resource_type == RESOURCE_TYPE_IMAGE &&
-      HTTP_AUTH_RELATION_BLOCKED_CROSS ==
-          HttpAuthRelationTypeOf(request_data.url,
-                                 request_data.first_party_for_cookies)) {
-    // Prevent third-party image content from prompting for login, as this
-    // is often a scam to extract credentials for another domain from the user.
-    // Only block image loads, as the attack applies largely to the "src"
-    // property of the <img> tag. It is common for web properties to allow
-    // untrusted values for <img src>; this is considered a fair thing for an
-    // HTML sanitizer to do. Conversely, any HTML sanitizer that didn't
-    // filter sources for <script>, <link>, <embed>, <object>, <iframe> tags
-    // would be considered vulnerable in and of itself.
-    do_not_prompt_for_login = true;
-    load_flags |= net::LOAD_DO_NOT_USE_EMBEDDED_IDENTITY;
-  }
-
-  // Sync loads should have maximum priority and should be the only
-  // requets that have the ignore limits flag set.
-  if (is_sync_load) {
-    DCHECK_EQ(request_data.priority, net::MAXIMUM_PRIORITY);
-    DCHECK_NE(load_flags & net::LOAD_IGNORE_LIMITS, 0);
-  } else {
-    DCHECK_EQ(load_flags & net::LOAD_IGNORE_LIMITS, 0);
-  }
   new_request->SetLoadFlags(load_flags);
 
   // Make extra info and read footer (contains request ID).
@@ -1395,38 +1404,48 @@
                                ->GetBlobDataFromPublicURL(new_request->url()));
   }
 
-  // Initialize the service worker handler for the request. We don't use
-  // ServiceWorker for synchronous loads to avoid renderer deadlocks.
-  const ServiceWorkerMode service_worker_mode =
-      is_sync_load ? ServiceWorkerMode::NONE : request_data.service_worker_mode;
-  ServiceWorkerRequestHandler::InitializeHandler(
-      new_request.get(), requester_info->service_worker_context(), blob_context,
-      child_id, request_data.service_worker_provider_id,
-      service_worker_mode != ServiceWorkerMode::ALL,
-      request_data.fetch_request_mode, request_data.fetch_credentials_mode,
-      request_data.fetch_redirect_mode, request_data.resource_type,
-      request_data.fetch_request_context_type, request_data.fetch_frame_type,
-      request_data.request_body);
+  std::unique_ptr<ResourceHandler> handler;
+  if (is_navigation_stream_request) {
+    // PlzNavigate: do not add ResourceThrottles for main resource requests from
+    // the renderer.  Decisions about the navigation should have been done in
+    // the initial request.
+    handler = CreateBaseResourceHandler(
+        new_request.get(), std::move(mojo_request),
+        std::move(url_loader_client), request_data.resource_type);
+  } else {
+    // Initialize the service worker handler for the request. We don't use
+    // ServiceWorker for synchronous loads to avoid renderer deadlocks.
+    const ServiceWorkerMode service_worker_mode =
+        is_sync_load ? ServiceWorkerMode::NONE
+                     : request_data.service_worker_mode;
+    ServiceWorkerRequestHandler::InitializeHandler(
+        new_request.get(), requester_info->service_worker_context(),
+        blob_context, child_id, request_data.service_worker_provider_id,
+        service_worker_mode != ServiceWorkerMode::ALL,
+        request_data.fetch_request_mode, request_data.fetch_credentials_mode,
+        request_data.fetch_redirect_mode, request_data.resource_type,
+        request_data.fetch_request_context_type, request_data.fetch_frame_type,
+        request_data.request_body);
 
-  ForeignFetchRequestHandler::InitializeHandler(
-      new_request.get(), requester_info->service_worker_context(), blob_context,
-      child_id, request_data.service_worker_provider_id, service_worker_mode,
-      request_data.fetch_request_mode, request_data.fetch_credentials_mode,
-      request_data.fetch_redirect_mode, request_data.resource_type,
-      request_data.fetch_request_context_type, request_data.fetch_frame_type,
-      request_data.request_body, request_data.initiated_in_secure_context);
+    ForeignFetchRequestHandler::InitializeHandler(
+        new_request.get(), requester_info->service_worker_context(),
+        blob_context, child_id, request_data.service_worker_provider_id,
+        service_worker_mode, request_data.fetch_request_mode,
+        request_data.fetch_credentials_mode, request_data.fetch_redirect_mode,
+        request_data.resource_type, request_data.fetch_request_context_type,
+        request_data.fetch_frame_type, request_data.request_body,
+        request_data.initiated_in_secure_context);
 
-  // Have the appcache associate its extra info with the request.
-  AppCacheInterceptor::SetExtraRequestInfo(
-      new_request.get(), requester_info->appcache_service(), child_id,
-      request_data.appcache_host_id, request_data.resource_type,
-      request_data.should_reset_appcache);
-
-  std::unique_ptr<ResourceHandler> handler(CreateResourceHandler(
-      requester_info.get(), new_request.get(), request_data,
-      sync_result_handler, route_id, child_id, resource_context,
-      std::move(mojo_request), std::move(url_loader_client)));
-
+    // Have the appcache associate its extra info with the request.
+    AppCacheInterceptor::SetExtraRequestInfo(
+        new_request.get(), requester_info->appcache_service(), child_id,
+        request_data.appcache_host_id, request_data.resource_type,
+        request_data.should_reset_appcache);
+    handler = CreateResourceHandler(
+        requester_info.get(), new_request.get(), request_data,
+        sync_result_handler, route_id, child_id, resource_context,
+        std::move(mojo_request), std::move(url_loader_client));
+  }
   if (handler)
     BeginRequestInternal(std::move(new_request), std::move(handler));
 }
@@ -1462,14 +1481,9 @@
     DCHECK(!url_loader_client);
     handler.reset(new SyncResourceHandler(request, sync_result_handler, this));
   } else {
-    if (mojo_request.is_pending()) {
-      handler.reset(new MojoAsyncResourceHandler(request, this,
-                                                 std::move(mojo_request),
-                                                 std::move(url_loader_client),
-                                                 request_data.resource_type));
-    } else {
-      handler.reset(new AsyncResourceHandler(request, this));
-    }
+    handler = CreateBaseResourceHandler(request, std::move(mojo_request),
+                                        std::move(url_loader_client),
+                                        request_data.resource_type);
 
     // The RedirectToFileResourceHandler depends on being next in the chain.
     if (request_data.download_to_file) {
@@ -1502,15 +1516,6 @@
     handler = std::move(detachable_handler);
   }
 
-  // PlzNavigate: do not add ResourceThrottles for main resource requests from
-  // the renderer.  Decisions about the navigation should have been done in the
-  // initial request.
-  if (IsBrowserSideNavigationEnabled() &&
-      IsResourceTypeFrame(request_data.resource_type)) {
-    DCHECK(request->url().SchemeIs(url::kBlobScheme));
-    return handler;
-  }
-
   return AddStandardHandlers(request, request_data.resource_type,
                              resource_context,
                              request_data.fetch_request_context_type,
@@ -1520,6 +1525,23 @@
 }
 
 std::unique_ptr<ResourceHandler>
+ResourceDispatcherHostImpl::CreateBaseResourceHandler(
+    net::URLRequest* request,
+    mojom::URLLoaderAssociatedRequest mojo_request,
+    mojom::URLLoaderClientPtr url_loader_client,
+    ResourceType resource_type) {
+  std::unique_ptr<ResourceHandler> handler;
+  if (mojo_request.is_pending()) {
+    handler.reset(new MojoAsyncResourceHandler(
+        request, this, std::move(mojo_request), std::move(url_loader_client),
+        resource_type));
+  } else {
+    handler.reset(new AsyncResourceHandler(request, this));
+  }
+  return handler;
+}
+
+std::unique_ptr<ResourceHandler>
 ResourceDispatcherHostImpl::AddStandardHandlers(
     net::URLRequest* request,
     ResourceType resource_type,
@@ -2610,9 +2632,17 @@
     ResourceContext* resource_context) {
   ChildProcessSecurityPolicyImpl* policy =
       ChildProcessSecurityPolicyImpl::GetInstance();
-
+  bool is_navigation_stream_request =
+      IsBrowserSideNavigationEnabled() &&
+      IsResourceTypeFrame(request_data.resource_type);
   // Check if the renderer is permitted to request the requested URL.
-  if (!policy->CanRequestURL(child_id, request_data.url)) {
+  // PlzNavigate: no need to check the URL here. The browser already picked the
+  // right renderer to send the request to. The original URL isn't used, as the
+  // renderer is fetching the stream URL. Checking the original URL doesn't work
+  // in case of redirects across schemes, since the original URL might not be
+  // granted to the final URL's renderer.
+  if (!is_navigation_stream_request &&
+      !policy->CanRequestURL(child_id, request_data.url)) {
     VLOG(1) << "Denied unauthorized request for "
             << request_data.url.possibly_invalid_spec();
     return false;
diff --git a/content/browser/loader/resource_dispatcher_host_impl.h b/content/browser/loader/resource_dispatcher_host_impl.h
index 826cc37..9ac1b25 100644
--- a/content/browser/loader/resource_dispatcher_host_impl.h
+++ b/content/browser/loader/resource_dispatcher_host_impl.h
@@ -597,6 +597,13 @@
       mojom::URLLoaderAssociatedRequest mojo_request,
       mojom::URLLoaderClientPtr url_loader_client);
 
+  // Creates either MojoAsyncResourceHandler or AsyncResourceHandler.
+  std::unique_ptr<ResourceHandler> CreateBaseResourceHandler(
+      net::URLRequest* request,
+      mojom::URLLoaderAssociatedRequest mojo_request,
+      mojom::URLLoaderClientPtr url_loader_client,
+      ResourceType resource_type);
+
   // Wraps |handler| in the standard resource handlers for normal resource
   // loading and navigation requests. This adds MimeTypeResourceHandler and
   // ResourceThrottles.
diff --git a/content/browser/renderer_host/input/touch_selection_controller_client_child_frame.cc b/content/browser/renderer_host/input/touch_selection_controller_client_child_frame.cc
index 140b6347..5a40845a 100644
--- a/content/browser/renderer_host/input/touch_selection_controller_client_child_frame.cc
+++ b/content/browser/renderer_host/input/touch_selection_controller_client_child_frame.cc
@@ -187,9 +187,6 @@
   anchor_point.Offset(-origin.x(), -origin.y());
   RenderWidgetHostImpl* host =
       RenderWidgetHostImpl::From(rwhv_->GetRenderWidgetHost());
-  // TODO(wjmaclean): Probably this ViewMsg should be converted to a FrameMsg
-  // since it will need to go to a RenderFrame once the Blink-side plumbing for
-  // this is hooked up.
   host->Send(new ViewMsg_ShowContextMenu(host->GetRoutingID(),
                                          ui::MENU_SOURCE_TOUCH_EDIT_MENU,
                                          gfx::ToRoundedPoint(anchor_point)));
diff --git a/content/browser/renderer_host/render_widget_host_view_aura.cc b/content/browser/renderer_host/render_widget_host_view_aura.cc
index 167bf42..80a0d1d 100644
--- a/content/browser/renderer_host/render_widget_host_view_aura.cc
+++ b/content/browser/renderer_host/render_widget_host_view_aura.cc
@@ -1429,7 +1429,7 @@
     size_t before, size_t after) {
   RenderFrameHostImpl* rfh = GetFocusedFrame();
   if (rfh)
-    rfh->ExtendSelectionAndDelete(before, after);
+    rfh->GetFrameInputHandler()->ExtendSelectionAndDelete(before, after);
 }
 
 void RenderWidgetHostViewAura::EnsureCaretNotInRect(
diff --git a/content/browser/site_per_process_browsertest.cc b/content/browser/site_per_process_browsertest.cc
index 1e75835a..0812caa 100644
--- a/content/browser/site_per_process_browsertest.cc
+++ b/content/browser/site_per_process_browsertest.cc
@@ -7851,8 +7851,7 @@
 
   // "Select all" in the subframe.  The bug only happens if there's a selection
   // change, which triggers the path through didChangeSelection.
-  root->child_at(0)->current_frame_host()->Send(new InputMsg_SelectAll(
-      root->child_at(0)->current_frame_host()->GetRoutingID()));
+  root->child_at(0)->current_frame_host()->GetFrameInputHandler()->SelectAll();
 
   // Prevent b.com process from terminating right away once the subframe
   // navigates away from b.com below.  This is necessary so that the renderer
diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc
index 627a0e5..ed882eff 100644
--- a/content/browser/web_contents/web_contents_impl.cc
+++ b/content/browser/web_contents/web_contents_impl.cc
@@ -2752,8 +2752,7 @@
   if (!focused_frame)
     return;
 
-  focused_frame->Send(new InputMsg_MoveRangeSelectionExtent(
-      focused_frame->GetRoutingID(), extent));
+  focused_frame->GetFrameInputHandler()->MoveRangeSelectionExtent(extent);
 }
 
 void WebContentsImpl::SelectRange(const gfx::Point& base,
@@ -2762,8 +2761,7 @@
   if (!focused_frame)
     return;
 
-  focused_frame->Send(
-      new InputMsg_SelectRange(focused_frame->GetRoutingID(), base, extent));
+  focused_frame->GetFrameInputHandler()->SelectRange(base, extent);
 }
 
 void WebContentsImpl::AdjustSelectionByCharacterOffset(int start_adjust,
@@ -2772,8 +2770,8 @@
   if (!focused_frame)
     return;
 
-  focused_frame->Send(new InputMsg_AdjustSelectionByCharacterOffset(
-      focused_frame->GetRoutingID(), start_adjust, end_adjust));
+  focused_frame->GetFrameInputHandler()->AdjustSelectionByCharacterOffset(
+      start_adjust, end_adjust);
 }
 
 void WebContentsImpl::UpdatePreferredSize(const gfx::Size& pref_size) {
@@ -2933,7 +2931,7 @@
   if (!focused_frame)
     return;
 
-  focused_frame->Send(new InputMsg_Undo(focused_frame->GetRoutingID()));
+  focused_frame->GetFrameInputHandler()->Undo();
   RecordAction(base::UserMetricsAction("Undo"));
 }
 
@@ -2941,7 +2939,7 @@
   RenderFrameHost* focused_frame = GetFocusedFrame();
   if (!focused_frame)
     return;
-  focused_frame->Send(new InputMsg_Redo(focused_frame->GetRoutingID()));
+  focused_frame->GetFrameInputHandler()->Redo();
   RecordAction(base::UserMetricsAction("Redo"));
 }
 
@@ -2950,7 +2948,7 @@
   if (!focused_frame)
     return;
 
-  focused_frame->Send(new InputMsg_Cut(focused_frame->GetRoutingID()));
+  focused_frame->GetFrameInputHandler()->Cut();
   RecordAction(base::UserMetricsAction("Cut"));
 }
 
@@ -2959,7 +2957,7 @@
   if (!focused_frame)
     return;
 
-  focused_frame->Send(new InputMsg_Copy(focused_frame->GetRoutingID()));
+  focused_frame->GetFrameInputHandler()->Copy();
   RecordAction(base::UserMetricsAction("Copy"));
 }
 
@@ -2970,8 +2968,7 @@
     return;
 
   // Windows/Linux don't have the concept of a find pasteboard.
-  focused_frame->Send(
-      new InputMsg_CopyToFindPboard(focused_frame->GetRoutingID()));
+  focused_frame->GetFrameInputHandler()->CopyToFindPboard();
   RecordAction(base::UserMetricsAction("CopyToFindPboard"));
 #endif
 }
@@ -2981,7 +2978,7 @@
   if (!focused_frame)
     return;
 
-  focused_frame->Send(new InputMsg_Paste(focused_frame->GetRoutingID()));
+  focused_frame->GetFrameInputHandler()->Paste();
   RecordAction(base::UserMetricsAction("Paste"));
 }
 
@@ -2990,8 +2987,7 @@
   if (!focused_frame)
     return;
 
-  focused_frame->Send(new InputMsg_PasteAndMatchStyle(
-      focused_frame->GetRoutingID()));
+  focused_frame->GetFrameInputHandler()->PasteAndMatchStyle();
   RecordAction(base::UserMetricsAction("PasteAndMatchStyle"));
 }
 
@@ -3000,7 +2996,7 @@
   if (!focused_frame)
     return;
 
-  focused_frame->Send(new InputMsg_Delete(focused_frame->GetRoutingID()));
+  focused_frame->GetFrameInputHandler()->Delete();
   RecordAction(base::UserMetricsAction("DeleteSelection"));
 }
 
@@ -3009,7 +3005,7 @@
   if (!focused_frame)
     return;
 
-  focused_frame->Send(new InputMsg_SelectAll(focused_frame->GetRoutingID()));
+  focused_frame->GetFrameInputHandler()->SelectAll();
   RecordAction(base::UserMetricsAction("SelectAll"));
 }
 
@@ -3018,8 +3014,7 @@
   if (!focused_frame)
     return;
 
-  focused_frame->Send(
-      new InputMsg_CollapseSelection(focused_frame->GetRoutingID()));
+  focused_frame->GetFrameInputHandler()->CollapseSelection();
 }
 
 void WebContentsImpl::Replace(const base::string16& word) {
@@ -3027,8 +3022,7 @@
   if (!focused_frame)
     return;
 
-  focused_frame->Send(new InputMsg_Replace(
-      focused_frame->GetRoutingID(), word));
+  focused_frame->GetFrameInputHandler()->Replace(word);
 }
 
 void WebContentsImpl::ReplaceMisspelling(const base::string16& word) {
@@ -3036,8 +3030,7 @@
   if (!focused_frame)
     return;
 
-  focused_frame->Send(new InputMsg_ReplaceMisspelling(
-      focused_frame->GetRoutingID(), word));
+  focused_frame->GetFrameInputHandler()->ReplaceMisspelling(word);
 }
 
 void WebContentsImpl::NotifyContextMenuClosed(
diff --git a/content/common/BUILD.gn b/content/common/BUILD.gn
index 139a7dec..e5e245e 100644
--- a/content/common/BUILD.gn
+++ b/content/common/BUILD.gn
@@ -601,6 +601,7 @@
     "host_zoom.mojom",
     "image_downloader/image_downloader.mojom",
     "indexed_db/indexed_db.mojom",
+    "input/input_handler.mojom",
     "leveldb_wrapper.mojom",
     "manifest_observer.mojom",
     "media/media_devices.mojom",
diff --git a/content/common/input/OWNERS b/content/common/input/OWNERS
index d1c97c5..d74c0a7 100644
--- a/content/common/input/OWNERS
+++ b/content/common/input/OWNERS
@@ -2,6 +2,8 @@
 dtapuska@chromium.org
 tdresser@chromium.org
 
+per-file *.mojom=set noparent
+per-file *.mojom=file://ipc/SECURITY_OWNERS
 per-file *_param_traits*.*=set noparent
 per-file *_param_traits*.*=file://ipc/SECURITY_OWNERS
 
diff --git a/content/common/input/input_handler.mojom b/content/common/input/input_handler.mojom
new file mode 100644
index 0000000..fae10f96
--- /dev/null
+++ b/content/common/input/input_handler.mojom
@@ -0,0 +1,84 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+module content.mojom;
+
+import "mojo/common/string16.mojom";
+import "services/ui/public/interfaces/ime/ime.mojom";
+import "ui/gfx/geometry/mojo/geometry.mojom";
+
+interface WidgetInputHandler {
+  // TODO(dtapuska): Implement me.
+};
+
+// This interface provides the input actions associated with the RenderFrame.
+// Other input actions may also be dispatched via the WidgetInputHandler
+// interface. If frame input actions are dispatched the WidgetInputHandler
+// should be fetched via the associated interface request so that input calls
+// remain in order. See https://goo.gl/x4ee8A for more details.
+interface FrameInputHandler {
+  // Sets the text composition to be between the given start and end offsets in
+  // the currently focused editable field.
+  SetCompositionFromExistingText(
+      int32 start, int32 end, array<ui.mojom.CompositionUnderline> underlines);
+
+  // Deletes the current selection plus the specified number of characters
+  // before and after the selection or caret.
+  ExtendSelectionAndDelete(int32 before, int32 after);
+
+  // Deletes text before and after the current cursor position, excluding the
+  // selection. The lengths are supplied in Java chars (UTF-16 Code Unit),
+  // not in code points or in glyphs.
+  DeleteSurroundingText(int32 before, int32 after);
+
+  // Deletes text before and after the current cursor position, excluding the
+  // selection. The lengths are supplied in code points, not in Java chars
+  // (UTF-16 Code Unit) or in glyphs. Does nothing if there are one or more
+  // invalid surrogate pairs in the requested range
+  DeleteSurroundingTextInCodePoints(int32 before, int32 after);
+
+  // Selects between the given start and end offsets in the currently focused
+  // editable field.
+  SetEditableSelectionOffsets(int32 start, int32 end);
+
+  // Message payload is the name/value of a WebCore edit command to execute.
+  ExecuteEditCommand(string command, mojo.common.mojom.String16? value);
+
+  // These messages are typically generated from context menus and request the
+  // renderer to apply the specified operation to the current selection.
+  Undo();
+  Redo();
+  Cut();
+  Copy();
+  CopyToFindPboard();
+  Paste();
+  PasteAndMatchStyle();
+  Delete();
+  SelectAll();
+  CollapseSelection();
+
+  // Replaces the selected region or a word around the cursor with the
+  // specified string.
+  Replace(mojo.common.mojom.String16 word);
+
+  // Replaces the misspelling in the selected region with the specified string.
+  ReplaceMisspelling(mojo.common.mojom.String16 word);
+
+  // Requests the renderer to select the region between two points.
+  // Expects a SelectRange_ACK message when finished.
+  SelectRange(gfx.mojom.Point base, gfx.mojom.Point extent);
+
+  // Sent by the browser to ask the renderer to adjust the selection start and
+  // end points by the given amounts. A negative amount moves the selection
+  // towards the beginning of the document, a positive amount moves the
+  // selection towards the end of the document.
+  AdjustSelectionByCharacterOffset(int32 start, int32 end);
+
+  // Requests the renderer to move the selection extent point to a new position.
+  // Expects a MoveRangeSelectionExtent_ACK message when finished.
+  MoveRangeSelectionExtent(gfx.mojom.Point extent);
+
+  // TODO(dtapuska): Implement WidgetInputHandler.
+  // GetWidgetInputHandler(associated WidgetInputHandler& interface_request);
+};
diff --git a/content/public/android/java/src/org/chromium/content/browser/SelectionPopupController.java b/content/public/android/java/src/org/chromium/content/browser/SelectionPopupController.java
index b332afa..4b7f15f 100644
--- a/content/public/android/java/src/org/chromium/content/browser/SelectionPopupController.java
+++ b/content/public/android/java/src/org/chromium/content/browser/SelectionPopupController.java
@@ -633,7 +633,7 @@
         int id = item.getItemId();
         int groupId = item.getGroupId();
 
-        if (id == mAssistMenuItemId) {
+        if (BuildInfo.isAtLeastO() && id == mAssistMenuItemId) {
             doAssistAction();
             mode.finish();
         } else if (id == R.id.select_action_menu_select_all) {
diff --git a/content/public/app/mojo/content_renderer_manifest.json b/content/public/app/mojo/content_renderer_manifest.json
index 9abc705..ddd80d4 100644
--- a/content/public/app/mojo/content_renderer_manifest.json
+++ b/content/public/app/mojo/content_renderer_manifest.json
@@ -42,6 +42,7 @@
           "blink::mojom::EngagementClient",
           "blink::mojom::InstallationService",
           "content::mojom::ImageDownloader",
+          "content::mojom::FrameInputHandler",
           "mojom::MediaDevicesListener"
         ]
       },
diff --git a/content/public/browser/DEPS b/content/public/browser/DEPS
index f2291a9..ca1ff35 100644
--- a/content/public/browser/DEPS
+++ b/content/public/browser/DEPS
@@ -1,5 +1,6 @@
 include_rules = [
   "+components/payments/mojom/payment_app.mojom.h",
+  "+content/common/input/input_handler.mojom.h",
   "+device/screen_orientation/public/interfaces",
   "+device/wake_lock/public/interfaces",
 ]
diff --git a/content/public/browser/render_frame_host.h b/content/public/browser/render_frame_host.h
index 5b23cbaf..e4dfc1f 100644
--- a/content/public/browser/render_frame_host.h
+++ b/content/public/browser/render_frame_host.h
@@ -10,6 +10,7 @@
 #include "base/callback_forward.h"
 #include "build/build_config.h"
 #include "content/common/content_export.h"
+#include "content/common/input/input_handler.mojom.h"
 #include "content/public/common/console_message_level.h"
 #include "content/public/common/file_chooser_params.h"
 #include "ipc/ipc_listener.h"
@@ -274,6 +275,8 @@
   // process to determine whether access to a feature is allowed.
   virtual bool IsFeatureEnabled(blink::WebFeaturePolicyFeature feature) = 0;
 
+  virtual mojom::FrameInputHandler* GetFrameInputHandler() = 0;
+
  private:
   // This interface should only be implemented inside content.
   friend class RenderFrameHostImpl;
diff --git a/content/public/common/content_switches.cc b/content/public/common/content_switches.cc
index 7e5f3f3f..3a01702 100644
--- a/content/public/common/content_switches.cc
+++ b/content/public/common/content_switches.cc
@@ -632,6 +632,9 @@
 const char kMHTMLSkipNostoreMain[]          = "skip-nostore-main";
 const char kMHTMLSkipNostoreAll[]           = "skip-nostore-all";
 
+// Use Mojo-based Input Event routing.
+const char kMojoInputMessages[] = "mojo-input-messages";
+
 // Use a Mojo-based LocalStorage implementation.
 const char kMojoLocalStorage[]              = "mojo-local-storage";
 
diff --git a/content/public/common/content_switches.h b/content/public/common/content_switches.h
index 3a5c975..2638248 100644
--- a/content/public/common/content_switches.h
+++ b/content/public/common/content_switches.h
@@ -193,6 +193,7 @@
 CONTENT_EXPORT extern const char kMHTMLGeneratorOption[];
 CONTENT_EXPORT extern const char kMHTMLSkipNostoreMain[];
 CONTENT_EXPORT extern const char kMHTMLSkipNostoreAll[];
+CONTENT_EXPORT extern const char kMojoInputMessages[];
 CONTENT_EXPORT extern const char kMojoLocalStorage[];
 CONTENT_EXPORT extern const char kMuteAudio[];
 CONTENT_EXPORT extern const char kNoReferrers[];
diff --git a/content/public/test/test_browser_thread_bundle.cc b/content/public/test/test_browser_thread_bundle.cc
index fa85212..6d0d4df2 100644
--- a/content/public/test/test_browser_thread_bundle.cc
+++ b/content/public/test/test_browser_thread_bundle.cc
@@ -10,6 +10,7 @@
 #include "base/run_loop.h"
 #include "base/task_scheduler/task_scheduler.h"
 #include "base/test/scoped_async_task_scheduler.h"
+#include "base/threading/sequenced_worker_pool.h"
 #include "content/browser/browser_thread_impl.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/test/test_browser_thread.h"
@@ -78,6 +79,12 @@
   // for DestructionObservers hooked to |message_loop_| to be able to invoke
   // BrowserThread::CurrentlyOn() -- ref. ~TestBrowserThread().
   message_loop_.reset();
+
+  // Disable redirection of SequencedWorkerPools to TaskScheduler. This is
+  // required in order to reset global state so that tests following this one in
+  // this process can still manage their own SequencedWorkerPool without using
+  // TestBrowserThreadBundle.
+  base::SequencedWorkerPool::EnableForProcess();
 }
 
 void TestBrowserThreadBundle::Init() {
@@ -123,6 +130,9 @@
         base::MakeUnique<base::test::ScopedAsyncTaskScheduler>();
   }
 
+  // Enable redirection of SequencedWorkerPools to TaskScheduler.
+  base::SequencedWorkerPool::EnableWithRedirectionToTaskSchedulerForProcess();
+
   if (options_ & REAL_DB_THREAD) {
     db_thread_ = base::MakeUnique<TestBrowserThread>(BrowserThread::DB);
     db_thread_->Start();
diff --git a/content/renderer/BUILD.gn b/content/renderer/BUILD.gn
index a71ae45e..4fa3a91c 100644
--- a/content/renderer/BUILD.gn
+++ b/content/renderer/BUILD.gn
@@ -157,6 +157,8 @@
     "ime_event_guard.h",
     "in_process_renderer_thread.cc",
     "in_process_renderer_thread.h",
+    "input/frame_input_handler_impl.cc",
+    "input/frame_input_handler_impl.h",
     "input/input_event_filter.cc",
     "input/input_event_filter.h",
     "input/input_handler_manager.cc",
diff --git a/content/renderer/input/frame_input_handler_impl.cc b/content/renderer/input/frame_input_handler_impl.cc
new file mode 100644
index 0000000..79be471
--- /dev/null
+++ b/content/renderer/input/frame_input_handler_impl.cc
@@ -0,0 +1,391 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/renderer/input/frame_input_handler_impl.h"
+
+#include <utility>
+
+#include "base/bind.h"
+#include "base/debug/stack_trace.h"
+#include "base/logging.h"
+#include "content/renderer/ime_event_guard.h"
+#include "content/renderer/render_thread_impl.h"
+#include "content/renderer/render_view_impl.h"
+#include "content/renderer/render_widget.h"
+#include "third_party/WebKit/public/web/WebLocalFrame.h"
+
+namespace content {
+
+FrameInputHandlerImpl::FrameInputHandlerImpl(
+    base::WeakPtr<RenderFrameImpl> render_frame,
+    mojom::FrameInputHandlerRequest request)
+    : binding_(this),
+      render_frame_(render_frame),
+      input_event_queue_(render_frame->GetRenderWidget()->GetInputEventQueue()),
+      main_thread_task_runner_(base::ThreadTaskRunnerHandle::Get()),
+      weak_ptr_factory_(this)
+
+{
+  weak_this_ = weak_ptr_factory_.GetWeakPtr();
+  // If we have created an input event queue move the mojo request over to the
+  // compositor thread.
+  if (input_event_queue_) {
+    // Mojo channel bound on compositor thread.
+    RenderThreadImpl::current()->compositor_task_runner()->PostTask(
+        FROM_HERE, base::BindOnce(&FrameInputHandlerImpl::BindNow,
+                                  base::Unretained(this), std::move(request)));
+  } else {
+    // Mojo channel bound on main thread.
+    BindNow(std::move(request));
+  }
+}
+
+FrameInputHandlerImpl::~FrameInputHandlerImpl() {}
+
+// static
+void FrameInputHandlerImpl::CreateMojoService(
+    base::WeakPtr<RenderFrameImpl> render_frame,
+    const service_manager::BindSourceInfo& source_info,
+    mojom::FrameInputHandlerRequest request) {
+  DCHECK(render_frame);
+
+  // Owns itself. Will be deleted when message pipe is destroyed.
+  new FrameInputHandlerImpl(render_frame, std::move(request));
+}
+
+void FrameInputHandlerImpl::RunOnMainThread(const base::Closure& closure) {
+  if (input_event_queue_) {
+    input_event_queue_->QueueClosure(closure);
+  } else {
+    closure.Run();
+  }
+}
+
+void FrameInputHandlerImpl::SetCompositionFromExistingText(
+    int32_t start,
+    int32_t end,
+    const std::vector<ui::CompositionUnderline>& ui_underlines) {
+  if (!main_thread_task_runner_->BelongsToCurrentThread()) {
+    RunOnMainThread(
+        base::Bind(&FrameInputHandlerImpl::SetCompositionFromExistingText,
+                   weak_this_, start, end, ui_underlines));
+    return;
+  }
+
+  if (!render_frame_)
+    return;
+
+  ImeEventGuard guard(render_frame_->GetRenderWidget());
+  std::vector<blink::WebCompositionUnderline> underlines;
+  for (const auto& underline : ui_underlines) {
+    blink::WebCompositionUnderline blink_underline(
+        underline.start_offset, underline.end_offset, underline.color,
+        underline.thick, underline.background_color);
+    underlines.push_back(blink_underline);
+  }
+
+  render_frame_->GetWebFrame()->SetCompositionFromExistingText(start, end,
+                                                               underlines);
+}
+
+void FrameInputHandlerImpl::ExtendSelectionAndDelete(int32_t before,
+                                                     int32_t after) {
+  if (!main_thread_task_runner_->BelongsToCurrentThread()) {
+    RunOnMainThread(base::Bind(&FrameInputHandlerImpl::ExtendSelectionAndDelete,
+                               weak_this_, before, after));
+    return;
+  }
+  if (!render_frame_)
+    return;
+  render_frame_->GetWebFrame()->ExtendSelectionAndDelete(before, after);
+}
+
+void FrameInputHandlerImpl::DeleteSurroundingText(int32_t before,
+                                                  int32_t after) {
+  if (!main_thread_task_runner_->BelongsToCurrentThread()) {
+    RunOnMainThread(base::Bind(&FrameInputHandlerImpl::DeleteSurroundingText,
+                               weak_this_, before, after));
+    return;
+  }
+  if (!render_frame_)
+    return;
+  render_frame_->GetWebFrame()->DeleteSurroundingText(before, after);
+}
+
+void FrameInputHandlerImpl::DeleteSurroundingTextInCodePoints(int32_t before,
+                                                              int32_t after) {
+  if (!main_thread_task_runner_->BelongsToCurrentThread()) {
+    RunOnMainThread(
+        base::Bind(&FrameInputHandlerImpl::DeleteSurroundingTextInCodePoints,
+                   weak_this_, before, after));
+    return;
+  }
+  if (!render_frame_)
+    return;
+  render_frame_->GetWebFrame()->DeleteSurroundingTextInCodePoints(before,
+                                                                  after);
+}
+
+void FrameInputHandlerImpl::SetEditableSelectionOffsets(int32_t start,
+                                                        int32_t end) {
+  if (!main_thread_task_runner_->BelongsToCurrentThread()) {
+    RunOnMainThread(
+        base::Bind(&FrameInputHandlerImpl::SetEditableSelectionOffsets,
+                   weak_this_, start, end));
+    return;
+  }
+  if (!render_frame_)
+    return;
+  render_frame_->GetWebFrame()->SetEditableSelectionOffsets(start, end);
+}
+
+void FrameInputHandlerImpl::ExecuteEditCommand(
+    const std::string& command,
+    const base::Optional<base::string16>& value) {
+  if (!main_thread_task_runner_->BelongsToCurrentThread()) {
+    RunOnMainThread(base::Bind(&FrameInputHandlerImpl::ExecuteEditCommand,
+                               weak_this_, command, value));
+    return;
+  }
+  if (!render_frame_)
+    return;
+  if (value) {
+    render_frame_->GetWebFrame()->ExecuteCommand(
+        blink::WebString::FromUTF8(command),
+        blink::WebString::FromUTF16(value.value()));
+    return;
+  }
+
+  render_frame_->GetWebFrame()->ExecuteCommand(
+      blink::WebString::FromUTF8(command));
+}
+
+void FrameInputHandlerImpl::Undo() {
+  RunOnMainThread(base::Bind(&FrameInputHandlerImpl::ExecuteCommandOnMainThread,
+                             weak_this_, "Undo", UpdateState::kNone));
+}
+
+void FrameInputHandlerImpl::Redo() {
+  RunOnMainThread(base::Bind(&FrameInputHandlerImpl::ExecuteCommandOnMainThread,
+                             weak_this_, "Redo", UpdateState::kNone));
+}
+
+void FrameInputHandlerImpl::Cut() {
+  RunOnMainThread(base::Bind(&FrameInputHandlerImpl::ExecuteCommandOnMainThread,
+                             weak_this_, "Cut",
+                             UpdateState::kIsSelectingRange));
+}
+
+void FrameInputHandlerImpl::Copy() {
+  RunOnMainThread(base::Bind(&FrameInputHandlerImpl::ExecuteCommandOnMainThread,
+                             weak_this_, "Copy",
+                             UpdateState::kIsSelectingRange));
+}
+
+void FrameInputHandlerImpl::CopyToFindPboard() {
+#if defined(OS_MACOSX)
+  if (!main_thread_task_runner_->BelongsToCurrentThread()) {
+    RunOnMainThread(
+        base::Bind(&FrameInputHandlerImpl::CopyToFindPboard, weak_this_));
+    return;
+  }
+  if (!render_frame_)
+    return;
+  render_frame_->OnCopyToFindPboard();
+#endif
+}
+
+void FrameInputHandlerImpl::Paste() {
+  RunOnMainThread(base::Bind(&FrameInputHandlerImpl::ExecuteCommandOnMainThread,
+                             weak_this_, "Paste", UpdateState::kIsPasting));
+}
+
+void FrameInputHandlerImpl::PasteAndMatchStyle() {
+  RunOnMainThread(base::Bind(&FrameInputHandlerImpl::ExecuteCommandOnMainThread,
+                             weak_this_, "PasteAndMatchStyle",
+                             UpdateState::kIsPasting));
+}
+
+void FrameInputHandlerImpl::Replace(const base::string16& word) {
+  if (!main_thread_task_runner_->BelongsToCurrentThread()) {
+    RunOnMainThread(
+        base::Bind(&FrameInputHandlerImpl::Replace, weak_this_, word));
+    return;
+  }
+  if (!render_frame_)
+    return;
+  blink::WebLocalFrame* frame = render_frame_->GetWebFrame();
+  if (frame->HasSelection())
+    frame->SelectWordAroundCaret();
+  frame->ReplaceSelection(blink::WebString::FromUTF16(word));
+  render_frame_->SyncSelectionIfRequired();
+}
+
+void FrameInputHandlerImpl::ReplaceMisspelling(const base::string16& word) {
+  if (!main_thread_task_runner_->BelongsToCurrentThread()) {
+    RunOnMainThread(base::Bind(&FrameInputHandlerImpl::ReplaceMisspelling,
+                               weak_this_, word));
+    return;
+  }
+  if (!render_frame_)
+    return;
+  blink::WebLocalFrame* frame = render_frame_->GetWebFrame();
+  if (!frame->HasSelection())
+    return;
+  frame->ReplaceMisspelledRange(blink::WebString::FromUTF16(word));
+}
+
+void FrameInputHandlerImpl::Delete() {
+  RunOnMainThread(base::Bind(&FrameInputHandlerImpl::ExecuteCommandOnMainThread,
+                             weak_this_, "Delete", UpdateState::kNone));
+}
+
+void FrameInputHandlerImpl::SelectAll() {
+  RunOnMainThread(base::Bind(&FrameInputHandlerImpl::ExecuteCommandOnMainThread,
+                             weak_this_, "SelectAll",
+                             UpdateState::kIsSelectingRange));
+}
+
+void FrameInputHandlerImpl::CollapseSelection() {
+  if (!main_thread_task_runner_->BelongsToCurrentThread()) {
+    RunOnMainThread(
+        base::Bind(&FrameInputHandlerImpl::CollapseSelection, weak_this_));
+    return;
+  }
+
+  if (!render_frame_)
+    return;
+  const blink::WebRange& range =
+      render_frame_->GetRenderWidget()->GetWebWidget()->CaretOrSelectionRange();
+  if (range.IsNull())
+    return;
+
+  HandlingState handling_state(render_frame_.get(),
+                               UpdateState::kIsSelectingRange);
+  render_frame_->GetWebFrame()->SelectRange(
+      blink::WebRange(range.EndOffset(), 0));
+}
+
+void FrameInputHandlerImpl::SelectRange(const gfx::Point& base,
+                                        const gfx::Point& extent) {
+  if (!main_thread_task_runner_->BelongsToCurrentThread()) {
+    // TODO(dtapuska): This event should be coalesced. Chrome IPC uses
+    // one outstanding event and an ACK to handle coalescing on the browser
+    // side. We should be able to clobber them in the main thread event queue.
+    RunOnMainThread(base::Bind(&FrameInputHandlerImpl::SelectRange, weak_this_,
+                               base, extent));
+    return;
+  }
+
+  if (!render_frame_)
+    return;
+  RenderViewImpl* render_view = render_frame_->render_view();
+  HandlingState handling_state(render_frame_.get(),
+                               UpdateState::kIsSelectingRange);
+  render_frame_->GetWebFrame()->SelectRange(
+      render_view->ConvertWindowPointToViewport(base),
+      render_view->ConvertWindowPointToViewport(extent));
+}
+
+void FrameInputHandlerImpl::AdjustSelectionByCharacterOffset(int32_t start,
+                                                             int32_t end) {
+  if (!main_thread_task_runner_->BelongsToCurrentThread()) {
+    RunOnMainThread(
+        base::Bind(&FrameInputHandlerImpl::AdjustSelectionByCharacterOffset,
+                   weak_this_, start, end));
+    return;
+  }
+
+  if (!render_frame_)
+    return;
+  blink::WebRange range =
+      render_frame_->GetRenderWidget()->GetWebWidget()->CaretOrSelectionRange();
+  if (range.IsNull())
+    return;
+
+  // Sanity checks to disallow empty and out of range selections.
+  if (start - end > range.length() || range.StartOffset() + start < 0)
+    return;
+
+  HandlingState handling_state(render_frame_.get(),
+                               UpdateState::kIsSelectingRange);
+  // A negative adjust amount moves the selection towards the beginning of
+  // the document, a positive amount moves the selection towards the end of
+  // the document.
+  render_frame_->GetWebFrame()->SelectRange(
+      blink::WebRange(range.StartOffset() + start,
+                      range.length() + end - start),
+      blink::WebLocalFrame::kPreserveHandleVisibility);
+}
+
+void FrameInputHandlerImpl::MoveRangeSelectionExtent(const gfx::Point& extent) {
+  if (!main_thread_task_runner_->BelongsToCurrentThread()) {
+    // TODO(dtapuska): This event should be coalesced. Chrome IPC uses
+    // one outstanding event and an ACK to handle coalescing on the browser
+    // side. We should be able to clobber them in the main thread event queue.
+    RunOnMainThread(base::Bind(&FrameInputHandlerImpl::MoveRangeSelectionExtent,
+                               weak_this_, extent));
+    return;
+  }
+
+  if (!render_frame_)
+    return;
+  HandlingState handling_state(render_frame_.get(),
+                               UpdateState::kIsSelectingRange);
+  render_frame_->GetWebFrame()->MoveRangeSelectionExtent(
+      render_frame_->render_view()->ConvertWindowPointToViewport(extent));
+}
+
+void FrameInputHandlerImpl::ExecuteCommandOnMainThread(
+    const std::string& command,
+    UpdateState update_state) {
+  if (!render_frame_)
+    return;
+
+  HandlingState handling_state(render_frame_.get(), update_state);
+  render_frame_->GetWebFrame()->ExecuteCommand(
+      blink::WebString::FromUTF8(command));
+}
+
+void FrameInputHandlerImpl::Release() {
+  if (!main_thread_task_runner_->BelongsToCurrentThread()) {
+    // Close the binding on the compositor thread first before telling the main
+    // thread to delete this object.
+    binding_.Close();
+    main_thread_task_runner_->PostTask(
+        FROM_HERE, base::Bind(&FrameInputHandlerImpl::Release, weak_this_));
+    return;
+  }
+  delete this;
+}
+
+void FrameInputHandlerImpl::BindNow(mojom::FrameInputHandlerRequest request) {
+  binding_.Bind(std::move(request));
+  binding_.set_connection_error_handler(
+      base::Bind(&FrameInputHandlerImpl::Release, base::Unretained(this)));
+}
+
+FrameInputHandlerImpl::HandlingState::HandlingState(
+    RenderFrameImpl* render_frame,
+    UpdateState state)
+    : render_frame_(render_frame),
+      original_select_range_value_(render_frame->handling_select_range()),
+      original_pasting_value_(render_frame->IsPasting()) {
+  switch (state) {
+    case UpdateState::kIsPasting:
+      render_frame->set_is_pasting(true);
+    case UpdateState::kIsSelectingRange:
+      render_frame->set_handling_select_range(true);
+      break;
+    case UpdateState::kNone:
+      break;
+  }
+}
+
+FrameInputHandlerImpl::HandlingState::~HandlingState() {
+  render_frame_->set_handling_select_range(original_select_range_value_);
+  render_frame_->set_is_pasting(original_pasting_value_);
+}
+
+}  // namespace content
diff --git a/content/renderer/input/frame_input_handler_impl.h b/content/renderer/input/frame_input_handler_impl.h
new file mode 100644
index 0000000..9c756029
--- /dev/null
+++ b/content/renderer/input/frame_input_handler_impl.h
@@ -0,0 +1,113 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_RENDERER_INPUT_FRAME_INPUT_HANDLER_IMPL_H_
+#define CONTENT_RENDERER_INPUT_FRAME_INPUT_HANDLER_IMPL_H_
+
+#include "base/memory/ref_counted.h"
+#include "content/common/input/input_handler.mojom.h"
+#include "content/renderer/render_frame_impl.h"
+#include "mojo/public/cpp/bindings/binding.h"
+
+namespace content {
+class MainThreadEventQueue;
+
+// This class provides an implementation of FrameInputHandler mojo interface.
+// When a compositor thread is being used in the renderer the mojo channel
+// is bound on the compositor thread. Method calls, and events received on the
+// compositor thread are then placed in the MainThreadEventQueue for
+// the associated RenderWidget. This is done as to ensure that input related
+// messages and events that are handled on the compositor thread aren't
+// executed before other input events that need to be processed on the
+// main thread. ie. Since some messages flow to the compositor thread
+// all input needs to flow there so the ordering of events is kept in sequence.
+//
+// eg. (B = Browser, CT = Compositor Thread, MT = Main Thread)
+//   B sends MouseEvent
+//   B sends Copy message
+//   CT receives MouseEvent (CT might do something with the MouseEvent)
+//   CT places MouseEvent in MainThreadEventQueue
+//   CT receives Copy message (CT has no use for the Copy message)
+//   CT places Copy message in MainThreadEventQueue
+//   MT receives MouseEvent
+//   MT receives Copy message
+//
+// When a compositor thread isn't used the mojo channel is just bound
+// on the main thread and messages are handled right away.
+class FrameInputHandlerImpl : public mojom::FrameInputHandler {
+ public:
+  static void CreateMojoService(
+      base::WeakPtr<RenderFrameImpl> render_frame,
+      const service_manager::BindSourceInfo& source_info,
+      mojom::FrameInputHandlerRequest request);
+
+  void SetCompositionFromExistingText(
+      int32_t start,
+      int32_t end,
+      const std::vector<ui::CompositionUnderline>& underlines) override;
+  void ExtendSelectionAndDelete(int32_t before, int32_t after) override;
+  void DeleteSurroundingText(int32_t before, int32_t after) override;
+  void DeleteSurroundingTextInCodePoints(int32_t before,
+                                         int32_t after) override;
+  void SetEditableSelectionOffsets(int32_t start, int32_t end) override;
+  void ExecuteEditCommand(const std::string& command,
+                          const base::Optional<base::string16>& value) override;
+  void Undo() override;
+  void Redo() override;
+  void Cut() override;
+  void Copy() override;
+  void CopyToFindPboard() override;
+  void Paste() override;
+  void PasteAndMatchStyle() override;
+  void Replace(const base::string16& word) override;
+  void ReplaceMisspelling(const base::string16& word) override;
+  void Delete() override;
+  void SelectAll() override;
+  void CollapseSelection() override;
+  void SelectRange(const gfx::Point& base, const gfx::Point& extent) override;
+  void AdjustSelectionByCharacterOffset(int32_t start, int32_t end) override;
+  void MoveRangeSelectionExtent(const gfx::Point& extent) override;
+
+ private:
+  ~FrameInputHandlerImpl() override;
+  enum class UpdateState { kNone, kIsPasting, kIsSelectingRange };
+
+  class HandlingState {
+   public:
+    HandlingState(RenderFrameImpl* render_frame, UpdateState state);
+    ~HandlingState();
+
+   private:
+    RenderFrameImpl* render_frame_;
+    bool original_select_range_value_;
+    bool original_pasting_value_;
+  };
+
+  FrameInputHandlerImpl(base::WeakPtr<RenderFrameImpl> render_frame,
+                        mojom::FrameInputHandlerRequest request);
+
+  void RunOnMainThread(const base::Closure& closure);
+  void BindNow(mojom::FrameInputHandlerRequest request);
+  void ExecuteCommandOnMainThread(const std::string& command,
+                                  UpdateState state);
+  void Release();
+
+  mojo::Binding<mojom::FrameInputHandler> binding_;
+
+  // |render_frame_| should only be accessed on the main thread. Use
+  // GetRenderFrame so that it will DCHECK this for you.
+  base::WeakPtr<RenderFrameImpl> render_frame_;
+
+  scoped_refptr<MainThreadEventQueue> input_event_queue_;
+  scoped_refptr<base::SingleThreadTaskRunner> main_thread_task_runner_;
+
+  base::WeakPtr<FrameInputHandlerImpl> weak_this_;
+  base::WeakPtrFactory<FrameInputHandlerImpl> weak_ptr_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(FrameInputHandlerImpl);
+};
+
+}  // namespace content
+
+#endif  // CONTENT_RENDERER_INPUT_FRAME_INPUT_HANDLER_IMPL_H_
diff --git a/content/renderer/pepper/pepper_video_decoder_host.cc b/content/renderer/pepper/pepper_video_decoder_host.cc
index 3aea65f..d3fb411d 100644
--- a/content/renderer/pepper/pepper_video_decoder_host.cc
+++ b/content/renderer/pepper/pepper_video_decoder_host.cc
@@ -502,8 +502,10 @@
                                     min_picture_count_);
   std::unique_ptr<VideoDecoderShim> new_decoder(
       new VideoDecoderShim(this, shim_texture_pool_size));
-  if (!new_decoder->Initialize(profile_, this))
+  if (!new_decoder->Initialize(media::VideoDecodeAccelerator::Config(profile_),
+                               this)) {
     return false;
+  }
 
   software_fallback_used_ = true;
   decoder_.reset(new_decoder.release());
diff --git a/content/renderer/render_frame_impl.cc b/content/renderer/render_frame_impl.cc
index d1e9d50..1959c8e 100644
--- a/content/renderer/render_frame_impl.cc
+++ b/content/renderer/render_frame_impl.cc
@@ -111,6 +111,7 @@
 #include "content/renderer/history_serialization.h"
 #include "content/renderer/image_downloader/image_downloader_impl.h"
 #include "content/renderer/ime_event_guard.h"
+#include "content/renderer/input/frame_input_handler_impl.h"
 #include "content/renderer/input/input_handler_manager.h"
 #include "content/renderer/installedapp/related_apps_fetcher.h"
 #include "content/renderer/internal_document_state_data.h"
@@ -6957,6 +6958,9 @@
   GetAssociatedInterfaceRegistry()->AddInterface(base::Bind(
       &RenderFrameImpl::BindFrameBindingsControl, weak_factory_.GetWeakPtr()));
 
+  GetInterfaceRegistry()->AddInterface(base::Bind(
+      &FrameInputHandlerImpl::CreateMojoService, weak_factory_.GetWeakPtr()));
+
   if (!frame_->Parent()) {
     // Only main frame have ImageDownloader service.
     GetInterfaceRegistry()->AddInterface(base::Bind(
diff --git a/content/renderer/render_frame_impl.h b/content/renderer/render_frame_impl.h
index f2449e2..169b4ba 100644
--- a/content/renderer/render_frame_impl.h
+++ b/content/renderer/render_frame_impl.h
@@ -734,6 +734,12 @@
   bool ScheduleFileChooser(const FileChooserParams& params,
                            blink::WebFileChooserCompletion* completion);
 
+  bool handling_select_range() const { return handling_select_range_; }
+
+  void set_is_pasting(bool value) { is_pasting_ = value; }
+
+  void set_handling_select_range(bool value) { handling_select_range_ = value; }
+
   // Plugin-related functions --------------------------------------------------
 
 #if BUILDFLAG(ENABLE_PLUGINS)
@@ -763,6 +769,16 @@
   void OnSetPepperVolume(int32_t pp_instance, double volume);
 #endif  // ENABLE_PLUGINS
 
+#if defined(OS_MACOSX)
+  void OnCopyToFindPboard();
+#endif
+
+  // Dispatches the current state of selection on the webpage to the browser if
+  // it has changed.
+  // TODO(varunjain): delete this method once we figure out how to keep
+  // selection handles in sync with the webpage.
+  void SyncSelectionIfRequired();
+
  protected:
   explicit RenderFrameImpl(const CreateParams& params);
 
@@ -959,10 +975,6 @@
 #endif
 #endif
 
-#if defined(OS_MACOSX)
-  void OnCopyToFindPboard();
-#endif
-
   // Callback scheduled from OnSerializeAsMHTML for when writing serialized
   // MHTML to file has been completed in the file thread.
   void OnWriteMHTMLToDiskComplete(
@@ -1015,12 +1027,6 @@
   void UpdateEncoding(blink::WebFrame* frame,
                       const std::string& encoding_name);
 
-  // Dispatches the current state of selection on the webpage to the browser if
-  // it has changed.
-  // TODO(varunjain): delete this method once we figure out how to keep
-  // selection handles in sync with the webpage.
-  void SyncSelectionIfRequired();
-
   bool RunJavaScriptDialog(JavaScriptDialogType type,
                            const base::string16& message,
                            const base::string16& default_value,
diff --git a/content/renderer/render_view_impl.cc b/content/renderer/render_view_impl.cc
index 034304b..301fc74 100644
--- a/content/renderer/render_view_impl.cc
+++ b/content/renderer/render_view_impl.cc
@@ -1230,7 +1230,6 @@
     IPC_MESSAGE_HANDLER(ViewMsg_MediaPlayerActionAt, OnMediaPlayerActionAt)
     IPC_MESSAGE_HANDLER(ViewMsg_PluginActionAt, OnPluginActionAt)
     IPC_MESSAGE_HANDLER(ViewMsg_SetActive, OnSetActive)
-    IPC_MESSAGE_HANDLER(ViewMsg_ShowContextMenu, OnShowContextMenu)
     IPC_MESSAGE_HANDLER(ViewMsg_ReleaseDisambiguationPopupBitmap,
                         OnReleaseDisambiguationPopupBitmap)
     IPC_MESSAGE_HANDLER(ViewMsg_ResolveTapDisambiguation,
@@ -1279,7 +1278,8 @@
     if (focused_frame) {
       input_handler_->set_handling_input_event(true);
       blink::WebRange initial_range = focused_frame->SelectionRange();
-      did_select = focused_frame->SelectWordAroundCaret();
+      if (!initial_range.IsNull())
+        did_select = focused_frame->SelectWordAroundCaret();
       if (did_select) {
         blink::WebRange adjusted_range = focused_frame->SelectionRange();
         start_adjust =
@@ -2411,20 +2411,6 @@
   date_time_picker_client_.reset(NULL);
 }
 
-#endif  // defined(OS_ANDROID)
-
-void RenderViewImpl::OnShowContextMenu(
-    ui::MenuSourceType source_type, const gfx::Point& location) {
-  input_handler_->set_context_menu_source_type(source_type);
-  has_host_context_menu_location_ = true;
-  host_context_menu_location_ = location;
-  if (webview())
-    webview()->ShowContextMenu(
-        static_cast<blink::WebMenuSourceType>(source_type));
-  has_host_context_menu_location_ = false;
-}
-
-#if defined(OS_ANDROID)
 bool RenderViewImpl::DidTapMultipleTargets(
     const WebSize& inner_viewport_offset,
     const WebRect& touch_rect,
diff --git a/content/renderer/render_view_impl.h b/content/renderer/render_view_impl.h
index 9c7c1a14..3e0a8e7 100644
--- a/content/renderer/render_view_impl.h
+++ b/content/renderer/render_view_impl.h
@@ -518,8 +518,6 @@
   void OnClosePage();
   void OnClose();
 
-  void OnShowContextMenu(ui::MenuSourceType source_type,
-                         const gfx::Point& location);
   void OnDeterminePageLanguage();
   void OnDisableScrollbarsForSmallWindows(
       const gfx::Size& disable_scrollbars_size_limit);
diff --git a/content/renderer/render_widget.cc b/content/renderer/render_widget.cc
index 6c5e6d5..40f6f5a 100644
--- a/content/renderer/render_widget.cc
+++ b/content/renderer/render_widget.cc
@@ -615,6 +615,7 @@
     IPC_MESSAGE_HANDLER(InputMsg_SetFocus, OnSetFocus)
     IPC_MESSAGE_HANDLER(InputMsg_SyntheticGestureCompleted,
                         OnSyntheticGestureCompleted)
+    IPC_MESSAGE_HANDLER(ViewMsg_ShowContextMenu, OnShowContextMenu)
     IPC_MESSAGE_HANDLER(ViewMsg_Close, OnClose)
     IPC_MESSAGE_HANDLER(ViewMsg_Resize, OnResize)
     IPC_MESSAGE_HANDLER(ViewMsg_EnableDeviceEmulation,
@@ -839,6 +840,10 @@
   Send(new InputHostMsg_HandleInputEvent_ACK(routing_id_, ack));
 }
 
+scoped_refptr<MainThreadEventQueue> RenderWidget::GetInputEventQueue() {
+  return input_event_queue_;
+}
+
 void RenderWidget::OnCursorVisibilityChange(bool is_visible) {
   if (GetWebWidget())
     GetWebWidget()->SetCursorVisibilityState(is_visible);
@@ -1586,6 +1591,18 @@
   }
 }
 
+void RenderWidget::OnShowContextMenu(ui::MenuSourceType source_type,
+                                     const gfx::Point& location) {
+  input_handler_->set_context_menu_source_type(source_type);
+  has_host_context_menu_location_ = true;
+  host_context_menu_location_ = location;
+  if (GetWebWidget()) {
+    GetWebWidget()->ShowContextMenu(
+        static_cast<blink::WebMenuSourceType>(source_type));
+  }
+  has_host_context_menu_location_ = false;
+}
+
 void RenderWidget::OnImeSetComposition(
     const base::string16& text,
     const std::vector<WebCompositionUnderline>& underlines,
diff --git a/content/renderer/render_widget.h b/content/renderer/render_widget.h
index de53c102..06552149 100644
--- a/content/renderer/render_widget.h
+++ b/content/renderer/render_widget.h
@@ -428,6 +428,8 @@
                          InputEventAckState ack_result,
                          uint32_t touch_event_id) override;
 
+  scoped_refptr<MainThreadEventQueue> GetInputEventQueue();
+
  protected:
   // Friend RefCounted so that the dtor can be non-public. Using this class
   // without ref-counting is an error.
@@ -505,6 +507,9 @@
   void OnCreateVideoAck(int32_t video_id);
   void OnUpdateVideoAck(int32_t video_id);
   void OnRequestMoveAck();
+  // Request from browser to show context menu.
+  virtual void OnShowContextMenu(ui::MenuSourceType source_type,
+                                 const gfx::Point& location);
   virtual void OnImeSetComposition(
       const base::string16& text,
       const std::vector<blink::WebCompositionUnderline>& underlines,
diff --git a/content/renderer/service_worker/service_worker_context_client.cc b/content/renderer/service_worker/service_worker_context_client.cc
index f181da5..3db1cf81 100644
--- a/content/renderer/service_worker/service_worker_context_client.cc
+++ b/content/renderer/service_worker/service_worker_context_client.cc
@@ -1067,6 +1067,7 @@
   payments::mojom::PaymentAppResponsePtr response =
       payments::mojom::PaymentAppResponse::New();
   response->method_name = web_response.method_name.Utf8();
+  response->stringified_details = web_response.stringified_details.Utf8();
   response_callback->OnPaymentAppResponse(
       std::move(response), base::Time::FromDoubleT(dispatch_event_time));
   context_->payment_response_callbacks.erase(payment_request_id);
diff --git a/device/bluetooth/bluetooth_adapter_mac.mm b/device/bluetooth/bluetooth_adapter_mac.mm
index ad78163..64dbb03 100644
--- a/device/bluetooth/bluetooth_adapter_mac.mm
+++ b/device/bluetooth/bluetooth_adapter_mac.mm
@@ -100,12 +100,12 @@
             initWithDiscoveryManager:low_energy_discovery_manager_.get()
                           andAdapter:this]);
     low_energy_central_manager_.reset([[CBCentralManager alloc]
-        initWithDelegate:low_energy_central_manager_delegate_.get()
+        initWithDelegate:low_energy_central_manager_delegate_
                    queue:dispatch_get_main_queue()]);
     low_energy_discovery_manager_->SetCentralManager(
-        low_energy_central_manager_.get());
+        low_energy_central_manager_);
   }
-  DCHECK(classic_discovery_manager_.get());
+  DCHECK(classic_discovery_manager_);
 }
 
 BluetoothAdapterMac::~BluetoothAdapterMac() {
@@ -293,8 +293,7 @@
   central_manager.delegate = low_energy_central_manager_delegate_;
   low_energy_central_manager_.reset(central_manager,
                                     base::scoped_policy::RETAIN);
-  low_energy_discovery_manager_->SetCentralManager(
-      low_energy_central_manager_.get());
+  low_energy_discovery_manager_->SetCentralManager(low_energy_central_manager_);
 }
 
 CBCentralManager* BluetoothAdapterMac::GetCentralManager() {
diff --git a/device/bluetooth/bluetooth_adapter_mac_unittest.mm b/device/bluetooth/bluetooth_adapter_mac_unittest.mm
index b330b3fa..8dc4c00 100644
--- a/device/bluetooth/bluetooth_adapter_mac_unittest.mm
+++ b/device/bluetooth/bluetooth_adapter_mac_unittest.mm
@@ -69,7 +69,7 @@
     }
     base::scoped_nsobject<MockCBPeripheral> mock_peripheral(
         [[MockCBPeripheral alloc] initWithUTF8StringIdentifier:identifier]);
-    return [mock_peripheral.get().peripheral retain];
+    return [[mock_peripheral peripheral] retain];
   }
 
   NSDictionary* AdvertisementData() {
@@ -100,7 +100,7 @@
     mock_central_manager_.reset([[MockCentralManager alloc] init]);
     [mock_central_manager_ setState:desired_state];
     CBCentralManager* centralManager =
-        (CBCentralManager*)mock_central_manager_.get();
+        static_cast<CBCentralManager*>(mock_central_manager_.get());
     adapter_mac_->SetCentralManagerForTesting(centralManager);
     return true;
   }
@@ -234,7 +234,7 @@
     return;
   base::scoped_nsobject<CBPeripheral> mock_peripheral(
       CreateMockPeripheral(kTestNSUUID));
-  if (mock_peripheral.get() == nil)
+  if (!mock_peripheral)
     return;
   EXPECT_EQ(kTestHashAddress, GetHashAddress(mock_peripheral));
 }
@@ -244,7 +244,7 @@
     return;
   base::scoped_nsobject<CBPeripheral> mock_peripheral(
       CreateMockPeripheral(kTestNSUUID));
-  if (mock_peripheral.get() == nil)
+  if (!mock_peripheral)
     return;
   base::scoped_nsobject<NSDictionary> advertisement_data(AdvertisementData());
 
diff --git a/device/bluetooth/bluetooth_low_energy_device_mac.mm b/device/bluetooth/bluetooth_low_energy_device_mac.mm
index 80de028..2f2cfa9b 100644
--- a/device/bluetooth/bluetooth_low_energy_device_mac.mm
+++ b/device/bluetooth/bluetooth_low_energy_device_mac.mm
@@ -31,7 +31,7 @@
       connected_(false),
       discovery_pending_count_(0) {
   DCHECK(BluetoothAdapterMac::IsLowEnergyAvailable());
-  DCHECK(peripheral_.get());
+  DCHECK(peripheral_);
   peripheral_delegate_.reset([[BluetoothLowEnergyPeripheralDelegate alloc]
       initWithBluetoothLowEnergyDeviceMac:this]);
   [peripheral_ setDelegate:peripheral_delegate_];
diff --git a/device/bluetooth/bluetooth_remote_gatt_characteristic_mac.mm b/device/bluetooth/bluetooth_remote_gatt_characteristic_mac.mm
index 4c9d99f8..900f3f66 100644
--- a/device/bluetooth/bluetooth_remote_gatt_characteristic_mac.mm
+++ b/device/bluetooth/bluetooth_remote_gatt_characteristic_mac.mm
@@ -74,11 +74,11 @@
       gatt_service_(gatt_service),
       cb_characteristic_(cb_characteristic, base::scoped_policy::RETAIN),
       weak_ptr_factory_(this) {
-  uuid_ = BluetoothAdapterMac::BluetoothUUIDWithCBUUID(
-      [cb_characteristic_.get() UUID]);
+  uuid_ =
+      BluetoothAdapterMac::BluetoothUUIDWithCBUUID([cb_characteristic_ UUID]);
   identifier_ = base::SysNSStringToUTF8(
       [NSString stringWithFormat:@"%s-%p", uuid_.canonical_value().c_str(),
-                                 (void*)cb_characteristic_]);
+                                 cb_characteristic_.get()]);
 }
 
 BluetoothRemoteGattCharacteristicMac::~BluetoothRemoteGattCharacteristicMac() {
@@ -104,7 +104,7 @@
 
 BluetoothGattCharacteristic::Properties
 BluetoothRemoteGattCharacteristicMac::GetProperties() const {
-  return ConvertProperties(cb_characteristic_.get().properties);
+  return ConvertProperties([cb_characteristic_ properties]);
 }
 
 BluetoothGattCharacteristic::Permissions
@@ -125,7 +125,7 @@
 }
 
 bool BluetoothRemoteGattCharacteristicMac::IsNotifying() const {
-  return cb_characteristic_.get().isNotifying == YES;
+  return [cb_characteristic_ isNotifying] == YES;
 }
 
 std::vector<BluetoothRemoteGattDescriptor*>
@@ -223,8 +223,7 @@
   DCHECK(unsubscribe_from_notification_callbacks_.second.is_null());
   subscribe_to_notification_callbacks_ =
       std::make_pair(callback, error_callback);
-  [GetCBPeripheral() setNotifyValue:YES
-                  forCharacteristic:cb_characteristic_.get()];
+  [GetCBPeripheral() setNotifyValue:YES forCharacteristic:cb_characteristic_];
 }
 
 void BluetoothRemoteGattCharacteristicMac::UnsubscribeFromNotifications(
@@ -238,16 +237,14 @@
   DCHECK(unsubscribe_from_notification_callbacks_.second.is_null());
   unsubscribe_from_notification_callbacks_ =
       std::make_pair(callback, error_callback);
-  [GetCBPeripheral() setNotifyValue:NO
-                  forCharacteristic:cb_characteristic_.get()];
+  [GetCBPeripheral() setNotifyValue:NO forCharacteristic:cb_characteristic_];
 }
 
 void BluetoothRemoteGattCharacteristicMac::DiscoverDescriptors() {
   VLOG(1) << *this << ": Discover descriptors.";
   is_discovery_complete_ = false;
   ++discovery_pending_count_;
-  [GetCBPeripheral()
-      discoverDescriptorsForCharacteristic:cb_characteristic_.get()];
+  [GetCBPeripheral() discoverDescriptorsForCharacteristic:cb_characteristic_];
 }
 
 void BluetoothRemoteGattCharacteristicMac::DidUpdateValue(NSError* error) {
@@ -286,7 +283,7 @@
 }
 
 void BluetoothRemoteGattCharacteristicMac::UpdateValue() {
-  NSData* nsdata_value = cb_characteristic_.get().value;
+  NSData* nsdata_value = [cb_characteristic_ value];
   const uint8_t* buffer = static_cast<const uint8_t*>(nsdata_value.bytes);
   value_.assign(buffer, buffer + nsdata_value.length);
 }
@@ -372,7 +369,7 @@
     descriptor_identifier_to_remove.insert(iter.first);
   }
 
-  for (CBDescriptor* cb_descriptor in cb_characteristic_.get().descriptors) {
+  for (CBDescriptor* cb_descriptor in [cb_characteristic_ descriptors]) {
     BluetoothRemoteGattDescriptorMac* gatt_descriptor_mac =
         GetBluetoothRemoteGattDescriptorMac(cb_descriptor);
     if (gatt_descriptor_mac) {
@@ -427,7 +424,7 @@
 
 CBCharacteristic* BluetoothRemoteGattCharacteristicMac::GetCBCharacteristic()
     const {
-  return cb_characteristic_.get();
+  return cb_characteristic_;
 }
 
 BluetoothAdapterMac* BluetoothRemoteGattCharacteristicMac::GetMacAdapter()
diff --git a/device/bluetooth/bluetooth_remote_gatt_descriptor_mac.mm b/device/bluetooth/bluetooth_remote_gatt_descriptor_mac.mm
index a8ede9c..173adcdf 100644
--- a/device/bluetooth/bluetooth_remote_gatt_descriptor_mac.mm
+++ b/device/bluetooth/bluetooth_remote_gatt_descriptor_mac.mm
@@ -51,7 +51,7 @@
   uuid_ = BluetoothAdapterMac::BluetoothUUIDWithCBUUID([cb_descriptor_ UUID]);
   identifier_ = base::SysNSStringToUTF8(
       [NSString stringWithFormat:@"%s-%p", uuid_.canonical_value().c_str(),
-                                 (void*)cb_descriptor_]);
+                                 cb_descriptor_.get()]);
 }
 
 std::string BluetoothRemoteGattDescriptorMac::GetIdentifier() const {
diff --git a/device/bluetooth/bluetooth_remote_gatt_service_mac.mm b/device/bluetooth/bluetooth_remote_gatt_service_mac.mm
index 15ec871..06ed4199 100644
--- a/device/bluetooth/bluetooth_remote_gatt_service_mac.mm
+++ b/device/bluetooth/bluetooth_remote_gatt_service_mac.mm
@@ -26,10 +26,10 @@
       is_primary_(is_primary),
       is_discovery_complete_(false),
       discovery_pending_count_(0) {
-  uuid_ = BluetoothAdapterMac::BluetoothUUIDWithCBUUID([service_.get() UUID]);
+  uuid_ = BluetoothAdapterMac::BluetoothUUIDWithCBUUID([service_ UUID]);
   identifier_ = base::SysNSStringToUTF8(
       [NSString stringWithFormat:@"%s-%p", uuid_.canonical_value().c_str(),
-                                 (void*)service_]);
+                                 service_.get()]);
 }
 
 BluetoothRemoteGattServiceMac::~BluetoothRemoteGattServiceMac() {}
@@ -214,7 +214,7 @@
 }
 
 CBService* BluetoothRemoteGattServiceMac::GetService() const {
-  return service_.get();
+  return service_;
 }
 
 BluetoothRemoteGattCharacteristicMac*
diff --git a/device/bluetooth/test/bluetooth_test_mac.mm b/device/bluetooth/test/bluetooth_test_mac.mm
index 3c5e8000..5f16078 100644
--- a/device/bluetooth/test/bluetooth_test_mac.mm
+++ b/device/bluetooth/test/bluetooth_test_mac.mm
@@ -38,7 +38,7 @@
   }
 
   // Returns MockCentralManager instance.
-  MockCentralManager* get() { return mock_central_manager_.get(); };
+  MockCentralManager* get() { return mock_central_manager_; };
 
  private:
   scoped_nsobject<MockCentralManager> mock_central_manager_;
@@ -235,10 +235,10 @@
   scoped_nsobject<MockCBPeripheral> mock_peripheral([[MockCBPeripheral alloc]
       initWithUTF8StringIdentifier:identifier
                               name:name]);
-  mock_peripheral.get().bluetoothTestMac = this;
+  [mock_peripheral setBluetoothTestMac:this];
   [central_manager_delegate
              centralManager:central_manager
-      didDiscoverPeripheral:mock_peripheral.get().peripheral
+      didDiscoverPeripheral:[mock_peripheral peripheral]
           advertisementData:CreateAdvertisementData(name, uuids, service_data,
                                                     tx_power)
                        RSSI:rssi];
@@ -276,14 +276,14 @@
   }
   DCHECK(name);
   DCHECK(identifier);
-  DCHECK([cbUUIDs.get() count] > 0);
+  DCHECK([cbUUIDs count] > 0);
   scoped_nsobject<MockCBPeripheral> mock_peripheral([[MockCBPeripheral alloc]
       initWithUTF8StringIdentifier:identifier
                               name:name]);
-  mock_peripheral.get().bluetoothTestMac = this;
+  [mock_peripheral setBluetoothTestMac:this];
   [mock_central_manager_->get()
-      setConnectedMockPeripheral:mock_peripheral.get().peripheral
-                withServiceUUIDs:cbUUIDs.get()];
+      setConnectedMockPeripheral:[mock_peripheral peripheral]
+                withServiceUUIDs:cbUUIDs];
 }
 
 void BluetoothTestMac::SimulateGattConnectionError(
diff --git a/device/bluetooth/test/mock_bluetooth_cbcharacteristic_mac.mm b/device/bluetooth/test/mock_bluetooth_cbcharacteristic_mac.mm
index 0a27de2..2fc9060 100644
--- a/device/bluetooth/test/mock_bluetooth_cbcharacteristic_mac.mm
+++ b/device/bluetooth/test/mock_bluetooth_cbcharacteristic_mac.mm
@@ -192,11 +192,11 @@
   scoped_nsobject<MockCBDescriptor> descriptor_mock([[MockCBDescriptor alloc]
       initWithCharacteristic:self.characteristic
                       CBUUID:uuid]);
-  [_descriptors.get() addObject:descriptor_mock];
+  [_descriptors addObject:descriptor_mock];
 }
 
 - (CBUUID*)UUID {
-  return _UUID.get();
+  return _UUID;
 }
 
 - (CBCharacteristic*)characteristic {
@@ -216,7 +216,7 @@
 }
 
 - (id)value {
-  return _value.get();
+  return _value;
 }
 
 - (BOOL)isNotifying {
diff --git a/device/bluetooth/test/mock_bluetooth_cbdescriptor_mac.mm b/device/bluetooth/test/mock_bluetooth_cbdescriptor_mac.mm
index 3a2f6152..1e0d82b 100644
--- a/device/bluetooth/test/mock_bluetooth_cbdescriptor_mac.mm
+++ b/device/bluetooth/test/mock_bluetooth_cbdescriptor_mac.mm
@@ -48,11 +48,11 @@
 }
 
 - (CBUUID*)UUID {
-  return _UUID.get();
+  return _UUID;
 }
 
 - (NSData*)value {
-  return _value.get();
+  return _value;
 }
 
 - (CBDescriptor*)descriptor {
diff --git a/device/bluetooth/test/mock_bluetooth_cbperipheral_mac.mm b/device/bluetooth/test/mock_bluetooth_cbperipheral_mac.mm
index ef7667b..9a2bbb8 100644
--- a/device/bluetooth/test/mock_bluetooth_cbperipheral_mac.mm
+++ b/device/bluetooth/test/mock_bluetooth_cbperipheral_mac.mm
@@ -123,11 +123,11 @@
 }
 
 - (void)removeAllServices {
-  [_services.get() removeAllObjects];
+  [_services removeAllObjects];
 }
 
 - (void)addServices:(NSArray*)services {
-  if (!_services.get()) {
+  if (!_services) {
     _services.reset([[NSMutableArray alloc] init]);
   }
   for (CBUUID* uuid in services) {
@@ -135,7 +135,7 @@
         initWithPeripheral:self.peripheral
                     CBUUID:uuid
                    primary:YES]);
-    [_services.get() addObject:service.get().service];
+    [_services addObject:[service service]];
   }
 }
 
@@ -147,7 +147,7 @@
   base::scoped_nsobject<CBService> serviceToRemove(service,
                                                    base::scoped_policy::RETAIN);
   DCHECK(serviceToRemove);
-  [_services.get() removeObject:serviceToRemove];
+  [_services removeObject:serviceToRemove];
   [self didModifyServices:@[ serviceToRemove ]];
 }
 
@@ -200,15 +200,15 @@
 }
 
 - (NSUUID*)identifier {
-  return _identifier.get();
+  return _identifier;
 }
 
 - (NSString*)name {
-  return _name.get();
+  return _name;
 }
 
 - (NSArray*)services {
-  return _services.get();
+  return _services;
 }
 
 - (CBPeripheral*)peripheral {
diff --git a/device/bluetooth/test/mock_bluetooth_cbservice_mac.mm b/device/bluetooth/test/mock_bluetooth_cbservice_mac.mm
index e82527d..cdd1516 100644
--- a/device/bluetooth/test/mock_bluetooth_cbservice_mac.mm
+++ b/device/bluetooth/test/mock_bluetooth_cbservice_mac.mm
@@ -60,7 +60,7 @@
 }
 
 - (CBUUID*)UUID {
-  return _UUID.get();
+  return _UUID;
 }
 
 - (void)addCharacteristicWithUUID:(CBUUID*)cb_uuid properties:(int)properties {
@@ -68,11 +68,11 @@
       [[MockCBCharacteristic alloc] initWithService:self.service
                                              CBUUID:cb_uuid
                                          properties:properties]);
-  [_characteristics.get() addObject:characteristic_mock];
+  [_characteristics addObject:characteristic_mock];
 }
 
 - (void)removeCharacteristicMock:(MockCBCharacteristic*)characteristic_mock {
-  [_characteristics.get() removeObject:characteristic_mock];
+  [_characteristics removeObject:characteristic_mock];
 }
 
 - (CBService*)service {
@@ -80,7 +80,7 @@
 }
 
 - (NSArray*)characteristics {
-  return _characteristics.get();
+  return _characteristics;
 }
 
 @end
diff --git a/device/bluetooth/test/mock_bluetooth_central_manager_mac.mm b/device/bluetooth/test/mock_bluetooth_central_manager_mac.mm
index fa0bd848..2a571246 100644
--- a/device/bluetooth/test/mock_bluetooth_central_manager_mac.mm
+++ b/device/bluetooth/test/mock_bluetooth_central_manager_mac.mm
@@ -78,16 +78,16 @@
 }
 
 - (NSArray*)retrieveConnectedPeripheralServiceUUIDs {
-  return [[_retrieveConnectedPeripheralServiceUUIDs.get() copy] autorelease];
+  return [[_retrieveConnectedPeripheralServiceUUIDs copy] autorelease];
 }
 
 - (NSArray*)retrieveConnectedPeripheralsWithServices:(NSArray*)services {
-  [_retrieveConnectedPeripheralServiceUUIDs.get()
+  [_retrieveConnectedPeripheralServiceUUIDs
       addObjectsFromArray:[services copy]];
   NSMutableArray* connectedPeripherals = [[NSMutableArray alloc] init];
   for (CBUUID* uuid in services) {
     NSSet* peripheralSet =
-        [_connectedMockPeripheralPerServiceUUID.get() objectForKey:uuid];
+        [_connectedMockPeripheralPerServiceUUID objectForKey:uuid];
     [connectedPeripherals addObjectsFromArray:peripheralSet.allObjects];
   }
   return connectedPeripherals;
@@ -97,11 +97,11 @@
                   withServiceUUIDs:(NSSet*)serviceUUIDs {
   for (CBUUID* uuid in serviceUUIDs) {
     NSMutableSet* peripheralSet =
-        [_connectedMockPeripheralPerServiceUUID.get() objectForKey:uuid];
+        [_connectedMockPeripheralPerServiceUUID objectForKey:uuid];
     if (!peripheralSet) {
       peripheralSet = [NSMutableSet set];
-      [_connectedMockPeripheralPerServiceUUID.get() setObject:peripheralSet
-                                                       forKey:uuid];
+      [_connectedMockPeripheralPerServiceUUID setObject:peripheralSet
+                                                 forKey:uuid];
     }
     [peripheralSet addObject:peripheral];
   }
diff --git a/device/vr/vr_types.h b/device/vr/vr_types.h
index a030bd9..a6980413 100644
--- a/device/vr/vr_types.h
+++ b/device/vr/vr_types.h
@@ -25,13 +25,6 @@
   float angle;
 } RotationAxisAngle;
 
-typedef struct Colorf {
-  float r;
-  float g;
-  float b;
-  float a;
-} Colorf;
-
 // A floating point quaternion, in JPL format.
 typedef struct Quatf {
   /// qx, qy, qz are the vector component.
diff --git a/extensions/browser/BUILD.gn b/extensions/browser/BUILD.gn
index 286ca631..04aafe1 100644
--- a/extensions/browser/BUILD.gn
+++ b/extensions/browser/BUILD.gn
@@ -5,6 +5,8 @@
 import("//build/config/features.gni")
 import("//extensions/features/features.gni")
 
+assert(enable_extensions)
+
 group("browser") {
   public_deps = [
     "//extensions/browser:browser_context_keyed_service_factories",
@@ -12,14 +14,10 @@
     "//extensions/browser/api:api_registration",
   ]
 
-  if (enable_extensions) {
-    # Includes all API implementations and the ExtensionsApiClient
-    # interface. Moving an API from src/chrome to src/extensions implies
-    # it can be cleanly disabled with enable_extensions=false.
-    # TODO: Eventually the entire extensions module should not be built
-    # when enable_extensions=false.
-    public_deps += [ "//extensions/browser/api" ]
-  }
+  # Includes all API implementations and the ExtensionsApiClient
+  # interface. Moving an API from src/chrome to src/extensions implies
+  # it can be cleanly disabled with enable_extensions=false.
+  public_deps += [ "//extensions/browser/api" ]
 }
 
 # Isolate the instantiation of BrowserContextKeyedServiceFactories.
@@ -40,17 +38,231 @@
 source_set("browser_sources") {
   visibility = [ "./*" ]
 
-  sources = []
+  sources = [
+    "api_activity_monitor.cc",
+    "api_activity_monitor.h",
+    "app_sorting.h",
+    "bad_message.cc",
+    "bad_message.h",
+    "blacklist_state.h",
+    "blob_holder.cc",
+    "blob_holder.h",
+    "blocked_action_type.h",
+    "browser_context_keyed_api_factory.h",
+    "component_extension_resource_manager.h",
+    "computed_hashes.cc",
+    "computed_hashes.h",
+    "content_hash_fetcher.cc",
+    "content_hash_fetcher.h",
+    "content_hash_reader.cc",
+    "content_hash_reader.h",
+    "content_hash_tree.cc",
+    "content_hash_tree.h",
+    "content_verifier.cc",
+    "content_verifier.h",
+    "content_verifier_delegate.h",
+    "content_verifier_io_data.cc",
+    "content_verifier_io_data.h",
+    "content_verify_job.cc",
+    "content_verify_job.h",
+    "crx_file_info.cc",
+    "crx_file_info.h",
+    "declarative_user_script_manager.cc",
+    "declarative_user_script_manager.h",
+    "declarative_user_script_manager_factory.cc",
+    "declarative_user_script_manager_factory.h",
+    "declarative_user_script_master.cc",
+    "declarative_user_script_master.h",
+    "deferred_start_render_host.h",
+    "deferred_start_render_host_observer.h",
+    "error_map.cc",
+    "error_map.h",
+    "event_listener_map.cc",
+    "event_listener_map.h",
+    "event_page_tracker.h",
+    "event_router.cc",
+    "event_router.h",
+    "event_router_factory.cc",
+    "event_router_factory.h",
+    "extension_api_frame_id_map.cc",
+    "extension_api_frame_id_map.h",
+    "extension_dialog_auto_confirm.cc",
+    "extension_dialog_auto_confirm.h",
+    "extension_error.cc",
+    "extension_error.h",
+    "extension_function.cc",
+    "extension_function.h",
+    "extension_function_dispatcher.cc",
+    "extension_function_dispatcher.h",
+    "extension_function_registry.cc",
+    "extension_function_registry.h",
+    "extension_host.cc",
+    "extension_host.h",
+    "extension_host_delegate.h",
+    "extension_host_observer.h",
+    "extension_host_queue.h",
+    "extension_icon_image.cc",
+    "extension_icon_image.h",
+    "extension_icon_placeholder.cc",
+    "extension_icon_placeholder.h",
+    "extension_message_filter.cc",
+    "extension_message_filter.h",
+    "extension_navigation_throttle.cc",
+    "extension_navigation_throttle.h",
+    "extension_navigation_ui_data.cc",
+    "extension_navigation_ui_data.h",
+    "extension_pref_store.cc",
+    "extension_pref_store.h",
+    "extension_pref_value_map.cc",
+    "extension_pref_value_map.h",
+    "extension_pref_value_map_factory.cc",
+    "extension_pref_value_map_factory.h",
+    "extension_prefs.cc",
+    "extension_prefs.h",
+    "extension_prefs_factory.cc",
+    "extension_prefs_factory.h",
+    "extension_prefs_observer.h",
+    "extension_prefs_scope.h",
+    "extension_protocols.cc",
+    "extension_protocols.h",
+    "extension_registry.cc",
+    "extension_registry.h",
+    "extension_registry_factory.cc",
+    "extension_registry_factory.h",
+    "extension_registry_observer.h",
+    "extension_request_limiting_throttle.cc",
+    "extension_request_limiting_throttle.h",
+    "extension_scoped_prefs.h",
+    "extension_service_worker_message_filter.cc",
+    "extension_service_worker_message_filter.h",
+    "extension_system.cc",
+    "extension_system.h",
+    "extension_system_provider.cc",
+    "extension_system_provider.h",
+    "extension_throttle_entry.cc",
+    "extension_throttle_entry.h",
+    "extension_throttle_entry_interface.h",
+    "extension_throttle_manager.cc",
+    "extension_throttle_manager.h",
+    "extension_user_script_loader.cc",
+    "extension_user_script_loader.h",
+    "extension_util.cc",
+    "extension_util.h",
+    "extension_web_contents_observer.cc",
+    "extension_web_contents_observer.h",
+    "extension_zoom_request_client.cc",
+    "extension_zoom_request_client.h",
+    "extensions_browser_client.cc",
+    "extensions_browser_client.h",
+    "external_install_info.cc",
+    "external_install_info.h",
+    "external_provider_interface.h",
+    "file_highlighter.cc",
+    "file_highlighter.h",
+    "file_reader.cc",
+    "file_reader.h",
+    "granted_file_entry.cc",
+    "granted_file_entry.h",
+    "image_loader.cc",
+    "image_loader.h",
+    "image_loader_factory.cc",
+    "image_loader_factory.h",
+    "info_map.cc",
+    "info_map.h",
+    "install_flag.h",
+    "io_thread_extension_message_filter.cc",
+    "io_thread_extension_message_filter.h",
+    "lazy_background_task_queue.cc",
+    "lazy_background_task_queue.h",
+    "lazy_background_task_queue_factory.cc",
+    "lazy_background_task_queue_factory.h",
+    "load_monitoring_extension_host_queue.cc",
+    "load_monitoring_extension_host_queue.h",
+    "management_policy.cc",
+    "management_policy.h",
+    "mojo/keep_alive_impl.cc",
+    "mojo/keep_alive_impl.h",
+    "mojo/service_registration.cc",
+    "mojo/service_registration.h",
+    "notification_types.cc",
+    "notification_types.h",
+    "null_app_sorting.cc",
+    "null_app_sorting.h",
+    "policy_check.cc",
+    "policy_check.h",
+    "pref_names.cc",
+    "pref_names.h",
+    "preload_check.cc",
+    "preload_check.h",
+    "preload_check_group.cc",
+    "preload_check_group.h",
+    "process_manager.cc",
+    "process_manager.h",
+    "process_manager_delegate.h",
+    "process_manager_factory.cc",
+    "process_manager_factory.h",
+    "process_manager_observer.h",
+    "process_map.cc",
+    "process_map.h",
+    "process_map_factory.cc",
+    "process_map_factory.h",
+    "quota_service.cc",
+    "quota_service.h",
+    "renderer_startup_helper.cc",
+    "renderer_startup_helper.h",
+    "requirements_checker.cc",
+    "requirements_checker.h",
+    "runtime_data.cc",
+    "runtime_data.h",
+    "sandboxed_unpacker.cc",
+    "sandboxed_unpacker.h",
+    "script_execution_observer.h",
+    "script_executor.cc",
+    "script_executor.h",
+    "serial_extension_host_queue.cc",
+    "serial_extension_host_queue.h",
+    "service_worker_manager.cc",
+    "service_worker_manager.h",
+    "state_store.cc",
+    "state_store.h",
+    "suggest_permission_util.cc",
+    "suggest_permission_util.h",
+    "uninstall_ping_sender.cc",
+    "uninstall_ping_sender.h",
+    "uninstall_reason.h",
+    "update_observer.h",
+    "url_request_util.cc",
+    "url_request_util.h",
+    "user_script_loader.cc",
+    "user_script_loader.h",
+    "verified_contents.cc",
+    "verified_contents.h",
+    "view_type_utils.cc",
+    "view_type_utils.h",
+    "warning_service.cc",
+    "warning_service.h",
+    "warning_service_factory.cc",
+    "warning_service_factory.h",
+    "warning_set.cc",
+    "warning_set.h",
+    "web_ui_user_script_loader.cc",
+    "web_ui_user_script_loader.h",
+  ]
 
   deps = [
     "//base:i18n",
     "//components/cast_certificate",
+    "//components/crx_file",
     "//components/guest_view/browser",
     "//components/keyed_service/content",
     "//components/keyed_service/core",
     "//components/pref_registry",
+    "//components/prefs",
     "//components/sessions",
+    "//components/sync",
     "//components/update_client",
+    "//components/update_client",
+    "//components/variations",
     "//components/version_info",
     "//components/web_cache/browser",
     "//components/web_modal",
@@ -58,256 +270,35 @@
     "//content/public/browser",
     "//content/public/common",
     "//crypto:platform",
+    "//crypto:platform",
+    "//extensions:extensions_browser_resources",
+    "//extensions/common",
     "//extensions/common",
     "//extensions/common/api",
     "//extensions/features",
     "//extensions/strings",
     "//google_apis",
+    "//services/preferences/public/cpp",
+    "//services/service_manager/public/cpp",
     "//ui/display",
   ]
 
+  public_deps = [
+    "//extensions/browser/app_window",
+    "//extensions/browser/guest_view",
+    "//extensions/browser/install",
+    "//extensions/browser/kiosk",
+    "//extensions/browser/updater",
+    "//extensions/browser/value_store",
+    "//ipc",
+  ]
+
   configs += [
     "//build/config:precompiled_headers",
 
     # TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
     "//build/config/compiler:no_size_t_to_int_warning",
   ]
-
-  if (enable_extensions) {
-    sources = [
-      "api_activity_monitor.cc",
-      "api_activity_monitor.h",
-      "app_sorting.h",
-      "bad_message.cc",
-      "bad_message.h",
-      "blacklist_state.h",
-      "blob_holder.cc",
-      "blob_holder.h",
-      "blocked_action_type.h",
-      "browser_context_keyed_api_factory.h",
-      "component_extension_resource_manager.h",
-      "computed_hashes.cc",
-      "computed_hashes.h",
-      "content_hash_fetcher.cc",
-      "content_hash_fetcher.h",
-      "content_hash_reader.cc",
-      "content_hash_reader.h",
-      "content_hash_tree.cc",
-      "content_hash_tree.h",
-      "content_verifier.cc",
-      "content_verifier.h",
-      "content_verifier_delegate.h",
-      "content_verifier_io_data.cc",
-      "content_verifier_io_data.h",
-      "content_verify_job.cc",
-      "content_verify_job.h",
-      "crx_file_info.cc",
-      "crx_file_info.h",
-      "declarative_user_script_manager.cc",
-      "declarative_user_script_manager.h",
-      "declarative_user_script_manager_factory.cc",
-      "declarative_user_script_manager_factory.h",
-      "declarative_user_script_master.cc",
-      "declarative_user_script_master.h",
-      "deferred_start_render_host.h",
-      "deferred_start_render_host_observer.h",
-      "error_map.cc",
-      "error_map.h",
-      "event_listener_map.cc",
-      "event_listener_map.h",
-      "event_page_tracker.h",
-      "event_router.cc",
-      "event_router.h",
-      "event_router_factory.cc",
-      "event_router_factory.h",
-      "extension_api_frame_id_map.cc",
-      "extension_api_frame_id_map.h",
-      "extension_dialog_auto_confirm.cc",
-      "extension_dialog_auto_confirm.h",
-      "extension_error.cc",
-      "extension_error.h",
-      "extension_function.cc",
-      "extension_function.h",
-      "extension_function_dispatcher.cc",
-      "extension_function_dispatcher.h",
-      "extension_function_registry.cc",
-      "extension_function_registry.h",
-      "extension_host.cc",
-      "extension_host.h",
-      "extension_host_delegate.h",
-      "extension_host_observer.h",
-      "extension_host_queue.h",
-      "extension_icon_image.cc",
-      "extension_icon_image.h",
-      "extension_icon_placeholder.cc",
-      "extension_icon_placeholder.h",
-      "extension_message_filter.cc",
-      "extension_message_filter.h",
-      "extension_navigation_throttle.cc",
-      "extension_navigation_throttle.h",
-      "extension_navigation_ui_data.cc",
-      "extension_navigation_ui_data.h",
-      "extension_pref_store.cc",
-      "extension_pref_store.h",
-      "extension_pref_value_map.cc",
-      "extension_pref_value_map.h",
-      "extension_pref_value_map_factory.cc",
-      "extension_pref_value_map_factory.h",
-      "extension_prefs.cc",
-      "extension_prefs.h",
-      "extension_prefs_factory.cc",
-      "extension_prefs_factory.h",
-      "extension_prefs_observer.h",
-      "extension_prefs_scope.h",
-      "extension_protocols.cc",
-      "extension_protocols.h",
-      "extension_registry.cc",
-      "extension_registry.h",
-      "extension_registry_factory.cc",
-      "extension_registry_factory.h",
-      "extension_registry_observer.h",
-      "extension_request_limiting_throttle.cc",
-      "extension_request_limiting_throttle.h",
-      "extension_scoped_prefs.h",
-      "extension_service_worker_message_filter.cc",
-      "extension_service_worker_message_filter.h",
-      "extension_system.cc",
-      "extension_system.h",
-      "extension_system_provider.cc",
-      "extension_system_provider.h",
-      "extension_throttle_entry.cc",
-      "extension_throttle_entry.h",
-      "extension_throttle_entry_interface.h",
-      "extension_throttle_manager.cc",
-      "extension_throttle_manager.h",
-      "extension_user_script_loader.cc",
-      "extension_user_script_loader.h",
-      "extension_util.cc",
-      "extension_util.h",
-      "extension_web_contents_observer.cc",
-      "extension_web_contents_observer.h",
-      "extension_zoom_request_client.cc",
-      "extension_zoom_request_client.h",
-      "extensions_browser_client.cc",
-      "extensions_browser_client.h",
-      "external_install_info.cc",
-      "external_install_info.h",
-      "external_provider_interface.h",
-      "file_highlighter.cc",
-      "file_highlighter.h",
-      "file_reader.cc",
-      "file_reader.h",
-      "granted_file_entry.cc",
-      "granted_file_entry.h",
-      "image_loader.cc",
-      "image_loader.h",
-      "image_loader_factory.cc",
-      "image_loader_factory.h",
-      "info_map.cc",
-      "info_map.h",
-      "install_flag.h",
-      "io_thread_extension_message_filter.cc",
-      "io_thread_extension_message_filter.h",
-      "lazy_background_task_queue.cc",
-      "lazy_background_task_queue.h",
-      "lazy_background_task_queue_factory.cc",
-      "lazy_background_task_queue_factory.h",
-      "load_monitoring_extension_host_queue.cc",
-      "load_monitoring_extension_host_queue.h",
-      "management_policy.cc",
-      "management_policy.h",
-      "mojo/keep_alive_impl.cc",
-      "mojo/keep_alive_impl.h",
-      "mojo/service_registration.cc",
-      "mojo/service_registration.h",
-      "notification_types.cc",
-      "notification_types.h",
-      "null_app_sorting.cc",
-      "null_app_sorting.h",
-      "policy_check.cc",
-      "policy_check.h",
-      "pref_names.cc",
-      "pref_names.h",
-      "preload_check.cc",
-      "preload_check.h",
-      "preload_check_group.cc",
-      "preload_check_group.h",
-      "process_manager.cc",
-      "process_manager.h",
-      "process_manager_delegate.h",
-      "process_manager_factory.cc",
-      "process_manager_factory.h",
-      "process_manager_observer.h",
-      "process_map.cc",
-      "process_map.h",
-      "process_map_factory.cc",
-      "process_map_factory.h",
-      "quota_service.cc",
-      "quota_service.h",
-      "renderer_startup_helper.cc",
-      "renderer_startup_helper.h",
-      "requirements_checker.cc",
-      "requirements_checker.h",
-      "runtime_data.cc",
-      "runtime_data.h",
-      "sandboxed_unpacker.cc",
-      "sandboxed_unpacker.h",
-      "script_execution_observer.h",
-      "script_executor.cc",
-      "script_executor.h",
-      "serial_extension_host_queue.cc",
-      "serial_extension_host_queue.h",
-      "service_worker_manager.cc",
-      "service_worker_manager.h",
-      "state_store.cc",
-      "state_store.h",
-      "suggest_permission_util.cc",
-      "suggest_permission_util.h",
-      "uninstall_ping_sender.cc",
-      "uninstall_ping_sender.h",
-      "uninstall_reason.h",
-      "update_observer.h",
-      "url_request_util.cc",
-      "url_request_util.h",
-      "user_script_loader.cc",
-      "user_script_loader.h",
-      "verified_contents.cc",
-      "verified_contents.h",
-      "view_type_utils.cc",
-      "view_type_utils.h",
-      "warning_service.cc",
-      "warning_service.h",
-      "warning_service_factory.cc",
-      "warning_service_factory.h",
-      "warning_set.cc",
-      "warning_set.h",
-      "web_ui_user_script_loader.cc",
-      "web_ui_user_script_loader.h",
-    ]
-
-    public_deps = [
-      "//extensions/browser/app_window",
-      "//extensions/browser/guest_view",
-      "//extensions/browser/install",
-      "//extensions/browser/kiosk",
-      "//extensions/browser/updater",
-      "//extensions/browser/value_store",
-      "//ipc",
-    ]
-
-    deps += [
-      "//components/crx_file",
-      "//components/prefs",
-      "//components/sync",
-      "//components/update_client",
-      "//components/variations",
-      "//crypto:platform",
-      "//extensions:extensions_browser_resources",
-      "//extensions/common",
-      "//services/preferences/public/cpp",
-      "//services/service_manager/public/cpp",
-    ]
-  }
 }
 
 source_set("browser_tests") {
diff --git a/extensions/test/BUILD.gn b/extensions/test/BUILD.gn
index e04c482..96322ea 100644
--- a/extensions/test/BUILD.gn
+++ b/extensions/test/BUILD.gn
@@ -2,8 +2,11 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
+import("//extensions/features/features.gni")
 import("//tools/json_schema_compiler/json_features.gni")
 
+assert(enable_extensions)
+
 json_features("test_api_features") {
   feature_type = "APIFeature"
   provider_class = "TestAPIFeatureProvider"
diff --git a/google_apis/gcm/tools/mcs_probe.cc b/google_apis/gcm/tools/mcs_probe.cc
index 2777b46b..798c5029 100644
--- a/google_apis/gcm/tools/mcs_probe.cc
+++ b/google_apis/gcm/tools/mcs_probe.cc
@@ -38,7 +38,6 @@
 #include "google_apis/gcm/engine/gservices_settings.h"
 #include "google_apis/gcm/engine/mcs_client.h"
 #include "google_apis/gcm/monitoring/fake_gcm_stats_recorder.h"
-#include "net/base/host_mapping_rules.h"
 #include "net/cert/cert_verifier.h"
 #include "net/cert/ct_policy_enforcer.h"
 #include "net/cert/multi_log_ct_verifier.h"
@@ -257,7 +256,6 @@
   MCSProbeAuthPreferences http_auth_preferences_;
   std::unique_ptr<net::HttpAuthHandlerFactory> http_auth_handler_factory_;
   std::unique_ptr<net::HttpServerPropertiesImpl> http_server_properties_;
-  std::unique_ptr<net::HostMappingRules> host_mapping_rules_;
   std::unique_ptr<net::HttpNetworkSession> network_session_;
   std::unique_ptr<net::ProxyService> proxy_service_;
 
@@ -406,7 +404,6 @@
   http_auth_handler_factory_ = net::HttpAuthHandlerRegistryFactory::Create(
       &http_auth_preferences_, host_resolver_.get());
   http_server_properties_.reset(new net::HttpServerPropertiesImpl());
-  host_mapping_rules_.reset(new net::HostMappingRules());
   proxy_service_ = net::ProxyService::CreateDirectWithNetLog(&net_log_);
 }
 
@@ -421,7 +418,6 @@
   session_params.ssl_config_service = new net::SSLConfigServiceDefaults();
   session_params.http_auth_handler_factory = http_auth_handler_factory_.get();
   session_params.http_server_properties = http_server_properties_.get();
-  session_params.host_mapping_rules = host_mapping_rules_.get();
   session_params.ignore_certificate_errors = true;
   session_params.testing_fixed_http_port = 0;
   session_params.testing_fixed_https_port = 0;
diff --git a/ios/chrome/browser/snapshots/snapshot_cache.mm b/ios/chrome/browser/snapshots/snapshot_cache.mm
index 7fb720d..612dc57e 100644
--- a/ios/chrome/browser/snapshots/snapshot_cache.mm
+++ b/ios/chrome/browser/snapshots/snapshot_cache.mm
@@ -10,6 +10,7 @@
 #include "base/files/file_enumerator.h"
 #include "base/files/file_path.h"
 #include "base/files/file_util.h"
+#include "base/lazy_instance.h"
 #include "base/location.h"
 #include "base/logging.h"
 #include "base/mac/bind_objc_block.h"
@@ -18,6 +19,7 @@
 #include "base/mac/scoped_nsobject.h"
 #include "base/strings/sys_string_conversions.h"
 #include "base/task_runner_util.h"
+#include "base/task_scheduler/post_task.h"
 #include "base/threading/thread_restrictions.h"
 #include "ios/chrome/browser/experimental_flags.h"
 #import "ios/chrome/browser/snapshots/lru_cache.h"
@@ -52,12 +54,20 @@
 const NSUInteger kCacheInitialCapacity = 100;
 const NSUInteger kGreyInitialCapacity = 8;
 const CGFloat kJPEGImageQuality = 1.0;  // Highest quality. No compression.
-// Sequence token to make sure creation/deletion of snapshots don't overlap.
-const char kSequenceToken[] = "SnapshotCacheSequenceToken";
 // Maximum size in number of elements that the LRU cache can hold before
 // starting to evict elements.
 const NSUInteger kLRUCacheMaxCapacity = 6;
 
+struct SnapshotTaskRunner {
+  const scoped_refptr<base::SequencedTaskRunner> task_runner =
+      base::CreateSequencedTaskRunnerWithTraits(
+          {base::MayBlock(), base::TaskPriority::USER_VISIBLE});
+};
+
+// Sequence token to make sure creation/deletion of snapshots don't overlap.
+base::LazyInstance<SnapshotTaskRunner>::Leaky g_snapshot_task_runner =
+    LAZY_INSTANCE_INITIALIZER;
+
 // The paths of the images saved to disk, given a cache directory.
 base::FilePath FilePathForSessionID(NSString* sessionID,
                                     const base::FilePath& directory) {
@@ -275,9 +285,8 @@
     [imageDictionary_ setObject:img forKey:sessionID];
 
   // Save the image to disk.
-  web::WebThread::PostBlockingPoolSequencedTask(
-      kSequenceToken, FROM_HERE,
-      base::BindBlock(^{
+  g_snapshot_task_runner.Get().task_runner->PostTask(
+      FROM_HERE, base::BindBlock(^{
         base::scoped_nsobject<UIImage> image([img retain]);
         WriteImageToDisk(image,
                          [SnapshotCache imagePathForSessionID:sessionID]);
@@ -291,9 +300,8 @@
   else
     [imageDictionary_ removeObjectForKey:sessionID];
 
-  web::WebThread::PostBlockingPoolSequencedTask(
-      kSequenceToken, FROM_HERE,
-      base::BindBlock(^{
+  g_snapshot_task_runner.Get().task_runner->PostTask(
+      FROM_HERE, base::BindBlock(^{
         base::FilePath imagePath =
             [SnapshotCache imagePathForSessionID:sessionID];
         base::DeleteFile(imagePath, false);
@@ -357,9 +365,8 @@
   DCHECK_CURRENTLY_ON(web::WebThread::UI);
   // Copying the date, as the block must copy the value, not the reference.
   const base::Time dateCopy = date;
-  web::WebThread::PostBlockingPoolSequencedTask(
-      kSequenceToken, FROM_HERE,
-      base::BindBlock(^{
+  g_snapshot_task_runner.Get().task_runner->PostTask(
+      FROM_HERE, base::BindBlock(^{
         std::set<base::FilePath> filesToKeep;
         for (NSString* sessionID : liveSessionIds) {
           base::FilePath curImagePath =
@@ -574,9 +581,10 @@
     }
   }
 
-  web::WebThread::PostBlockingPoolTask(
-      FROM_HERE, base::Bind(&ConvertAndSaveGreyImage, colorImagePath,
-                            greyImagePath, backgroundingColorImage_));
+  base::PostTaskWithTraits(
+      FROM_HERE, {base::MayBlock(), base::TaskPriority::USER_VISIBLE},
+      base::BindOnce(&ConvertAndSaveGreyImage, colorImagePath, greyImagePath,
+                     backgroundingColorImage_));
 }
 
 @end
diff --git a/ios/chrome/browser/snapshots/snapshot_cache_unittest.mm b/ios/chrome/browser/snapshots/snapshot_cache_unittest.mm
index 51fa1c8e..500f9422 100644
--- a/ios/chrome/browser/snapshots/snapshot_cache_unittest.mm
+++ b/ios/chrome/browser/snapshots/snapshot_cache_unittest.mm
@@ -16,11 +16,10 @@
 #include "base/mac/scoped_nsobject.h"
 #include "base/run_loop.h"
 #include "base/strings/sys_string_conversions.h"
-#include "base/threading/sequenced_worker_pool.h"
+#include "base/task_scheduler/task_scheduler.h"
 #include "base/time/time.h"
 #import "ios/chrome/browser/snapshots/snapshot_cache_internal.h"
 #include "ios/web/public/test/test_web_thread_bundle.h"
-#include "ios/web/public/web_thread.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "testing/gtest_mac.h"
 #include "testing/platform_test.h"
@@ -84,7 +83,7 @@
   // Flushes all the runloops internally used by the snapshot cache.
   void FlushRunLoops() {
     base::RunLoop().RunUntilIdle();
-    web::WebThread::GetBlockingPool()->FlushForTesting();
+    base::TaskScheduler::GetInstance()->FlushForTesting();
     base::RunLoop().RunUntilIdle();
   }
 
diff --git a/ios/web/public/web_thread.h b/ios/web/public/web_thread.h
index be41b30..0ab786c 100644
--- a/ios/web/public/web_thread.h
+++ b/ios/web/public/web_thread.h
@@ -139,38 +139,6 @@
     return GetTaskRunnerForThread(identifier)->DeleteSoon(from_here, object);
   }
 
-  // Simplified wrappers for posting to the blocking thread pool. Use this
-  // for doing things like blocking I/O.
-  //
-  // The first variant will run the task in the pool with no sequencing
-  // semantics, so may get run in parallel with other posted tasks. The second
-  // variant will all post a task with no sequencing semantics, and will post a
-  // reply task to the origin TaskRunner upon completion.  The third variant
-  // provides sequencing between tasks with the same sequence token name.
-  //
-  // These tasks are guaranteed to run before shutdown.
-  //
-  // If you need to provide different shutdown semantics (like you have
-  // something slow and noncritical that doesn't need to block shutdown),
-  // or you want to manually provide a sequence token (which saves a map
-  // lookup and is guaranteed unique without you having to come up with a
-  // unique string), you can access the sequenced worker pool directly via
-  // GetBlockingPool().
-  //
-  // If you need to PostTaskAndReplyWithResult, use
-  // base::PostTaskAndReplyWithResult() with GetBlockingPool() as the task
-  // runner.
-  static bool PostBlockingPoolTask(const tracked_objects::Location& from_here,
-                                   base::OnceClosure task);
-  static bool PostBlockingPoolTaskAndReply(
-      const tracked_objects::Location& from_here,
-      base::OnceClosure task,
-      base::OnceClosure reply);
-  static bool PostBlockingPoolSequencedTask(
-      const std::string& sequence_token_name,
-      const tracked_objects::Location& from_here,
-      base::OnceClosure task);
-
   // Returns the thread pool used for blocking file I/O. Use this object to
   // perform random blocking operations such as file writes.
   static base::SequencedWorkerPool* GetBlockingPool() WARN_UNUSED_RESULT;
diff --git a/ios/web/web_state/navigation_callbacks_inttest.mm b/ios/web/web_state/navigation_callbacks_inttest.mm
index cedf3bb..56e2e82 100644
--- a/ios/web/web_state/navigation_callbacks_inttest.mm
+++ b/ios/web/web_state/navigation_callbacks_inttest.mm
@@ -172,7 +172,7 @@
 // Verifies correctness of |NavigationContext| (|arg0|) for reload navigation
 // passed to |DidFinishNavigation|. Asserts that |NavigationContext| the same as
 // |context|.
-ACTION_P3(VerifyReloadFinishedContext, web_state, url, context) {
+ACTION_P4(VerifyReloadFinishedContext, web_state, url, context, is_web_page) {
   ASSERT_EQ(*context, arg0);
   ASSERT_TRUE(*context);
   EXPECT_EQ(web_state, (*context)->GetWebState());
@@ -182,7 +182,14 @@
                                (*context)->GetPageTransition()));
   EXPECT_FALSE((*context)->IsSameDocument());
   EXPECT_FALSE((*context)->GetError());
-  EXPECT_FALSE((*context)->GetResponseHeaders());
+  if (is_web_page) {
+    ASSERT_TRUE((*context)->GetResponseHeaders());
+    std::string mime_type;
+    (*context)->GetResponseHeaders()->GetMimeType(&mime_type);
+    EXPECT_EQ(kExpectedMimeType, mime_type);
+  } else {
+    EXPECT_FALSE((*context)->GetResponseHeaders());
+  }
   NavigationManager* navigation_manager = web_state->GetNavigationManager();
   NavigationItem* item = navigation_manager->GetLastCommittedItem();
   EXPECT_GT(item->GetTimestamp().ToInternalValue(), 0);
@@ -230,6 +237,36 @@
   LoadUrl(url);
 }
 
+// Tests web page reload navigation.
+TEST_F(StartAndFinishNavigationTest, WebPageReloadNavigation) {
+  const GURL url = HttpServer::MakeUrl("http://chromium.test");
+  std::map<GURL, std::string> responses;
+  responses[url] = "Chromium Test";
+  web::test::SetUpSimpleHttpServer(responses);
+
+  // Perform new page navigation.
+  NavigationContext* context = nullptr;
+  EXPECT_CALL(*observer_, DidStartNavigation(_));
+  EXPECT_CALL(*observer_, DidFinishNavigation(_));
+  LoadUrl(url);
+
+  // Reload web page.
+  EXPECT_CALL(*observer_, DidStartNavigation(_))
+      .WillOnce(VerifyReloadStartedContext(web_state(), url, &context));
+  EXPECT_CALL(*observer_, DidFinishNavigation(_))
+      .WillOnce(VerifyReloadFinishedContext(web_state(), url, &context,
+                                            true /* is_web_page */));
+  // TODO(crbug.com/700958): ios/web ignores |check_for_repost| flag and current
+  // delegate does not run callback for ShowRepostFormWarningDialog. Clearing
+  // the delegate will allow form resubmission. Remove this workaround (clearing
+  // the delegate, once |check_for_repost| is supported).
+  web_state()->SetDelegate(nullptr);
+  ExecuteBlockAndWaitForLoad(url, ^{
+    navigation_manager()->Reload(ReloadType::NORMAL,
+                                 false /*check_for_repost*/);
+  });
+}
+
 // Tests user-initiated hash change.
 TEST_F(StartAndFinishNavigationTest, UserInitiatedHashChangeNavigation) {
   const GURL url = HttpServer::MakeUrl("http://chromium.test");
@@ -362,7 +399,8 @@
   EXPECT_CALL(*observer_, DidStartNavigation(_))
       .WillOnce(VerifyReloadStartedContext(web_state(), url, &context));
   EXPECT_CALL(*observer_, DidFinishNavigation(_))
-      .WillOnce(VerifyReloadFinishedContext(web_state(), url, &context));
+      .WillOnce(VerifyReloadFinishedContext(web_state(), url, &context,
+                                            false /* is_web_page */));
   navigation_manager()->Reload(ReloadType::NORMAL, false /*check_for_repost*/);
 }
 
diff --git a/ios/web/web_state/ui/crw_web_controller.mm b/ios/web/web_state/ui/crw_web_controller.mm
index ec226cd..7e7dfc3 100644
--- a/ios/web/web_state/ui/crw_web_controller.mm
+++ b/ios/web/web_state/ui/crw_web_controller.mm
@@ -2053,10 +2053,8 @@
           [transientItem->GetHttpRequestHeaders() copy]);
       [self loadWithParams:reloadParams];
     } else {
-      // As with back and forward navigation, load the URL manually instead of
-      // using the web view's reload. This ensures state processing and delegate
-      // calls are consistent.
-      // TODO(eugenebut): revisit this for WKWebView.
+      self.currentNavItem->SetTransitionType(
+          ui::PageTransition::PAGE_TRANSITION_RELOAD);
       [self loadCurrentURL];
     }
   }
diff --git a/ios/web/web_thread_impl.cc b/ios/web/web_thread_impl.cc
index b6d082e..d1d6f415 100644
--- a/ios/web/web_thread_impl.cc
+++ b/ios/web/web_thread_impl.cc
@@ -330,31 +330,6 @@
 }
 
 // static
-bool WebThread::PostBlockingPoolTask(const tracked_objects::Location& from_here,
-                                     base::OnceClosure task) {
-  return g_globals.Get().blocking_pool->PostWorkerTask(from_here,
-                                                       std::move(task));
-}
-
-// static
-bool WebThread::PostBlockingPoolTaskAndReply(
-    const tracked_objects::Location& from_here,
-    base::OnceClosure task,
-    base::OnceClosure reply) {
-  return g_globals.Get().blocking_pool->PostTaskAndReply(
-      from_here, std::move(task), std::move(reply));
-}
-
-// static
-bool WebThread::PostBlockingPoolSequencedTask(
-    const std::string& sequence_token_name,
-    const tracked_objects::Location& from_here,
-    base::OnceClosure task) {
-  return g_globals.Get().blocking_pool->PostNamedSequencedWorkerTask(
-      sequence_token_name, from_here, std::move(task));
-}
-
-// static
 base::SequencedWorkerPool* WebThread::GetBlockingPool() {
   return g_globals.Get().blocking_pool.get();
 }
diff --git a/media/base/android/java/src/org/chromium/media/MediaCodecUtil.java b/media/base/android/java/src/org/chromium/media/MediaCodecUtil.java
index 9baeee13..8f67ebba 100644
--- a/media/base/android/java/src/org/chromium/media/MediaCodecUtil.java
+++ b/media/base/android/java/src/org/chromium/media/MediaCodecUtil.java
@@ -87,7 +87,13 @@
         @SuppressWarnings("deprecation")
         private int getCodecCount() {
             if (hasNewMediaCodecList()) return mCodecList.length;
-            return MediaCodecList.getCodecCount();
+            try {
+                return MediaCodecList.getCodecCount();
+            } catch (RuntimeException e) {
+                // Swallow the exception due to bad Android implementation and pretend
+                // MediaCodecList is not supported.
+                return 0;
+            }
         }
 
         @SuppressWarnings("deprecation")
diff --git a/media/blink/multibuffer_data_source.cc b/media/blink/multibuffer_data_source.cc
index 4d8d045a..c0e7c1dd 100644
--- a/media/blink/multibuffer_data_source.cc
+++ b/media/blink/multibuffer_data_source.cc
@@ -505,6 +505,8 @@
   render_task_runner_->PostTask(
       FROM_HERE, base::Bind(base::ResetAndReturn(&init_cb_), success));
 
+  UpdateBufferSizes();
+
   // Even if data is cached, say that we're loading at this point for
   // compatibility.
   UpdateLoadingState_Locked(true);
@@ -610,6 +612,17 @@
       std::min((kTargetSecondsBufferedAhead + kTargetSecondsBufferedBehind) *
                    bytes_per_second,
                preload_high + pin_backward);
+
+  if (url_data_->FullyCached() ||
+      (url_data_->length() != kPositionNotSpecified &&
+       url_data_->length() < kDefaultPinSize)) {
+    // We just make pin_forwards/backwards big enough to encompass the
+    // whole file regardless of where we are, with some extra margins.
+    pin_forward = std::max(pin_forward, url_data_->length() * 2);
+    pin_backward = std::max(pin_backward, url_data_->length() * 2);
+    buffer_size = url_data_->length();
+  }
+
   reader_->SetMaxBuffer(buffer_size);
   reader_->SetPinRange(pin_backward, pin_forward);
 
diff --git a/media/blink/multibuffer_data_source_unittest.cc b/media/blink/multibuffer_data_source_unittest.cc
index 90a6cea..1ade7293 100644
--- a/media/blink/multibuffer_data_source_unittest.cc
+++ b/media/blink/multibuffer_data_source_unittest.cc
@@ -239,14 +239,15 @@
 
   void InitializeWithCORS(const char* url,
                           bool expected,
-                          UrlData::CORSMode cors_mode) {
+                          UrlData::CORSMode cors_mode,
+                          size_t file_size = kFileSize) {
     GURL gurl(url);
     data_source_.reset(new MockMultibufferDataSource(
         gurl, cors_mode, message_loop_.task_runner(), url_index_,
         view_->MainFrame()->ToWebLocalFrame(), &host_));
     data_source_->SetPreload(preload_);
 
-    response_generator_.reset(new TestResponseGenerator(gurl, kFileSize));
+    response_generator_.reset(new TestResponseGenerator(gurl, file_size));
     EXPECT_CALL(*this, OnInitialize(expected));
     data_source_->Initialize(base::Bind(
         &MultibufferDataSourceTest::OnInitialize, base::Unretained(this)));
@@ -256,8 +257,10 @@
     EXPECT_EQ(data_source_->downloading(), false);
   }
 
-  void Initialize(const char* url, bool expected) {
-    InitializeWithCORS(url, expected, UrlData::CORS_UNSPECIFIED);
+  void Initialize(const char* url,
+                  bool expected,
+                  size_t file_size = kFileSize) {
+    InitializeWithCORS(url, expected, UrlData::CORS_UNSPECIFIED, file_size);
   }
 
   // Helper to initialize tests with a valid 200 response.
@@ -272,8 +275,8 @@
   }
 
   // Helper to initialize tests with a valid 206 response.
-  void InitializeWith206Response() {
-    Initialize(kHttpUrl, true);
+  void InitializeWith206Response(size_t file_size = kFileSize) {
+    Initialize(kHttpUrl, true, file_size);
 
     EXPECT_CALL(host_, SetTotalBytes(response_generator_->content_length()));
     Respond(response_generator_->Generate206(0));
@@ -1515,7 +1518,7 @@
 }
 
 TEST_F(MultibufferDataSourceTest, CheckBufferSizes) {
-  InitializeWith206Response();
+  InitializeWith206Response(1 << 30);  // 1 gb
 
   data_source_->SetBitrate(1 << 20);  // 1 mbit / s
   base::RunLoop().RunUntilIdle();
@@ -1563,6 +1566,28 @@
   EXPECT_EQ(71 << 20, buffer_size());
 }
 
+TEST_F(MultibufferDataSourceTest, CheckBufferSizeForSmallFiles) {
+  InitializeWith206Response();
+
+  data_source_->SetBitrate(1 << 20);  // 1 mbit / s
+  base::RunLoop().RunUntilIdle();
+  EXPECT_EQ(1 << 20, data_source_bitrate());
+  EXPECT_EQ(2 << 20, preload_low());
+  EXPECT_EQ(3 << 20, preload_high());
+  EXPECT_EQ(25 << 20, max_buffer_forward());
+  EXPECT_EQ(kFileSize * 2, max_buffer_backward());
+  EXPECT_EQ(5013504 /* file size rounted up to blocks size */, buffer_size());
+
+  data_source_->SetBitrate(80 << 20);  // 80 mbit / s
+  base::RunLoop().RunUntilIdle();
+  EXPECT_EQ(80 << 20, data_source_bitrate());
+  EXPECT_EQ(50 << 20, preload_low());
+  EXPECT_EQ(51 << 20, preload_high());
+  EXPECT_EQ(51 << 20, max_buffer_forward());
+  EXPECT_EQ(20 << 20, max_buffer_backward());
+  EXPECT_EQ(5013504 /* file size rounted up to blocks size */, buffer_size());
+}
+
 // Provoke an edge case where the loading state may not end up transitioning
 // back to "idle" when we're done loading.
 TEST_F(MultibufferDataSourceTest, Http_CheckLoadingTransition) {
diff --git a/media/blink/resource_multibuffer_data_provider.cc b/media/blink/resource_multibuffer_data_provider.cc
index 18a8847f..54f0d73 100644
--- a/media/blink/resource_multibuffer_data_provider.cc
+++ b/media/blink/resource_multibuffer_data_provider.cc
@@ -452,6 +452,10 @@
   url_data_->set_length(size);
   fifo_.push_back(DataBuffer::CreateEOSBuffer());
 
+  if (url_data_->url_index()) {
+    url_data_->url_index()->TryInsert(url_data_);
+  }
+
   DCHECK(Available());
   url_data_->multibuffer()->OnDataProviderEvent(this);
 
diff --git a/media/blink/url_index.cc b/media/blink/url_index.cc
index f255cb19..21babb7f 100644
--- a/media/blink/url_index.cc
+++ b/media/blink/url_index.cc
@@ -149,10 +149,19 @@
                             scoped_refptr<UrlData>(this)));
 }
 
-bool UrlData::Valid() const {
+bool UrlData::FullyCached() {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  if (length_ == kPositionNotSpecified)
+    return false;
+  // Check that the first unavailable block in the cache is after the
+  // end of the file.
+  return (multibuffer()->FindNextUnavailable(0) << kBlockSizeShift) >= length_;
+}
+
+bool UrlData::Valid() {
   DCHECK(thread_checker_.CalledOnValidThread());
   base::Time now = base::Time::Now();
-  if (!range_supported_)
+  if (!range_supported_ && !FullyCached())
     return false;
   // When ranges are not supported, we cannot re-use cached data.
   if (valid_until_ > now)
diff --git a/media/blink/url_index.h b/media/blink/url_index.h
index f18d358..d8c2a6cf 100644
--- a/media/blink/url_index.h
+++ b/media/blink/url_index.h
@@ -91,6 +91,9 @@
   // Returns the number of blocks cached for this resource.
   size_t CachedSize();
 
+  // Returns true if this resource is fully cached in memory.
+  bool FullyCached();
+
   // Returns our url_index, or nullptr if it's been destroyed.
   UrlIndex* url_index() const { return url_index_.get(); }
 
@@ -131,7 +134,7 @@
   // Returns true it is valid to keep using this to access cached data.
   // A single media player instance may choose to ignore this for resources
   // that have already been opened.
-  bool Valid() const;
+  bool Valid();
 
   // Virtual so we can override it for testing.
   virtual ResourceMultiBuffer* multibuffer();
diff --git a/media/gpu/video_decode_accelerator_unittest.cc b/media/gpu/video_decode_accelerator_unittest.cc
index 522e61cd..991394b3 100644
--- a/media/gpu/video_decode_accelerator_unittest.cc
+++ b/media/gpu/video_decode_accelerator_unittest.cc
@@ -647,10 +647,12 @@
   LOG_ASSERT(decoder_deleted());
   LOG_ASSERT(!decoder_.get());
 
+  VideoDecodeAccelerator::Config config(profile_);
+
   if (fake_decoder_) {
     decoder_.reset(new FakeVideoDecodeAccelerator(
         frame_size_, base::Bind(&DoNothingReturnTrue)));
-    LOG_ASSERT(decoder_->Initialize(profile_, this));
+    LOG_ASSERT(decoder_->Initialize(config, this));
   } else {
     if (!vda_factory_) {
       vda_factory_ = GpuVideoDecodeAcceleratorFactory::Create(
@@ -660,7 +662,6 @@
       LOG_ASSERT(vda_factory_);
     }
 
-    VideoDecodeAccelerator::Config config(profile_);
     if (g_test_import) {
       config.output_mode = VideoDecodeAccelerator::Config::OutputMode::IMPORT;
     }
diff --git a/media/video/video_decode_accelerator.h b/media/video/video_decode_accelerator.h
index 6fe0573f..e61f6e2 100644
--- a/media/video/video_decode_accelerator.h
+++ b/media/video/video_decode_accelerator.h
@@ -130,9 +130,7 @@
     Config();
     Config(const Config& config);
 
-    // Intentional converting constructor.
-    // TODO(watk): Make this explicit.
-    Config(VideoCodecProfile profile);
+    explicit Config(VideoCodecProfile profile);
 
     ~Config();
 
diff --git a/net/BUILD.gn b/net/BUILD.gn
index ed29540..6b6dbf8 100644
--- a/net/BUILD.gn
+++ b/net/BUILD.gn
@@ -4331,6 +4331,7 @@
     "cert/internal/certificate_policies_unittest.cc",
     "cert/internal/extended_key_usage_unittest.cc",
     "cert/internal/name_constraints_unittest.cc",
+    "cert/internal/nist_pkits_unittest.cc",
     "cert/internal/nist_pkits_unittest.h",
     "cert/internal/parse_certificate_unittest.cc",
     "cert/internal/parse_name_unittest.cc",
diff --git a/net/base/host_mapping_rules.cc b/net/base/host_mapping_rules.cc
index 3c24b24..b8fef17 100644
--- a/net/base/host_mapping_rules.cc
+++ b/net/base/host_mapping_rules.cc
@@ -28,22 +28,17 @@
 
 HostMappingRules::HostMappingRules() {}
 
+HostMappingRules::HostMappingRules(const HostMappingRules& host_mapping_rules) =
+    default;
+
 HostMappingRules::~HostMappingRules() {}
 
+HostMappingRules& HostMappingRules::operator=(
+    const HostMappingRules& host_mapping_rules) = default;
+
 bool HostMappingRules::RewriteHost(HostPortPair* host_port) const {
-  // Check if the hostname was excluded.
-  for (ExclusionRuleList::const_iterator it = exclusion_rules_.begin();
-       it != exclusion_rules_.end(); ++it) {
-    const ExclusionRule& rule = *it;
-    if (base::MatchPattern(host_port->host(), rule.hostname_pattern))
-      return false;
-  }
-
   // Check if the hostname was remapped.
-  for (MapRuleList::const_iterator it = map_rules_.begin();
-       it != map_rules_.end(); ++it) {
-    const MapRule& rule = *it;
-
+  for (const auto& rule : map_rules_) {
     // The rule's hostname_pattern will be something like:
     //     www.foo.com
     //     *.foo.com
@@ -57,6 +52,12 @@
         continue;  // This rule doesn't apply.
     }
 
+    // Check if the hostname was excluded.
+    for (const auto& rule : exclusion_rules_) {
+      if (base::MatchPattern(host_port->host(), rule.hostname_pattern))
+        return false;
+    }
+
     host_port->set_host(rule.replacement_hostname);
     if (rule.replacement_port != -1)
       host_port->set_port(static_cast<uint16_t>(rule.replacement_port));
diff --git a/net/base/host_mapping_rules.h b/net/base/host_mapping_rules.h
index 8c7bc0b..02aef43 100644
--- a/net/base/host_mapping_rules.h
+++ b/net/base/host_mapping_rules.h
@@ -18,8 +18,11 @@
 class NET_EXPORT_PRIVATE HostMappingRules {
  public:
   HostMappingRules();
+  HostMappingRules(const HostMappingRules& host_mapping_rules);
   ~HostMappingRules();
 
+  HostMappingRules& operator=(const HostMappingRules& host_mapping_rules);
+
   // Modifies |*host_port| based on the current rules. Returns true if
   // |*host_port| was modified, false otherwise.
   bool RewriteHost(HostPortPair* host_port) const;
@@ -46,8 +49,6 @@
 
   MapRuleList map_rules_;
   ExclusionRuleList exclusion_rules_;
-
-  DISALLOW_COPY_AND_ASSIGN(HostMappingRules);
 };
 
 }  // namespace net
diff --git a/net/base/network_change_notifier_win.cc b/net/base/network_change_notifier_win.cc
index 9b2b35e..1dc2038 100644
--- a/net/base/network_change_notifier_win.cc
+++ b/net/base/network_change_notifier_win.cc
@@ -14,6 +14,7 @@
 #include "base/message_loop/message_loop.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/single_thread_task_runner.h"
+#include "base/task_runner_util.h"
 #include "base/threading/thread.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "base/time/time.h"
@@ -136,8 +137,6 @@
 //
 NetworkChangeNotifier::ConnectionType
 NetworkChangeNotifierWin::RecomputeCurrentConnectionType() const {
-  DCHECK(CalledOnValidThread());
-
   EnsureWinsockInit();
 
   // The following code was adapted from:
@@ -205,6 +204,18 @@
                           : NetworkChangeNotifier::CONNECTION_NONE;
 }
 
+void NetworkChangeNotifierWin::RecomputeCurrentConnectionTypeOnDnsThread(
+    base::Callback<void(ConnectionType)> reply_callback) const {
+  // Unretained is safe in this call because this object owns the thread and the
+  // thread is stopped in this object's destructor.
+  base::PostTaskAndReplyWithResult(
+      dns_config_service_thread_->message_loop()->task_runner().get(),
+      FROM_HERE,
+      base::Bind(&NetworkChangeNotifierWin::RecomputeCurrentConnectionType,
+                 base::Unretained(this)),
+      reply_callback);
+}
+
 NetworkChangeNotifier::ConnectionType
 NetworkChangeNotifierWin::GetCurrentConnectionType() const {
   base::AutoLock auto_lock(last_computed_connection_type_lock_);
@@ -225,12 +236,13 @@
   // Start watching for the next address change.
   WatchForAddressChange();
 
-  NotifyObservers();
+  RecomputeCurrentConnectionTypeOnDnsThread(base::Bind(
+      &NetworkChangeNotifierWin::NotifyObservers, weak_factory_.GetWeakPtr()));
 }
 
-void NetworkChangeNotifierWin::NotifyObservers() {
+void NetworkChangeNotifierWin::NotifyObservers(ConnectionType connection_type) {
   DCHECK(CalledOnValidThread());
-  SetCurrentConnectionType(RecomputeCurrentConnectionType());
+  SetCurrentConnectionType(connection_type);
   NotifyObserversOfIPAddressChange();
 
   // Calling GetConnectionType() at this very moment is likely to give
@@ -274,8 +286,11 @@
   // Treat the transition from NotifyAddrChange failing to succeeding as a
   // network change event, since network changes were not being observed in
   // that interval.
-  if (sequential_failures_ > 0)
-    NotifyObservers();
+  if (sequential_failures_ > 0) {
+    RecomputeCurrentConnectionTypeOnDnsThread(
+        base::Bind(&NetworkChangeNotifierWin::NotifyObservers,
+                   weak_factory_.GetWeakPtr()));
+  }
 
   if (sequential_failures_ < 2000) {
     UMA_HISTOGRAM_COUNTS_10000("Net.NotifyAddrChangeFailures",
@@ -305,7 +320,14 @@
 }
 
 void NetworkChangeNotifierWin::NotifyParentOfConnectionTypeChange() {
-  SetCurrentConnectionType(RecomputeCurrentConnectionType());
+  RecomputeCurrentConnectionTypeOnDnsThread(base::Bind(
+      &NetworkChangeNotifierWin::NotifyParentOfConnectionTypeChangeImpl,
+      weak_factory_.GetWeakPtr()));
+}
+
+void NetworkChangeNotifierWin::NotifyParentOfConnectionTypeChangeImpl(
+    ConnectionType connection_type) {
+  SetCurrentConnectionType(connection_type);
   bool current_offline = IsOffline();
   offline_polls_++;
   // If we continue to appear offline, delay sending out the notification in
@@ -323,10 +345,10 @@
 
   NotifyObserversOfConnectionTypeChange();
   double max_bandwidth_mbps = 0.0;
-  ConnectionType connection_type = CONNECTION_NONE;
+  ConnectionType max_connection_type = CONNECTION_NONE;
   GetCurrentMaxBandwidthAndConnectionType(&max_bandwidth_mbps,
-                                          &connection_type);
-  NotifyObserversOfMaxBandwidthChange(max_bandwidth_mbps, connection_type);
+                                          &max_connection_type);
+  NotifyObserversOfMaxBandwidthChange(max_bandwidth_mbps, max_connection_type);
 }
 
 }  // namespace net
diff --git a/net/base/network_change_notifier_win.h b/net/base/network_change_notifier_win.h
index 94bab7f8..6871499 100644
--- a/net/base/network_change_notifier_win.h
+++ b/net/base/network_change_notifier_win.h
@@ -9,6 +9,7 @@
 
 #include <memory>
 
+#include "base/callback.h"
 #include "base/compiler_specific.h"
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
@@ -62,15 +63,21 @@
   // It is not thread safe, see crbug.com/324913.
   virtual ConnectionType RecomputeCurrentConnectionType() const;
 
+  // Calls RecomputeCurrentConnectionTypeImpl on the DNS thread and runs
+  // |reply_callback| with the type on the calling thread.
+  virtual void RecomputeCurrentConnectionTypeOnDnsThread(
+      base::Callback<void(ConnectionType)> reply_callback) const;
+
   void SetCurrentConnectionType(ConnectionType connection_type);
 
   // Notifies IP address change observers of a change immediately, and notifies
   // network state change observers on a delay.  Must only be called on the
   // thread |this| was created on.
-  void NotifyObservers();
+  void NotifyObservers(ConnectionType connection_type);
 
   // Forwards connection type notifications to parent class.
   void NotifyParentOfConnectionTypeChange();
+  void NotifyParentOfConnectionTypeChangeImpl(ConnectionType connection_type);
 
   // Tries to start listening for a single subsequent address change.  Returns
   // false on failure.  The caller is responsible for updating |is_watching_|.
diff --git a/net/base/network_change_notifier_win_unittest.cc b/net/base/network_change_notifier_win_unittest.cc
index 40dc42d..96cf416b 100644
--- a/net/base/network_change_notifier_win_unittest.cc
+++ b/net/base/network_change_notifier_win_unittest.cc
@@ -2,12 +2,14 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include "net/base/network_change_notifier_win.h"
+
 #include "base/macros.h"
 #include "base/message_loop/message_loop.h"
 #include "base/run_loop.h"
+#include "base/single_thread_task_runner.h"
 #include "net/base/network_change_notifier.h"
 #include "net/base/network_change_notifier_factory.h"
-#include "net/base/network_change_notifier_win.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -39,6 +41,14 @@
   }
 
   // From NetworkChangeNotifierWin.
+  void RecomputeCurrentConnectionTypeOnDnsThread(
+      base::Callback<void(ConnectionType)> reply_callback) const override {
+    base::ThreadTaskRunnerHandle::Get()->PostTask(
+        FROM_HERE,
+        base::Bind(reply_callback, NetworkChangeNotifier::CONNECTION_UNKNOWN));
+  }
+
+  // From NetworkChangeNotifierWin.
   MOCK_METHOD0(WatchForAddressChangeInternal, bool());
 
  private:
diff --git a/net/cert/internal/nist_pkits_unittest.cc b/net/cert/internal/nist_pkits_unittest.cc
new file mode 100644
index 0000000..6e65dff
--- /dev/null
+++ b/net/cert/internal/nist_pkits_unittest.cc
@@ -0,0 +1,59 @@
+// 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 "net/cert/internal/nist_pkits_unittest.h"
+
+#include "base/strings/string_split.h"
+#include "net/cert/internal/certificate_policies.h"
+
+namespace net {
+
+namespace {
+
+// 2.16.840.1.101.3.2.1.48.1
+const uint8_t kTestPolicy1[] = {0x60, 0x86, 0x48, 0x01, 0x65,
+                                0x03, 0x02, 0x01, 0x30, 0x01};
+
+// 2.16.840.1.101.3.2.1.48.2
+const uint8_t kTestPolicy2[] = {0x60, 0x86, 0x48, 0x01, 0x65,
+                                0x03, 0x02, 0x01, 0x30, 0x02};
+
+// 2.16.840.1.101.3.2.1.48.3
+const uint8_t kTestPolicy3[] = {0x60, 0x86, 0x48, 0x01, 0x65,
+                                0x03, 0x02, 0x01, 0x30, 0x03};
+
+// 2.16.840.1.101.3.2.1.48.6
+const uint8_t kTestPolicy6[] = {0x60, 0x86, 0x48, 0x01, 0x65,
+                                0x03, 0x02, 0x01, 0x30, 0x06};
+
+}  // namespace
+
+PkitsTestSettings::PkitsTestSettings() {
+  SetInitialPolicySet("anyPolicy");
+}
+
+PkitsTestSettings::~PkitsTestSettings() = default;
+
+void PkitsTestSettings::SetInitialPolicySet(const char* const policy_names) {
+  initial_policy_set.clear();
+  std::vector<std::string> names = base::SplitString(
+      policy_names, ",", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
+  for (const std::string& policy_name : names) {
+    if (policy_name == "anyPolicy") {
+      initial_policy_set.insert(AnyPolicy());
+    } else if (policy_name == "NIST-test-policy-1") {
+      initial_policy_set.insert(der::Input(kTestPolicy1));
+    } else if (policy_name == "NIST-test-policy-2") {
+      initial_policy_set.insert(der::Input(kTestPolicy2));
+    } else if (policy_name == "NIST-test-policy-3") {
+      initial_policy_set.insert(der::Input(kTestPolicy3));
+    } else if (policy_name == "NIST-test-policy-6") {
+      initial_policy_set.insert(der::Input(kTestPolicy6));
+    } else {
+      ADD_FAILURE() << "Unknown policy name: " << policy_name;
+    }
+  }
+}
+
+}  // namespace net
diff --git a/net/cert/internal/nist_pkits_unittest.h b/net/cert/internal/nist_pkits_unittest.h
index 6a454020..265f332 100644
--- a/net/cert/internal/nist_pkits_unittest.h
+++ b/net/cert/internal/nist_pkits_unittest.h
@@ -5,9 +5,38 @@
 #ifndef NET_CERT_INTERNAL_NIST_PKITS_UNITTEST_H_
 #define NET_CERT_INTERNAL_NIST_PKITS_UNITTEST_H_
 
+#include <set>
+
 #include "net/cert/internal/test_helpers.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
+namespace net {
+
+// Describes additional inputs to verification in the PKITS tests
+// (which are referred to as "settings" in that document).
+struct PkitsTestSettings {
+  // Default construction results in the "default settings".
+  PkitsTestSettings();
+  ~PkitsTestSettings();
+
+  // Sets |initial_policy_set| to the specified policies. The
+  // policies are described as comma-separated symbolic strings like
+  // "anyPolicy" and "NIST-test-policy-1".
+  void SetInitialPolicySet(const char* const policy_names);
+
+  // A set of policy OIDs to use for "initial-policy-set".
+  std::set<der::Input> initial_policy_set;
+
+  // The value of "initial-explicit-policy".
+  bool initial_explicit_policy = false;
+
+  // The value of "initial-policy-mapping-inhibit".
+  bool initial_policy_mapping_inhibit = false;
+
+  // The value of "initial-inhibit-any-policy".
+  bool initial_inhibit_any_policy = false;
+};
+
 // Parameterized test class for PKITS tests.
 // The instantiating code should define a PkitsTestDelegate with an appropriate
 // static Verify method, and then INSTANTIATE_TYPED_TEST_CASE_P for each
@@ -17,7 +46,8 @@
  public:
   template <size_t num_certs, size_t num_crls>
   bool Verify(const char* const (&cert_names)[num_certs],
-              const char* const (&crl_names)[num_crls]) {
+              const char* const (&crl_names)[num_crls],
+              const PkitsTestSettings& settings) {
     std::vector<std::string> cert_ders;
     for (const std::string& s : cert_names)
       cert_ders.push_back(net::ReadTestFileToString(
@@ -26,11 +56,13 @@
     for (const std::string& s : crl_names)
       crl_ders.push_back(net::ReadTestFileToString(
           "net/third_party/nist-pkits/crls/" + s + ".crl"));
-    return PkitsTestDelegate::Verify(cert_ders, crl_ders);
+    return PkitsTestDelegate::Verify(cert_ders, crl_ders, settings);
   }
 };
 
 // Inline the generated test code:
 #include "net/third_party/nist-pkits/pkits_testcases-inl.h"
 
+}  // namespace net
+
 #endif  // NET_CERT_INTERNAL_NIST_PKITS_UNITTEST_H_
diff --git a/net/cert/internal/path_builder_pkits_unittest.cc b/net/cert/internal/path_builder_pkits_unittest.cc
index c9850208..81eceeef 100644
--- a/net/cert/internal/path_builder_pkits_unittest.cc
+++ b/net/cert/internal/path_builder_pkits_unittest.cc
@@ -52,7 +52,8 @@
 class PathBuilderPkitsTestDelegate {
  public:
   static bool Verify(std::vector<std::string> cert_ders,
-                     std::vector<std::string> crl_ders) {
+                     std::vector<std::string> crl_ders,
+                     const PkitsTestSettings& settings) {
     if (cert_ders.empty()) {
       ADD_FAILURE() << "cert_ders is empty";
       return false;
@@ -112,7 +113,7 @@
                                "ValidDSASignaturesTest4EE"};
   const char* const crls[] = {"TrustAnchorRootCRL", "DSACACRL"};
   // DSA signatures are intentionally unsupported.
-  ASSERT_FALSE(this->Verify(certs, crls));
+  ASSERT_FALSE(this->Verify(certs, crls, {}));
 }
 
 // Modified version of 4.1.5 Valid DSA Parameter Inheritance Test5
@@ -124,7 +125,7 @@
   const char* const crls[] = {"TrustAnchorRootCRL", "DSACACRL",
                               "DSAParametersInheritedCACRL"};
   // DSA signatures are intentionally unsupported.
-  ASSERT_FALSE(this->Verify(certs, crls));
+  ASSERT_FALSE(this->Verify(certs, crls, {}));
 }
 
 class PkitsTest13SignatureVerificationCustomPathBuilderFoo
@@ -139,7 +140,7 @@
   const char* const crls[] = {"TrustAnchorRootCRL",
                               "nameConstraintsRFC822CA1CRL"};
   // Name constraints on rfc822Names are not supported.
-  ASSERT_FALSE(this->Verify(certs, crls));
+  ASSERT_FALSE(this->Verify(certs, crls, {}));
 }
 
 // Modified version of 4.13.23 Valid RFC822 nameConstraints Test23
@@ -151,7 +152,7 @@
   const char* const crls[] = {"TrustAnchorRootCRL",
                               "nameConstraintsRFC822CA2CRL"};
   // Name constraints on rfc822Names are not supported.
-  ASSERT_FALSE(this->Verify(certs, crls));
+  ASSERT_FALSE(this->Verify(certs, crls, {}));
 }
 
 // Modified version of 4.13.25 Valid RFC822 nameConstraints Test25
@@ -163,7 +164,7 @@
   const char* const crls[] = {"TrustAnchorRootCRL",
                               "nameConstraintsRFC822CA3CRL"};
   // Name constraints on rfc822Names are not supported.
-  ASSERT_FALSE(this->Verify(certs, crls));
+  ASSERT_FALSE(this->Verify(certs, crls, {}));
 }
 
 // Modified version of 4.13.27 Valid DN and RFC822 nameConstraints Test27
@@ -176,7 +177,7 @@
   const char* const crls[] = {"TrustAnchorRootCRL", "nameConstraintsDN1CACRL",
                               "nameConstraintsDN1subCA3CRL"};
   // Name constraints on rfc822Names are not supported.
-  ASSERT_FALSE(this->Verify(certs, crls));
+  ASSERT_FALSE(this->Verify(certs, crls, {}));
 }
 
 // Modified version of 4.13.34 Valid URI nameConstraints Test34
@@ -187,7 +188,7 @@
                                "ValidURInameConstraintsTest34EE"};
   const char* const crls[] = {"TrustAnchorRootCRL", "nameConstraintsURI1CACRL"};
   // Name constraints on uniformResourceIdentifiers are not supported.
-  ASSERT_FALSE(this->Verify(certs, crls));
+  ASSERT_FALSE(this->Verify(certs, crls, {}));
 }
 
 // Modified version of 4.13.36 Valid URI nameConstraints Test36
@@ -198,7 +199,7 @@
                                "ValidURInameConstraintsTest36EE"};
   const char* const crls[] = {"TrustAnchorRootCRL", "nameConstraintsURI2CACRL"};
   // Name constraints on uniformResourceIdentifiers are not supported.
-  ASSERT_FALSE(this->Verify(certs, crls));
+  ASSERT_FALSE(this->Verify(certs, crls, {}));
 }
 
 INSTANTIATE_TYPED_TEST_CASE_P(PathBuilder,
diff --git a/net/cert/internal/verify_certificate_chain_pkits_unittest.cc b/net/cert/internal/verify_certificate_chain_pkits_unittest.cc
index 2039a60d..8c16a3a6e 100644
--- a/net/cert/internal/verify_certificate_chain_pkits_unittest.cc
+++ b/net/cert/internal/verify_certificate_chain_pkits_unittest.cc
@@ -48,14 +48,16 @@
 class VerifyCertificateChainPkitsTestDelegate {
  public:
   static bool Verify(std::vector<std::string> cert_ders,
-                     std::vector<std::string> crl_ders) {
+                     std::vector<std::string> crl_ders,
+                     const PkitsTestSettings& settings) {
     if (cert_ders.empty()) {
       ADD_FAILURE() << "cert_ders is empty";
       return false;
     }
 
-    // PKITS lists chains from trust anchor to target, VerifyCertificateChain
-    // takes them starting with the target and not including the trust anchor.
+    // PKITS lists chains from trust anchor to target, whereas
+    // VerifyCertificateChain takes them starting with the target and ending
+    // with the trust anchor.
     std::vector<scoped_refptr<net::ParsedCertificate>> input_chain;
     CertErrors parsing_errors;
     for (auto i = cert_ders.rbegin(); i != cert_ders.rend(); ++i) {
@@ -97,7 +99,7 @@
                                "ValidDSASignaturesTest4EE"};
   const char* const crls[] = {"TrustAnchorRootCRL", "DSACACRL"};
   // DSA signatures are intentionally unsupported.
-  ASSERT_FALSE(this->Verify(certs, crls));
+  ASSERT_FALSE(this->Verify(certs, crls, {}));
 }
 
 // Modified version of 4.1.5 Valid DSA Parameter Inheritance Test5
@@ -109,7 +111,7 @@
   const char* const crls[] = {"TrustAnchorRootCRL", "DSACACRL",
                               "DSAParametersInheritedCACRL"};
   // DSA signatures are intentionally unsupported.
-  ASSERT_FALSE(this->Verify(certs, crls));
+  ASSERT_FALSE(this->Verify(certs, crls, {}));
 }
 
 class PkitsTest13SignatureVerificationCustom
@@ -124,7 +126,7 @@
   const char* const crls[] = {"TrustAnchorRootCRL",
                               "nameConstraintsRFC822CA1CRL"};
   // Name constraints on rfc822Names are not supported.
-  ASSERT_FALSE(this->Verify(certs, crls));
+  ASSERT_FALSE(this->Verify(certs, crls, {}));
 }
 
 // Modified version of 4.13.23 Valid RFC822 nameConstraints Test23
@@ -136,7 +138,7 @@
   const char* const crls[] = {"TrustAnchorRootCRL",
                               "nameConstraintsRFC822CA2CRL"};
   // Name constraints on rfc822Names are not supported.
-  ASSERT_FALSE(this->Verify(certs, crls));
+  ASSERT_FALSE(this->Verify(certs, crls, {}));
 }
 
 // Modified version of 4.13.25 Valid RFC822 nameConstraints Test25
@@ -148,7 +150,7 @@
   const char* const crls[] = {"TrustAnchorRootCRL",
                               "nameConstraintsRFC822CA3CRL"};
   // Name constraints on rfc822Names are not supported.
-  ASSERT_FALSE(this->Verify(certs, crls));
+  ASSERT_FALSE(this->Verify(certs, crls, {}));
 }
 
 // Modified version of 4.13.27 Valid DN and RFC822 nameConstraints Test27
@@ -161,7 +163,7 @@
   const char* const crls[] = {"TrustAnchorRootCRL", "nameConstraintsDN1CACRL",
                               "nameConstraintsDN1subCA3CRL"};
   // Name constraints on rfc822Names are not supported.
-  ASSERT_FALSE(this->Verify(certs, crls));
+  ASSERT_FALSE(this->Verify(certs, crls, {}));
 }
 
 // Modified version of 4.13.34 Valid URI nameConstraints Test34
@@ -172,7 +174,7 @@
                                "ValidURInameConstraintsTest34EE"};
   const char* const crls[] = {"TrustAnchorRootCRL", "nameConstraintsURI1CACRL"};
   // Name constraints on uniformResourceIdentifiers are not supported.
-  ASSERT_FALSE(this->Verify(certs, crls));
+  ASSERT_FALSE(this->Verify(certs, crls, {}));
 }
 
 // Modified version of 4.13.36 Valid URI nameConstraints Test36
@@ -183,7 +185,7 @@
                                "ValidURInameConstraintsTest36EE"};
   const char* const crls[] = {"TrustAnchorRootCRL", "nameConstraintsURI2CACRL"};
   // Name constraints on uniformResourceIdentifiers are not supported.
-  ASSERT_FALSE(this->Verify(certs, crls));
+  ASSERT_FALSE(this->Verify(certs, crls, {}));
 }
 
 INSTANTIATE_TYPED_TEST_CASE_P(VerifyCertificateChain,
diff --git a/net/http/http_network_session.cc b/net/http/http_network_session.cc
index dc6b262..6f5a1e5 100644
--- a/net/http/http_network_session.cc
+++ b/net/http/http_network_session.cc
@@ -109,7 +109,6 @@
       ssl_config_service(nullptr),
       http_auth_handler_factory(nullptr),
       net_log(nullptr),
-      host_mapping_rules(nullptr),
       socket_performance_watcher_factory(nullptr),
       ignore_certificate_errors(false),
       testing_fixed_http_port(0),
diff --git a/net/http/http_network_session.h b/net/http/http_network_session.h
index d8e867f..89234cb 100644
--- a/net/http/http_network_session.h
+++ b/net/http/http_network_session.h
@@ -21,6 +21,7 @@
 #include "base/memory/ref_counted.h"
 #include "base/memory/weak_ptr.h"
 #include "base/threading/non_thread_safe.h"
+#include "net/base/host_mapping_rules.h"
 #include "net/base/host_port_pair.h"
 #include "net/base/net_export.h"
 #include "net/dns/host_resolver.h"
@@ -95,7 +96,7 @@
     HttpAuthHandlerFactory* http_auth_handler_factory;
     HttpServerProperties* http_server_properties;
     NetLog* net_log;
-    HostMappingRules* host_mapping_rules;
+    HostMappingRules host_mapping_rules;
     SocketPerformanceWatcherFactory* socket_performance_watcher_factory;
     bool ignore_certificate_errors;
     uint16_t testing_fixed_http_port;
diff --git a/net/http/http_stream_factory_impl.cc b/net/http/http_stream_factory_impl.cc
index 669f04f..11b602c 100644
--- a/net/http/http_stream_factory_impl.cc
+++ b/net/http/http_stream_factory_impl.cc
@@ -219,7 +219,7 @@
 }
 
 const HostMappingRules* HttpStreamFactoryImpl::GetHostMappingRules() const {
-  return session_->params().host_mapping_rules;
+  return &session_->params().host_mapping_rules;
 }
 
 void HttpStreamFactoryImpl::OnNewSpdySessionReady(
diff --git a/net/http/http_stream_factory_impl_job_controller.cc b/net/http/http_stream_factory_impl_job_controller.cc
index b234702..986f9a3 100644
--- a/net/http/http_stream_factory_impl_job_controller.cc
+++ b/net/http/http_stream_factory_impl_job_controller.cc
@@ -892,8 +892,7 @@
 GURL HttpStreamFactoryImpl::JobController::ApplyHostMappingRules(
     const GURL& url,
     HostPortPair* endpoint) {
-  const HostMappingRules* mapping_rules = session_->params().host_mapping_rules;
-  if (mapping_rules && mapping_rules->RewriteHost(endpoint)) {
+  if (session_->params().host_mapping_rules.RewriteHost(endpoint)) {
     url::Replacements<char> replacements;
     const std::string port_str = base::UintToString(endpoint->port());
     replacements.SetPort(port_str.c_str(), url::Component(0, port_str.size()));
diff --git a/net/spdy/core/hpack/hpack_huffman_decoder.cc b/net/spdy/core/hpack/hpack_huffman_decoder.cc
index 3e2fc9c..8513579 100644
--- a/net/spdy/core/hpack/hpack_huffman_decoder.cc
+++ b/net/spdy/core/hpack/hpack_huffman_decoder.cc
@@ -292,9 +292,9 @@
   return first_canonical + ordinal_in_length;
 }
 
-char HpackHuffmanDecoder::CanonicalToSource(HuffmanWord canonical) {
+uint8_t HpackHuffmanDecoder::CanonicalToSource(HuffmanWord canonical) {
   DCHECK_LT(canonical, 256u);
-  return static_cast<char>(kCanonicalToSymbol[canonical]);
+  return kCanonicalToSymbol[canonical];
 }
 
 // TODO(jamessynge): Maybe further refactorings, including just passing in a
diff --git a/net/spdy/core/hpack/hpack_huffman_decoder.h b/net/spdy/core/hpack/hpack_huffman_decoder.h
index d33fa581..8658b40 100644
--- a/net/spdy/core/hpack/hpack_huffman_decoder.h
+++ b/net/spdy/core/hpack/hpack_huffman_decoder.h
@@ -57,9 +57,9 @@
   static HuffmanWord DecodeToCanonical(HuffmanCodeLength code_length,
                                        HuffmanWord bits);
 
-  // Converts a canonical symbol to the source symbol (the char in the original
+  // Converts a canonical symbol to the source symbol (the octet in the original
   // string that was encoded).
-  static char CanonicalToSource(HuffmanWord canonical);
+  static uint8_t CanonicalToSource(HuffmanWord canonical);
 };
 
 }  // namespace net
diff --git a/net/spdy/core/hpack/hpack_huffman_decoder_test.cc b/net/spdy/core/hpack/hpack_huffman_decoder_test.cc
index ddf1bcd..8ab010f 100644
--- a/net/spdy/core/hpack/hpack_huffman_decoder_test.cc
+++ b/net/spdy/core/hpack/hpack_huffman_decoder_test.cc
@@ -47,7 +47,7 @@
     return HpackHuffmanDecoder::DecodeToCanonical(code_length, bits);
   }
 
-  static char CanonicalToSource(HuffmanWord canonical) {
+  static uint8_t CanonicalToSource(HuffmanWord canonical) {
     return HpackHuffmanDecoder::CanonicalToSource(canonical);
   }
 };
@@ -68,8 +68,7 @@
     if (canonical == 256u) {
       return canonical;
     }
-    return static_cast<unsigned char>(
-        HpackHuffmanDecoderPeer::CanonicalToSource(canonical));
+    return HpackHuffmanDecoderPeer::CanonicalToSource(canonical);
   }
 
   void EncodeString(SpdyStringPiece input, SpdyString* encoded) {
diff --git a/net/third_party/nist-pkits/generate_tests.py b/net/third_party/nist-pkits/generate_tests.py
index fe9f9b76..ac83c6f0 100644
--- a/net/third_party/nist-pkits/generate_tests.py
+++ b/net/third_party/nist-pkits/generate_tests.py
@@ -32,13 +32,26 @@
   output.write(');\n')
 
 
-def generate_test(test_case_name, test_number, raw_test_name, certs, crls, should_validate,
-                     output):
+def bool_to_str(b):
+  return "true" if b else "false"
+
+
+def output_test(test_case_name, test_number, raw_test_name, subpart_number,
+                info, certs, crls, sanitized_test_names, output):
+  '''Writes a test case to |output|, and appends the test name to
+  |sanitized_test_names|.'''
   sanitized_test_name = 'Section%s%s' % (test_number.split('.')[1],
                                          sanitize_name(raw_test_name))
+
+  if subpart_number is not None:
+    sanitized_test_name += "Subpart%d" % (subpart_number)
+
+  sanitized_test_names.append(sanitized_test_name)
+
   certs_formatted = ', '.join('"%s"' % n for n in certs)
   crls_formatted = ', '.join('"%s"' % n for n in crls)
-  assert_function = 'ASSERT_TRUE' if should_validate else 'ASSERT_FALSE'
+  assert_function = 'ASSERT_TRUE' if info.should_validate else 'ASSERT_FALSE'
+
   output.write('''
 // %(test_number)s %(raw_test_name)s
 WRAPPED_TYPED_TEST_P(%(test_case_name)s, %(sanitized_test_name)s) {
@@ -48,24 +61,74 @@
   const char* const crls[] = {
     %(crls_formatted)s
   };
-  %(assert_function)s(this->Verify(certs, crls));
-}
 ''' % vars())
 
-  return sanitized_test_name
+  default_settings = TestInfo(False)
+
+  settings_str = ''
+
+  # Output any non-default settings. Only settings that differ from
+  # the default settings are written, so as to keep the generated
+  # file more readable.
+  if info.initial_policy_set != default_settings.initial_policy_set:
+    settings_str += '''  settings.SetInitialPolicySet("%s");
+''' % (','.join(info.initial_policy_set))
+
+  if info.initial_explicit_policy != default_settings.initial_explicit_policy:
+    settings_str += '''  settings.initial_explicit_policy = %s;
+''' % bool_to_str(info.initial_explicit_policy)
+
+  if (info.initial_policy_mapping_inhibit !=
+          default_settings.initial_policy_mapping_inhibit):
+    settings_str += '''  settings.initial_policy_mapping_inhibit = %s;
+''' % bool_to_str(info.initial_policy_mapping_inhibit)
+
+  if (info.initial_inhibit_any_policy !=
+          default_settings.initial_inhibit_any_policy):
+    settings_str += '''settings.initial_inhibit_any_policy = %s;
+''' % bool_to_str(info.initial_inhibit_any_policy)
+
+  settings_param_str = '{}'
+
+  if settings_str != '':
+    output.write('''
+  // Custom settings
+  PkitsTestSettings settings;
+''')
+    output.write(settings_str)
+    output.write('\n')
+    settings_param_str = 'settings'
+
+  output.write('''  %(assert_function)s(this->Verify(certs, crls, %(settings_param_str)s));
+}
+''' % vars())
 
 
 # Matches a section header, ex: "4.1 Signature Verification"
 SECTION_MATCHER = re.compile('^\s*(\d+\.\d+)\s+(.+)\s*$')
 # Matches a test header, ex: "4.1.1 Valid Signatures Test1"
 TEST_MATCHER = re.compile('^\s*(\d+\.\d+.\d+)\s+(.+)\s*$')
+
+# Matches the various headers in a test specification.
+EXPECTED_HEADER_MATCHER = re.compile('^\s*Expected Result:')
+PROCEDURE_HEADER_MATCHER = re.compile('^\s*Procedure:')
+PATH_HEADER_MATCHER = re.compile('^\s*Certification Path:')
+
+# Matches the Procedure text if using default settings.
+USING_DEFAULT_SETTINGS_MATCHER = re.compile(
+    '^.*using the \s*default settings.*')
+
+# Matches the description text if using custom settings.
+CUSTOM_SETTINGS_MATCHER = re.compile(
+    '.*this\s+test\s+be\s+validated\s+using\s+the\s+following\s+inputs:.*')
+
 # Match an expected test result. Note that some results in the PDF have a typo
 # "path not should validate" instead of "path should not validate".
 TEST_RESULT_MATCHER = re.compile(
-    '^\s*Expected Result:.*path (should validate|'
-    'should not validate|not should validate)')
-PATH_HEADER_MATCHER = re.compile('^\s*Certification Path:')
-# Matches a line in the certification path, ex: "\u2022 Good CA Cert, Good CA CRL"
+    '^.*path (should validate|should not validate|not should validate)')
+
+# Matches a line in the certification path, ex:
+#    "\u2022 Good CA Cert, Good CA CRL"
 PATH_MATCHER = re.compile('^\s*\xe2\x80\xa2\s*(.+)\s*$')
 # Matches a page number. These may appear in the middle of multi-line fields and
 # thus need to be ignored.
@@ -73,30 +136,69 @@
 # Matches if an entry in a certification path refers to a CRL, ex:
 # "onlySomeReasons CA2 CRL1".
 CRL_MATCHER = re.compile('^.*CRL\d*$')
-def parse_test(lines, i, test_case_name, test_number, test_name, output):
-  expected_result = None
-  certs = []
-  crls = []
 
+
+class TestSections(object):
+  def __init__(self):
+    self.description_lines = []
+    self.procedure_lines = []
+    self.expected_result_lines = []
+    self.cert_path_lines = []
+
+
+def parse_main_test_sections(lines, i):
+  result = TestSections()
+
+  # Read the description lines (text after test name up until
+  # "Procedure:").
+  result.description_lines = []
   while i < len(lines):
-    result_match = TEST_RESULT_MATCHER.match(lines[i])
-    i += 1
-    if result_match:
-      expected_result = result_match.group(1) == 'should validate'
+    if PROCEDURE_HEADER_MATCHER.match(lines[i]):
       break
+    result.description_lines.append(lines[i])
+    i += 1
 
+  # Read the procedure lines (text starting at "Procedure:" and up until
+  # "Expected Result:".
+  result.procedure_lines = []
   while i < len(lines):
-    path_match = PATH_HEADER_MATCHER.match(lines[i])
-    i += 1
-    if path_match:
+    if EXPECTED_HEADER_MATCHER.match(lines[i]):
       break
+    result.procedure_lines.append(lines[i])
+    i += 1
 
+  # Read the expected result lines (text starting at "Expected Result:" and up
+  # until "Certification Path:".
+  result.expected_result_lines = []
+  while i < len(lines):
+    if PATH_HEADER_MATCHER.match(lines[i]):
+      break
+    result.expected_result_lines.append(lines[i])
+    i += 1
+
+  # Read the certification path lines (text starting at "Certification Path:"
+  # and up until the next test title.
+  result.cert_path_lines = []
+  while i < len(lines):
+    if TEST_MATCHER.match(lines[i]) or SECTION_MATCHER.match(lines[i]):
+      break
+    result.cert_path_lines.append(lines[i])
+    i += 1
+
+  return i, result 
+
+
+def parse_cert_path_lines(lines):
   path_lines = []
-  while i < len(lines):
-    line = lines[i].strip()
-    if TEST_MATCHER.match(line) or SECTION_MATCHER.match(line):
-      break
-    i += 1
+  crls = []
+  certs = []
+
+  for line in lines[1:]:
+    line = line.strip()
+
+    if "is composed of the following objects:" in line:
+      continue
+
     if not line or PAGE_NUMBER_MATCHER.match(line):
       continue
     path_match = PATH_MATCHER.match(line)
@@ -114,13 +216,851 @@
       else:
         certs.append(path)
 
-  assert certs
-  assert crls
-  assert expected_result is not None
-  sanitized_test_name = generate_test(test_case_name, test_number, test_name,
-                                      certs, crls, expected_result, output)
+  return certs, crls
 
-  return i, sanitized_test_name
+
+ANY_POLICY = 'anyPolicy'
+TEST_POLICY_1 = 'NIST-test-policy-1'
+TEST_POLICY_2 = 'NIST-test-policy-2'
+TEST_POLICY_3 = 'NIST-test-policy-3'
+TEST_POLICY_6 = 'NIST-test-policy-6'
+
+# TODO(eroman): This omits a few outputs from PKITS:
+#
+#  * authorities-constrained-policy-set
+#  * user-constrained-policy-set
+#  * explicit-policy-indicator
+#
+#  Consider adding the constrained policy sets in the future, if our
+#  verification code supports outputting them.
+class TestInfo(object):
+  """This structure describes a test inputs and outputs"""
+
+  def __init__(self, should_validate,
+               # These defaults come from section 3 of PKITS.pdf
+               initial_policy_set = [ANY_POLICY],
+               initial_explicit_policy = False,
+               initial_policy_mapping_inhibit = False,
+               initial_inhibit_any_policy = False):
+    self.should_validate = should_validate
+    self.initial_policy_set = initial_policy_set
+    self.initial_explicit_policy = initial_explicit_policy
+    self.initial_policy_mapping_inhibit = initial_policy_mapping_inhibit
+    self.initial_inhibit_any_policy = initial_inhibit_any_policy
+
+
+TEST_OVERRIDES = {
+  '4.8.1': [ # All Certificates Same Policy Test1
+    # 1. default settings, but with initial-explicit-policy set. The path
+    # should validate successfully
+    TestInfo(True, initial_explicit_policy=True),
+
+    # 2. default settings, but with initial-explicit-policy set and
+    # initial-policy-set = {NIST-test-policy-1}. The path should validate
+    # successfully. 
+    TestInfo(True, initial_explicit_policy=True,
+             initial_policy_set=[TEST_POLICY_1]),
+
+    # 3. default settings, but with initial-explicit-policy set and
+    # initial-policy-set = {NIST-test-policy-2}. The path should not validate
+    # successfully. 
+    TestInfo(False, initial_explicit_policy=True,
+             initial_policy_set=[TEST_POLICY_2]),
+
+    # 4. default settings, but with initial-explicit-policy set and
+    # initial-policy-set = {NIST-test-policy-1, NIST-test-policy-2}. The path
+    # should validate successfully. 
+    TestInfo(True, initial_explicit_policy=True,
+             initial_policy_set=[TEST_POLICY_1, TEST_POLICY_2]),
+  ],
+
+  '4.8.2': [ # All Certificates No Policies Test2
+    # 1. default settings. The path should validate successfully.
+    TestInfo(True),
+
+    # 2. default settings, but with initial-explicit-policy set. The path
+    # should not validate successfully
+    TestInfo(False, initial_explicit_policy=True),
+  ],
+
+  '4.8.3': [ # Different Policies Test3
+    # 1. default settings. The path should validate successfully.
+    TestInfo(True),
+
+    # 2. default settings, but with initial-explicit-policy set. The path
+    # should not validate successfully.
+    TestInfo(False, initial_explicit_policy=True),
+
+    # 3. default settings, but with initial-explicit-policy set and
+    # initial-policy-set = {NIST-test-policy-1, NIST-test-policy-2}. The path
+    # should not validate successfully. 
+    TestInfo(False, initial_explicit_policy=True,
+             initial_policy_set=[TEST_POLICY_1, TEST_POLICY_2]),
+  ],
+
+  '4.8.4': [ # Different Policies Test4 
+    # Procedure: Validate Different Policies Test4 EE using the default
+    # settings or open and verify Signed Test Message 6.2.2.69 using the
+    # default settings. 
+    #
+    # Expected Result: The authorities-constrained-policy-set and the
+    # user-constrained-policy-set will be empty. The explicit-policy-indicator
+    # will be set if the application can process the policyConstraints
+    # extension. If the application can process the policyConstraints extension
+    # then the path should not validate successfully. If the application can
+    # not process the policyConstraints extension, then the path should
+    # validate successfully.
+    TestInfo(False),
+  ],
+
+  '4.8.5': [ # 4.8.5 Different Policies Test5
+    # Procedure: Validate Different Policies Test5 EE using the default
+    # settings or open and verify Signed Test Message 6.2.2.70 using the
+    # default settings.
+    #
+    # Expected Result: The authorities-constrained-policy-set and the
+    # user-constrained-policy-set will be empty. The explicit-policy-indicator
+    # will be set if the application can process the policyConstraints
+    # extension. If the application can process the policyConstraints extension
+    # then the path should not validate successfully. If the application can
+    # not process the policyConstraints extension, then the path should
+    # validate successfully
+    TestInfo(False),
+  ],
+
+  '4.8.6': [ # Overlapping Policies Test6
+    # 1. default settings. The path should validate successfully.
+    TestInfo(True),
+
+    # 2. default settings, but with initial-policy-set = {NIST-test-policy-1}.
+    # The path should validate successfully.
+    TestInfo(True, initial_policy_set=[TEST_POLICY_1]),
+
+    # 3. default settings, but with initial-policy-set = {NIST-test-policy-2}.
+    # The path should not validate successfully. 
+    TestInfo(False, initial_policy_set=[TEST_POLICY_2]),
+  ],
+
+  '4.8.7': [ # Different Policies Test7
+    # Procedure: Validate Different Policies Test7 EE using the default
+    # settings or open and verify Signed Test Message 6.2.2.72 using the
+    # default settings.
+    #
+    # Expected Result: The authorities-constrained-policy-set and the
+    # user-constrained-policy-set will be empty. If the
+    # explicit-policy-indicator will be set if the application can process the
+    # policyConstraints extension. If the application can process the
+    # policyConstraints extension, then the path should not validate
+    # successfully. If the application can not process the policyConstraints
+    # extension, then the path should validate successfully. 
+    TestInfo(False),
+  ],
+
+  '4.8.8': [ # Different Policies Test8 
+    # Procedure: Validate Different Policies Test8 EE using the default
+    # settings or open and verify Signed Test Message 6.2.2.73 using the
+    # default settings.
+    #
+    # Expected Result: The authorities-constrained-policy-set and the
+    # user-constrained-policy-set will be empty. The explicit-policy-indicator
+    # will be set if the application can process the policyConstraints
+    # extension. If the application can process the policyConstraints extension
+    # then the path should not validate successfully. If the application can
+    # not process the policyConstraints extension, then the path should
+    # validate successfully.
+    TestInfo(False),
+  ],
+
+  '4.8.9': [ # Different Policies Test9
+    # Procedure: Validate Different Policies Test9 EE using the default
+    # settings or open and verify Signed Test Message 6.2.2.74 using the
+    # default settings.
+    #
+    # Expected Result: The authorities-constrained-policy-set and the
+    # user-constrained-policy-set will be empty. The explicit-policy-indicator
+    # will be set if the application can process the policyConstraints
+    # extension. If the application can process the policyConstraints
+    # extension, then the path should not validate successfully. If the
+    # application can not process the policyConstraints extension, then the
+    # path should validate successfully. 
+    TestInfo(False),
+  ],
+
+  '4.8.10': [ # All Certificates Same Policies Test10
+    # 1. default settings. The path should validate successfully.
+    TestInfo(True),
+    
+    # 2. default settings, but with initial-policy-set = {NIST-test-policy-1}.
+    # The path should validate successfully.
+    TestInfo(True, initial_policy_set=[TEST_POLICY_1]),
+
+    # 3. default settings, but with initial-policy-set = {NIST-test-policy-2}.
+    # The path should validate successfully.
+    TestInfo(True, initial_policy_set=[TEST_POLICY_2]),
+  ],
+
+  '4.8.11': [ # All Certificates AnyPolicy Test11
+    # 1. default settings. The path should validate successfully.
+    TestInfo(True),
+
+    # 2. default settings, but with initial-policy-set = {NIST-test-policy-1}.
+    # The path should validate successfully. 
+    TestInfo(True, initial_policy_set=[TEST_POLICY_1]),
+  ],
+
+  '4.8.12': [ # Different Policies Test12
+    # Procedure: Validate Different Policies Test12 EE using the default
+    # settings or open and verify Signed Test Message 6.2.2.77 using the
+    # default settings.
+    #
+    # Expected Result: The authorities-constrained-policy-set and the
+    # user-constrained-policy-set will be empty. The explicit-policy-indicator
+    # will be set if the application can process the policyConstraints
+    # extension. If the application can process the policyConstraints
+    # extension, then the path should not validate successfully. If the
+    # application can not process the policyConstraints extension, then the
+    # path should validate successfully. 
+    TestInfo(False),
+  ],
+
+  '4.8.13': [ # All Certificates Same Policies Test13
+    # 1. default settings, but with initial-policy-set = {NIST-test-policy-1}.
+    # The path should validate successfully.
+    TestInfo(True, initial_policy_set=[TEST_POLICY_1]),
+
+    # 2. default settings, but with initial-policy-set = {NIST-test-policy-2}.
+    # The path should validate successfully.
+    TestInfo(True, initial_policy_set=[TEST_POLICY_2]),
+
+    # 3. default settings, but with initial-policy-set = {NIST-test-policy-3}.
+    # The path should validate successfully. 
+    TestInfo(True, initial_policy_set=[TEST_POLICY_3]),
+  ],
+
+  '4.8.14': [ # AnyPolicy Test14
+    # 1. default settings, but with initial-policy-set = {NIST-test-policy-1}.
+    # The path should validate successfully.
+    TestInfo(True, initial_policy_set=[TEST_POLICY_1]),
+
+    # 2. default settings, but with initial-policy-set = {NIST-test-policy-2}.
+    # The path should not validate successfully. 
+    TestInfo(False, initial_policy_set=[TEST_POLICY_2]),
+  ],
+
+  '4.8.15': [ # User Notice Qualifier Test15
+    # Procedure: Validate User Notice Qualifier Test15 EE using the default
+    # settings or open and verify Signed Test Message 6.2.2.80 using the
+    # default settings.
+    #
+    # Expected Result: The authorities-constrained-policy-set will be
+    # {NIST-test-policy-1} and the explicit-policy-indicator will be the same
+    # as the initial-explicit-policy indicator. If the initial-policy-set is
+    # any-policy or otherwise includes NIST-test-policy-1, then the
+    # user-constrained-policy-set will be {NIST-test-policy-1}. If not, the
+    # user-constrained-policy-set will be empty. If the initial-explicit-policy
+    # indicator is set and the initial-policy-set does not include
+    # NIST-test-policy-1, then the path should be rejected, otherwise it should
+    # validate successfully. If the path validates successfully, then the
+    # application should display the user notice.
+    TestInfo(True),
+  ],
+
+  '4.8.16': [ # User Notice Qualifier Test16
+    # Procedure: Validate User Notice Qualifier Test16 EE using the default
+    # settings or open and verify Signed Test Message 6.2.2.81 using the
+    # default settings.
+    #
+    # Expected Result: The authorities-constrained-policy-set will be
+    # {NIST-test-policy-1} and the explicit-policy-indicator will be the same
+    # as the initial-explicit-policy indicator. If the initial-policy-set is
+    # any-policy or otherwise includes NIST-test-policy-1, then the
+    # user-constrained-policy-set will be {NIST-test-policy-1}. If not, the
+    # user-constrained-policy-set will be empty. If the initial-explicit-policy
+    # indicator is set and the initial-policy-set does not include
+    # NIST-test-policy-1, then the path should be rejected, otherwise it should
+    # validate successfully. If the path validates successfully, then the
+    # application should display the user notice associated with
+    # NIST-test-policy-1. The user notice associated with NIST-test-policy-2
+    # should not be displayed. 
+    TestInfo(True),
+  ],
+
+  '4.8.17': [ # User Notice Qualifier Test17
+    # Procedure: Validate User Notice Qualifier Test17 EE using the default
+    # settings or open and verify Signed Test Message 6.2.2.82 using the
+    # default settings.
+    #
+    # Expected Result: The authorities-constrained-policy-set will be
+    # {NIST-test-policy-1} and the explicit-policy-indicator will be the same
+    # as the initial-explicit-policy indicator. If the initial-policy-set is
+    # any-policy or otherwise includes NIST-test-policy-1, then the
+    # user-constrained-policy-set will be {NIST-test-policy-1}. If not, the
+    # user-constrained-policy-set will be empty. If the initial-explicit-policy
+    # indicator is set and the initial-policy-set does not include
+    # NIST-test-policy-1, then the path should be rejected, otherwise it should
+    # validate successfully. If the path validates successfully, then the
+    # application should display the user notice associated with anyPolicy. 
+    TestInfo(True),
+  ],
+
+  '4.8.18': [ # User Notice Qualifier Test18 
+    # 1. default settings, but with initial-policy-set = {NIST-test-policy-1}.
+    # The path should validate successfully and the qualifier associated with
+    # NIST-test-policy-1 in the end entity certificate should be displayed.
+    TestInfo(True, initial_policy_set=[TEST_POLICY_1]),
+
+    # 2. default settings, but with initial-policy-set = {NIST-test-policy-2}.
+    # The path should validate successfully and the qualifier associated with
+    # anyPolicy in the end entity certificate should be displayed. 
+    TestInfo(True, initial_policy_set=[TEST_POLICY_2]),
+  ],
+
+  '4.8.19': [ # User Notice Qualifier Test19
+    # Procedure: Validate User Notice Qualifier Test19 EE using the default
+    # settings or open and verify Signed Test Message 6.2.2.84 using the
+    # default settings.
+    #
+    # Expected Result: The authorities-constrained-policy-set will be
+    # {NIST-test-policy-1} and the explicit-policy-indicator will be the same
+    # as the initial-explicit-policy indicator. If the initial-policy-set is
+    # any-policy or otherwise includes NIST-test-policy-1, then the
+    # user-constrained-policy-set will be {NIST-test-policy-1}. If not, the
+    # user-constrained-policy-set will be empty. If the initial-explicit-policy
+    # indicator is set and the initial-policy-set does not include
+    # NIST-test-policy-1, then the path should be rejected, otherwise it should
+    # validate successfully.  Since the explicitText exceeds the maximum size
+    # of 200 characters, the application may choose to reject the certificate.
+    # If the application accepts the certificate, display of the user notice is
+    # optional. 
+    TestInfo(True),
+  ],
+
+  '4.8.20': [ # CPS Pointer Qualifier Test20
+    # Procedure: Validate CPS Pointer Qualifier Test20 EE using the default
+    # settings or open and verify Signed Test Message 6.2.2.85 using the
+    # default settings. (If possible, it is recommended that this test be run
+    # with the initial-explicit-policy indicator set. If this can not be done,
+    # manually check that the authorities-constrained-policy-set and
+    # user-constrained-policy-set are correct.)
+    #
+    # Expected Result: The authorities-constrained-policy-set will be
+    # {NIST-test-policy-1} and the explicit-policy-indicator will be the same
+    # as the initial-explicit-policy indicator. If the initial-policy-set is
+    # any-policy or otherwise includes NIST-test-policy-1, then the
+    # user-constrained-policy-set will be {NIST-test-policy-1}. If not, the
+    # user-constrained-policy-set will be empty. If the initial-explicit-policy
+    # indicator is set and the initial-policy-set does not include
+    # NIST-test-policy-1, then the path should be rejected, otherwise it should
+    # validate successfully. The CPS pointer in the qualifier should be
+    # associated with NIST-testpolicy-1 in the
+    # authorities-constrained-policy-set (and in the user-constrained-policy-set
+    # if NIST-test-policy-1 is in that set). There are no processing
+    # requirements associated with the CPS pointer qualifier. 
+    TestInfo(True, initial_explicit_policy=True,
+             initial_policy_set=[TEST_POLICY_1]),
+  ],
+
+  '4.10.1': [ # Valid Policy Mapping Test1
+    # 1. default settings, but with initial-policy-set = {NIST-test-policy-1}.
+    # The path should validate successfully.
+    TestInfo(True, initial_policy_set=[TEST_POLICY_1]),
+
+    # 2. default settings, but with initial-policy-set = {NIST-test-policy-2}.
+    # The path should not validate successfully. 
+    TestInfo(False, initial_policy_set=[TEST_POLICY_2]),
+
+    # 3. default settings, but with initial-policy-mapping-inhibit set. The
+    # path should not validate successfully.
+    TestInfo(False, initial_policy_mapping_inhibit=True),
+  ],
+
+  '4.10.2': [ # Invalid Policy Mapping Test2
+    # 1. default settings. The path should not validate successfully.
+    TestInfo(False),
+
+    # 2. default settings, but with initial-policy-mapping-inhibit set. The
+    # path should not validate successfully. 
+    TestInfo(False, initial_policy_mapping_inhibit=True),
+  ],
+
+  '4.10.3': [ # Valid Policy Mapping Test3
+    # 1. default settings, but with initial-policy-set = {NIST-test-policy-1}.
+    # The path should not validate successfully.
+    TestInfo(False, initial_policy_set=[TEST_POLICY_1]),
+
+    # 2. default settings, but with initial-policy-set = {NIST-test-policy-2}.
+    # The path should validate successfully. 
+    TestInfo(True, initial_policy_set=[TEST_POLICY_2]),
+  ],
+
+  '4.10.4': [ # Invalid Policy Mapping Test4
+    # Procedure: Validate Invalid Policy Mapping Test4 EE using the default
+    # settings or open and verify Signed Test Message 6.2.2.97 using the
+    # default settings.
+    #
+    # Expected Result: The authorities-constrained-policy-set and the
+    # user-constrained-policy-set will be empty and the
+    # explicit-policy-indicator will be set (if the application can process the
+    # policyConstraints extension). If the application can process the
+    # policyConstraints extension, then the path should be rejected, otherwise
+    # it should validate successfully. 
+    TestInfo(False),
+  ],
+
+  '4.10.5': [ # Valid Policy Mapping Test5
+    # 1. default settings, but with initial-policy-set = {NIST-test-policy-1}.
+    # The path should validate successfully.
+    TestInfo(True, initial_policy_set=[TEST_POLICY_1]),
+
+    # 2. default settings, but with initial-policy-set = {NIST-test-policy-6}.
+    # The path should not validate successfully. 
+    TestInfo(False, initial_policy_set=[TEST_POLICY_6]),
+  ],
+
+  '4.10.6': [ # Valid Policy Mapping Test6
+    # 1. default settings, but with initial-policy-set = {NIST-test-policy-1}.
+    # The path should validate successfully.
+    TestInfo(True, initial_policy_set=[TEST_POLICY_1]),
+
+    # 2. default settings, but with initial-policy-set = {NIST-test-policy-6}.
+    # The path should not validate successfully. 
+    TestInfo(False, initial_policy_set=[TEST_POLICY_6]),
+  ],
+
+  '4.10.9': [ # Valid Policy Mapping Test9
+    # Procedure: Validate Valid Policy Mapping Test9 EE using the default
+    # settings or open and verify Signed Test Message 6.2.2.102 using the
+    # default settings.
+    #
+    # Expected Result: The authorities-constrained-policy-set will be
+    # {NIST-test-policy-1} and the explicit-policy-indicator will be set (if
+    # the application can process the policyConstraints extension). If the
+    # initial-policy-set is any-policy or otherwise includes
+    # NIST-test-policy-1, then the user-constrained-policy-set will be
+    # {NIST-test-policy-1}. If not, the user-constrained-policy-set will be
+    # empty. If the initial-policy-set does not include NIST-test-policy-1 (and
+    # the application can process the policyConstraints extension), then the
+    # path should be rejected, otherwise it should validate successfully. 
+    TestInfo(True),
+  ],
+
+  '4.10.10': [ # Invalid Policy Mapping Test10
+    # Procedure: Validate Invalid Policy Mapping Test10 EE using the default
+    # settings or open and verify Signed Test Message 6.2.2.103 using the
+    # default settings.
+    #
+    # Expected Result: The authorities-constrained-policy-set and the
+    # user-constrained-policy-set will be empty and the
+    # explicit-policy-indicator will be set (if the application can process the
+    # policyConstraints extension). If the application can process the
+    # policyConstraints extension, then the path should be rejected, otherwise
+    # it should validate successfully.
+    TestInfo(False),
+  ],
+
+  '4.10.11': [ # Valid Policy Mapping Test11
+    # Procedure: Validate Valid Policy Mapping Test11 EE using the default
+    # settings or open and verify Signed Test Message 6.2.2.104 using the
+    # default settings.
+    #
+    # Expected Result: The authorities-constrained-policy-set will be
+    # {NIST-test-policy-1} and the explicit-policy-indicator will be set (if
+    # the application can process the policyConstraints extension). If the
+    # initial-policy-set is any-policy or otherwise includes
+    # NIST-test-policy-1, then the user-constrained-policy-set will be
+    # {NIST-test-policy-1}. If not, the user-constrained-policy-set will be
+    # empty. If the initial-policy-set does not include NIST-test-policy-1 (and
+    # the application can process the policyConstraints extension), then the
+    # path should be rejected, otherwise it should validate successfully. 
+    TestInfo(True),
+  ],
+
+  '4.10.12': [ # Valid Policy Mapping Test12
+    # 1. default settings, but with initial-policy-set = {NIST-test-policy-1}.
+    # The path should validate successfully and the application should display
+    # the user notice associated with NIST-test-policy-3 in the end entity
+    # certificate.
+    TestInfo(True, initial_policy_set=[TEST_POLICY_1]),
+
+    # 2. default settings, but with initial-policy-set = {NIST-test-policy-2}.
+    # The path should validate successfully and the application should display
+    # the user notice associated with anyPolicy in the end entity certificate. 
+    TestInfo(True, initial_policy_set=[TEST_POLICY_2]),
+  ],
+
+  '4.10.13': [ # Valid Policy Mapping Test13
+    # Procedure: Validate Valid Policy Mapping Test13 EE using the default
+    # settings or open and verify Signed Test Message 6.2.2.106 using the
+    # default settings.
+    #
+    # Expected Result: The authorities-constrained-policy-set will be
+    # {NIST-test-policy-1} and the explicit-policy-indicator will be set (if
+    # the application can process the policyConstraints extension). If the
+    # initial-policy-set is any-policy or otherwise includes
+    # NIST-test-policy-1, then the user-constrained-policy-set will be
+    # {NIST-test-policy-1}. If not, the user-constrained-policy-set will be
+    # empty. If the initial-policy-set does not include NIST-test-policy-1 (and
+    # the application can process the policyConstraints extension), then the
+    # path should be rejected, otherwise it should validate successfully. If
+    # the path is accepted, the application should display the user notice
+    # associated with NIST-testpolicy-1 in the intermediate certificate. 
+    TestInfo(True),
+  ],
+
+  '4.10.14': [ # Valid Policy Mapping Test14
+    # Procedure: Validate Valid Policy Mapping Test14 EE using the default
+    # settings or open and verify Signed Test Message 6.2.2.107 using the
+    # default settings.
+    #
+    # Expected Result: The authorities-constrained-policy-set will be
+    # {NIST-test-policy-1} and the explicit-policy-indicator will be set (if
+    # the application can process the policyConstraints extension). If the
+    # initial-policy-set is any-policy or otherwise includes
+    # NIST-test-policy-1, then the user-constrained-policy-set will be
+    # {NIST-test-policy-1}. If not, the user-constrained-policy-set will be
+    # empty. If the initial-policy-set does not include NIST-test-policy-1 (and
+    # the application can process the policyConstraints extension), then the
+    # path should be rejected, otherwise it should validate successfully. If
+    # the path is accepted, the application should display the user notice
+    # associated with anyPolicy in the intermediate certificate
+    TestInfo(True),
+  ],
+
+  '4.11.1': [ # Invalid inhibitPolicyMapping Test1 
+    # Procedure: Validate Invalid inhibitPolicyMapping Test1 EE using the
+    # default settings or open and verify Signed Test Message 6.2.2.108 using
+    # the default settings.
+    #
+    # Expected Result: The authorities-constrained-policy-set and the
+    # user-constrained-policy-set will be empty. The explicit-policy-indicator
+    # will be set.  The path should not validate successfully. 
+    TestInfo(False),
+  ],
+
+  '4.11.2': [ # Valid inhibitPolicyMapping Test2
+    # Procedure: Validate Valid inhibitPolicyMapping Test2 EE using the default
+    # settings or open and verify Signed Test Message 6.2.2.109 using the
+    # default settings.
+    #
+    # Expected Result: The authorities-constrained-policy-set will be
+    # {NIST-test-policy-1} and the explicit-policy-indicator will be set. If
+    # the initial-policy-set is any-policy or otherwise includes
+    # NIST-test-policy-1, then the path should validate successfully. 
+    TestInfo(True),
+  ],
+
+  '4.11.3': [ # Invalid inhibitPolicyMapping Test3 
+    # Procedure: Validate Invalid inhibitPolicyMapping Test3 EE using the
+    # default settings or open and verify Signed Test Message 6.2.2.110 using
+    # the default settings.
+    #
+    # Expected Result: The authorities-constrained-policy-set and the
+    # user-constrained-policy-set will be empty and the
+    # explicit-policy-indicator will be set.  The path should not validate
+    # successfully. 
+    TestInfo(False),
+  ],
+
+  '4.11.4': [ # Valid inhibitPolicyMapping Test4
+    # Procedure: Validate Valid inhibitPolicyMapping Test4 EE using the default
+    # settings or open and verify Signed Test Message 6.2.2.111 using the
+    # default settings.
+    #
+    # Expected Result: The authorities-constrained-policy-set will be
+    # {NIST-test-policy-2} and the explicit-policy-indicator will be set. If
+    # the initial-policy-set is any-policy or otherwise includes
+    # NIST-test-policy-2, then the path should validate successfully. 
+    TestInfo(True),
+  ],
+
+  '4.11.5': [ # Invalid inhibitPolicyMapping Test5
+    # Procedure: Validate Invalid inhibitPolicyMapping Test5 EE using the
+    # default settings or open and verify Signed Test Message 6.2.2.112 using
+    # the default settings.
+    #
+    # Expected Result: The authorities-constrained-policy-set and the
+    # user-constrained-policy-set will be empty and the
+    # explicit-policy-indicator will be set.  The path should not validate
+    # successfully. 
+    TestInfo(False),
+  ],
+
+  '4.11.6': [ # Invalid inhibitPolicyMapping Test6
+    # Procedure: Validate Invalid inhibitPolicyMapping Test6 EE using the
+    # default settings or open and verify Signed Test Message 6.2.2.113 using
+    # the default settings.
+    #
+    # Expected Result: The authorities-constrained-policy-set and the
+    # user-constrained-policy-set will be empty and the
+    # explicit-policy-indicator will be set. The path should not validate
+    # successfully. 
+    TestInfo(False),
+  ],
+
+  '4.11.7': [ # Valid Self-Issued inhibitPolicyMapping Test7
+    # Procedure: Validate Valid Self-Issued inhibitPolicyMapping Test7 EE using
+    # the default settings or open and verify Signed Test Message 6.2.2.114
+    # using the default settings.
+    #
+    # Expected Result: The authorities-constrained-policy-set will be
+    # {NIST-test-policy-1} and the explicit-policy-indicator will be set. If
+    # the initial-policy-set is any-policy or otherwise includes
+    # NIST-test-policy-1, then the path should validate successfully. 
+    TestInfo(True),
+  ],
+
+  '4.11.8': [ # Invalid Self-Issued inhibitPolicyMapping Test8
+    # Procedure: Validate Invalid Self-Issued inhibitPolicyMapping Test8 EE
+    # using the default settings or open and verify Signed Test Message
+    # 6.2.2.115 using the default settings.
+    #
+    # Expected Result: The authorities-constrained-policy-set and
+    # user-constrained-policy-set will be empty and the
+    # explicit-policy-indicator will be set. The path should not validate
+    # successfully. 
+    TestInfo(False),
+  ],
+
+  '4.11.9': [ # Invalid Self-Issued inhibitPolicyMapping Test9
+    # Procedure: Validate Invalid Self-Issued inhibitPolicyMapping Test9 EE
+    # using the default settings or open and verify Signed Test Message
+    # 6.2.2.116 using the default settings.
+    #
+    # Expected Result: The authorities-constrained-policy-set and
+    # user-constrained-policy-set will be empty and the
+    # explicit-policy-indicator will be set. The path should not validate
+    # successfully. 
+    TestInfo(False),
+  ],
+
+  '4.11.10': [ # Invalid Self-Issued inhibitPolicyMapping Test10
+    # Procedure: Validate Invalid Self-Issued inhibitPolicyMapping Test10 EE
+    # using the default settings or open and verify Signed Test Message
+    # 6.2.2.117 using the default settings.
+    #
+    # Expected Result: The authorities-constrained-policy-set and
+    # user-constrained-policy-set will be empty and the
+    # explicit-policy-indicator will be set. The path should not validate
+    # successfully. 
+    TestInfo(False),
+  ],
+
+  '4.11.11': [ # Invalid Self-Issued inhibitPolicyMapping Test11
+    # Procedure: Validate Invalid Self-Issued inhibitPolicyMapping Test11 EE
+    # using the default settings or open and verify Signed Test Message
+    # 6.2.2.118 using the default settings.
+    #
+    # Expected Result: The authorities-constrained-policy-set and
+    # user-constrained-policy-set will be empty and the
+    # explicit-policy-indicator will be set. The path should not validate
+    # successfully. 
+    TestInfo(False),
+  ],
+
+  '4.12.1': [ # Invalid inhibitAnyPolicy Test1
+    # Procedure: Validate Invalid inhibitAnyPolicy Test1 EE using the default
+    # settings or open and verify Signed Test Message 6.2.2.119 using the
+    # default settings.
+    #
+    # Expected Result: The authorities-constrained-policy-set and
+    # user-constrained-policy-set will be empty and the
+    # explicit-policy-indicator will be set (if the application can process the
+    # policyConstraints extension). If the application can process the
+    # policyConstraints extension, then the path should not validate
+    # successfully. 
+    TestInfo(False),
+  ],
+
+  '4.12.2': [ # Valid inhibitAnyPolicy Test2
+    # Procedure: Validate Valid inhibitAnyPolicy Test2 EE using the default
+    # settings or open and verify Signed Test Message 6.2.2.120 using the
+    # default settings.
+    #
+    # Expected Result: The authorities-constrained-policy-set will be
+    # {NIST-test-policy-1} and the explicit-policy-indicator will be set (if
+    # the application can process the policyConstraints extension). If the
+    # initial-policy-set is any-policy or otherwise includes
+    # NIST-test-policy-1, then the user-constrained-policy-set will be
+    # {NIST-test-policy-1} and the path should validate successfully. If not,
+    # then the user-constrained-policy-set will be empty. If the
+    # user-constrained-policy-set is empty and the application can process the
+    # policyConstraints extension, then the path should not validate
+    # successfully. 
+
+    TestInfo(True),
+  ],
+
+  '4.12.3': [ # inhibitAnyPolicy Test3 
+     # 1. default settings. The path should validate successfully.
+    TestInfo(True),
+
+     # 2. default settings, but with initial-inhibit-any-policy set. The path
+     # should not validate successfully. 
+    TestInfo(False, initial_inhibit_any_policy=True),
+  ],
+
+  '4.12.4': [ # Invalid inhibitAnyPolicy Test4
+    # Procedure: Validate Invalid inhibitAnyPolicy Test4 EE using the default
+    # settings or open and verify Signed Test Message 6.2.2.122 using the
+    # default settings.
+    #
+    # Expected Result: The authorities-constrained-policy-set and
+    # user-constrained-policy-set will be empty and the
+    # explicit-policy-indicator will be set (if the application can process the
+    # policyConstraints extension). If the application can process the
+    # policyConstraints extension, then the path should not validate
+    # successfully. 
+    TestInfo(False),
+  ],
+
+  '4.12.5': [ # Invalid inhibitAnyPolicy Test5 
+    # Procedure: Validate Invalid inhibitAnyPolicy Test5 EE using the default
+    # settings or open and verify Signed Test Message 6.2.2.123 using the
+    # default settings.
+    #
+    # Expected Result: The authorities-constrained-policy-set and
+    # user-constrained-policy-set will be empty and the
+    # explicit-policy-indicator will be set (if the application can process the
+    # policyConstraints extension). If the application can process the
+    # policyConstraints extension, then the path should not validate
+    # successfully.
+    TestInfo(False),
+  ],
+
+  '4.12.6': [ # Invalid inhibitAnyPolicy Test6
+    # Procedure: Validate Invalid inhibitAnyPolicy Test6 EE using the default
+    # settings or open and verify Signed Test Message 6.2.2.124 using the
+    # default settings.
+    #
+    # Expected Result: The authorities-constrained-policy-set and
+    # user-constrained-policy-set will be empty and the
+    # explicit-policy-indicator will be set (if the application can process the
+    # policyConstraints extension). If the application can process the
+    # policyConstraints extension, then the path should not validate
+    # successfully. 
+    TestInfo(False),
+  ],
+
+  '4.12.7': [ # Valid Self-Issued inhibitAnyPolicy Test7
+    # Procedure: Validate Valid Self-Issued inhibitAnyPolicy Test7 EE using the
+    # default settings or open and verify Signed Test Message 6.2.2.125 using
+    # the default settings.
+    #
+    # Expected Result: The authorities-constrained-policy-set will be
+    # {NIST-test-policy-1} and the explicit-policy-indicator will be set (if
+    # the application can process the policyConstraints extension). If the
+    # initial-policy-set is any-policy or otherwise includes
+    # NIST-test-policy-1, then the user-constrained-policy-set will be
+    # {NIST-test-policy-1} and the path should validate successfully. If not,
+    # then the user-constrained-policy-set will be empty. If the
+    # user-constrained-policy-set is empty and the application can process the
+    # policyConstraints extension, then the path should not validate
+    # successfully. 
+    TestInfo(True),
+  ],
+
+  '4.12.8': [ # Invalid Self-Issued inhibitAnyPolicy Test8 
+    # Procedure: Validate Invalid Self-Issued inhibitAnyPolicy Test8 EE using
+    # the default settings or open and verify Signed Test Message 6.2.2.126
+    # using the default settings.
+    #
+    # Expected Result: The authorities-constrained-policy-set and
+    # user-constrained-policy-set will be empty and the
+    # explicit-policy-indicator will be set (if the application can process the
+    # policyConstraints extension). If the application can process the
+    # policyConstraints extension, then the path should not validate
+    # successfully. 
+    TestInfo(False),
+  ],
+
+  '4.12.9': [ # Valid Self-Issued inhibitAnyPolicy Test9
+    # Procedure: Validate Valid Self-Issued inhibitAnyPolicy Test9 EE using the
+    # default settings or open and verify Signed Test Message 6.2.2.127 using
+    # the default settings.
+    #
+    # Expected Result: The authorities-constrained-policy-set will be
+    # {NIST-test-policy-1} and the explicit-policy-indicator will be set (if
+    # the application can process the policyConstraints extension). If the
+    # initial-policy-set is any-policy or otherwise includes
+    # NIST-test-policy-1, then the user-constrained-policy-set will be
+    # {NIST-test-policy-1} and the path should validate successfully. If not,
+    # then the user-constrained-policy-set will be empty. If the
+    # user-constrained-policy-set is empty and the application can process the
+    # policyConstraints extension, then the path should not validate
+    # successfully. 
+    TestInfo(True),
+  ],
+
+  '4.12.10': [ # Invalid Self-Issued inhibitAnyPolicy Test10
+    # Procedure: Validate Invalid Self-Issued inhibitAnyPolicy Test10 EE using
+    # the default settings or open and verify Signed Test Message 6.2.2.128
+    # using the default settings.
+    #
+    # Expected Result: The authorities-constrained-policy-set and
+    # user-constrained-policy-set will be empty and the
+    # explicit-policy-indicator will be set (if the application can process the
+    # policyConstraints extension). If the application can process the
+    # policyConstraints extension, then the path should not validate
+    # successfully. 
+    TestInfo(False),
+  ],
+}
+
+
+def parse_test(lines, i, test_case_name, test_number, test_name,
+               sanitized_test_names, output):
+  # Start by doing a coarse level of parsing that separates out the lines for
+  # the main sections.
+  i, test_sections = parse_main_test_sections(lines, i)
+
+  certs, crls = parse_cert_path_lines(test_sections.cert_path_lines)
+
+  # Most tests have a formulaic specification: they use the default
+  # settings, and have one expectation. These are easily parsed and are handled
+  # programmatically. In contrast, many of the policies tests have a more
+  # complicated specification which involves multiple subtests having various
+  # settings, as well as expectations described in terms of supported
+  # extensions. Rather than try to handle all the nuanced language, these are
+  # handled manually via "overrides".
+  overrides = TEST_OVERRIDES.get(test_number, None)
+
+  if overrides is None:
+    # Verify that the test description doesn't include numbered subparts (those
+    # are not handled here).
+    if CUSTOM_SETTINGS_MATCHER.match(" ".join(test_sections.description_lines)):
+      sys.stderr.write('Unexpected custom settings for %s\n' % test_number)
+      sys.exit(1)
+
+    # Verify that the test is using only default settings.
+    if not USING_DEFAULT_SETTINGS_MATCHER.match(
+        " ".join(test_sections.procedure_lines)):
+      sys.stderr.write('Unexpected procedure for %s: %s\n' %
+                       (test_number, " ".join(test_section.procedure_lines)))
+      sys.exit(1)
+
+    # Check whether expected result is validation success or failure.
+    result_match = TEST_RESULT_MATCHER.match(
+       test_sections.expected_result_lines[0])
+    if not result_match:
+      sys.stderr.write('Unknown expectation for %s:\n%s\n' % (
+          test_number, " ".join(test_sections.expected_result_lines)))
+      sys.exit(1)
+    # Initializes with default settings.
+    info = TestInfo(result_match.group(1) == 'should validate')
+
+    output_test(test_case_name, test_number, test_name, None, info, certs,
+                crls, sanitized_test_names, output)
+  else:
+    # The overrides may have a series of inputs (settings) and outputs
+    # (success/failure) for this test. Output each as a separate test case.
+    for subpart_i in range(len(overrides)):
+      info = overrides[subpart_i]
+      # If the test has only 1 subpart, don't number it.
+      subpart_number = subpart_i + 1 if len(overrides) > 1 else None
+      output_test(test_case_name, test_number, test_name, subpart_number, info,
+                  certs, crls, sanitized_test_names, output)
+
+  return i
 
 
 def main():
@@ -164,17 +1104,12 @@
         finalize_test_case(test_case_name, sanitized_test_names, output)
         sanitized_test_names = []
 
-      # TODO(mattm): Handle certificate policies tests.
-      if section_match.group(1) in ('4.8', '4.9', '4.10', '4.11', '4.12'):
-        test_case_name = None
-        output.write('\n// Skipping section %s\n' % section_match.group(1))
-        continue
-
       test_case_name = 'PkitsTest%02d%s' % (
           int(section_match.group(1).split('.')[-1]),
           sanitize_name(section_match.group(2)))
       output.write('\ntemplate <typename PkitsTestDelegate>\n')
-      output.write('class %s : public PkitsTest<PkitsTestDelegate> {};\n' % test_case_name)
+      output.write('class %s : public PkitsTest<PkitsTestDelegate> {};\n' %
+                   test_case_name)
       output.write('TYPED_TEST_CASE_P(%s);\n' % test_case_name)
 
     if match:
@@ -183,10 +1118,8 @@
       if not test_case_name:
         output.write('// Skipped %s %s\n' % (test_number, test_name))
         continue
-      i, sanitized_test_name = parse_test(lines, i, test_case_name, test_number,
-                                         test_name, output)
-      if sanitized_test_name:
-        sanitized_test_names.append(sanitized_test_name)
+      i, parse_test(lines, i, test_case_name, test_number,
+                    test_name, sanitized_test_names, output)
 
   if test_case_name:
     finalize_test_case(test_case_name, sanitized_test_names, output)
diff --git a/net/third_party/nist-pkits/pkits_testcases-inl.h b/net/third_party/nist-pkits/pkits_testcases-inl.h
index 22f3c47d..7ff0864 100644
--- a/net/third_party/nist-pkits/pkits_testcases-inl.h
+++ b/net/third_party/nist-pkits/pkits_testcases-inl.h
@@ -17,7 +17,7 @@
   const char* const certs[] = {"TrustAnchorRootCertificate", "GoodCACert",
                                "ValidCertificatePathTest1EE"};
   const char* const crls[] = {"TrustAnchorRootCRL", "GoodCACRL"};
-  ASSERT_TRUE(this->Verify(certs, crls));
+  ASSERT_TRUE(this->Verify(certs, crls, {}));
 }
 
 // 4.1.2 Invalid CA Signature Test2
@@ -26,7 +26,7 @@
   const char* const certs[] = {"TrustAnchorRootCertificate", "BadSignedCACert",
                                "InvalidCASignatureTest2EE"};
   const char* const crls[] = {"TrustAnchorRootCRL", "BadSignedCACRL"};
-  ASSERT_FALSE(this->Verify(certs, crls));
+  ASSERT_FALSE(this->Verify(certs, crls, {}));
 }
 
 // 4.1.3 Invalid EE Signature Test3
@@ -35,7 +35,7 @@
   const char* const certs[] = {"TrustAnchorRootCertificate", "GoodCACert",
                                "InvalidEESignatureTest3EE"};
   const char* const crls[] = {"TrustAnchorRootCRL", "GoodCACRL"};
-  ASSERT_FALSE(this->Verify(certs, crls));
+  ASSERT_FALSE(this->Verify(certs, crls, {}));
 }
 
 // 4.1.4 Valid DSA Signatures Test4
@@ -44,7 +44,7 @@
   const char* const certs[] = {"TrustAnchorRootCertificate", "DSACACert",
                                "ValidDSASignaturesTest4EE"};
   const char* const crls[] = {"TrustAnchorRootCRL", "DSACACRL"};
-  ASSERT_TRUE(this->Verify(certs, crls));
+  ASSERT_TRUE(this->Verify(certs, crls, {}));
 }
 
 // 4.1.5 Valid DSA Parameter Inheritance Test5
@@ -55,7 +55,7 @@
                                "ValidDSAParameterInheritanceTest5EE"};
   const char* const crls[] = {"TrustAnchorRootCRL", "DSACACRL",
                               "DSAParametersInheritedCACRL"};
-  ASSERT_TRUE(this->Verify(certs, crls));
+  ASSERT_TRUE(this->Verify(certs, crls, {}));
 }
 
 // 4.1.6 Invalid DSA Signature Test6
@@ -64,7 +64,7 @@
   const char* const certs[] = {"TrustAnchorRootCertificate", "DSACACert",
                                "InvalidDSASignatureTest6EE"};
   const char* const crls[] = {"TrustAnchorRootCRL", "DSACACRL"};
-  ASSERT_FALSE(this->Verify(certs, crls));
+  ASSERT_FALSE(this->Verify(certs, crls, {}));
 }
 
 WRAPPED_REGISTER_TYPED_TEST_CASE_P(PkitsTest01SignatureVerification,
@@ -86,7 +86,7 @@
                                "BadnotBeforeDateCACert",
                                "InvalidCAnotBeforeDateTest1EE"};
   const char* const crls[] = {"TrustAnchorRootCRL", "BadnotBeforeDateCACRL"};
-  ASSERT_FALSE(this->Verify(certs, crls));
+  ASSERT_FALSE(this->Verify(certs, crls, {}));
 }
 
 // 4.2.2 Invalid EE notBefore Date Test2
@@ -95,7 +95,7 @@
   const char* const certs[] = {"TrustAnchorRootCertificate", "GoodCACert",
                                "InvalidEEnotBeforeDateTest2EE"};
   const char* const crls[] = {"TrustAnchorRootCRL", "GoodCACRL"};
-  ASSERT_FALSE(this->Verify(certs, crls));
+  ASSERT_FALSE(this->Verify(certs, crls, {}));
 }
 
 // 4.2.3 Valid pre2000 UTC notBefore Date Test3
@@ -104,7 +104,7 @@
   const char* const certs[] = {"TrustAnchorRootCertificate", "GoodCACert",
                                "Validpre2000UTCnotBeforeDateTest3EE"};
   const char* const crls[] = {"TrustAnchorRootCRL", "GoodCACRL"};
-  ASSERT_TRUE(this->Verify(certs, crls));
+  ASSERT_TRUE(this->Verify(certs, crls, {}));
 }
 
 // 4.2.4 Valid GeneralizedTime notBefore Date Test4
@@ -113,7 +113,7 @@
   const char* const certs[] = {"TrustAnchorRootCertificate", "GoodCACert",
                                "ValidGeneralizedTimenotBeforeDateTest4EE"};
   const char* const crls[] = {"TrustAnchorRootCRL", "GoodCACRL"};
-  ASSERT_TRUE(this->Verify(certs, crls));
+  ASSERT_TRUE(this->Verify(certs, crls, {}));
 }
 
 // 4.2.5 Invalid CA notAfter Date Test5
@@ -123,7 +123,7 @@
                                "BadnotAfterDateCACert",
                                "InvalidCAnotAfterDateTest5EE"};
   const char* const crls[] = {"TrustAnchorRootCRL", "BadnotAfterDateCACRL"};
-  ASSERT_FALSE(this->Verify(certs, crls));
+  ASSERT_FALSE(this->Verify(certs, crls, {}));
 }
 
 // 4.2.6 Invalid EE notAfter Date Test6
@@ -132,7 +132,7 @@
   const char* const certs[] = {"TrustAnchorRootCertificate", "GoodCACert",
                                "InvalidEEnotAfterDateTest6EE"};
   const char* const crls[] = {"TrustAnchorRootCRL", "GoodCACRL"};
-  ASSERT_FALSE(this->Verify(certs, crls));
+  ASSERT_FALSE(this->Verify(certs, crls, {}));
 }
 
 // 4.2.7 Invalid pre2000 UTC EE notAfter Date Test7
@@ -141,7 +141,7 @@
   const char* const certs[] = {"TrustAnchorRootCertificate", "GoodCACert",
                                "Invalidpre2000UTCEEnotAfterDateTest7EE"};
   const char* const crls[] = {"TrustAnchorRootCRL", "GoodCACRL"};
-  ASSERT_FALSE(this->Verify(certs, crls));
+  ASSERT_FALSE(this->Verify(certs, crls, {}));
 }
 
 // 4.2.8 Valid GeneralizedTime notAfter Date Test8
@@ -150,7 +150,7 @@
   const char* const certs[] = {"TrustAnchorRootCertificate", "GoodCACert",
                                "ValidGeneralizedTimenotAfterDateTest8EE"};
   const char* const crls[] = {"TrustAnchorRootCRL", "GoodCACRL"};
-  ASSERT_TRUE(this->Verify(certs, crls));
+  ASSERT_TRUE(this->Verify(certs, crls, {}));
 }
 
 WRAPPED_REGISTER_TYPED_TEST_CASE_P(
@@ -174,7 +174,7 @@
   const char* const certs[] = {"TrustAnchorRootCertificate", "GoodCACert",
                                "InvalidNameChainingTest1EE"};
   const char* const crls[] = {"TrustAnchorRootCRL", "GoodCACRL"};
-  ASSERT_FALSE(this->Verify(certs, crls));
+  ASSERT_FALSE(this->Verify(certs, crls, {}));
 }
 
 // 4.3.2 Invalid Name Chaining Order Test2
@@ -184,7 +184,7 @@
                                "NameOrderingCACert",
                                "InvalidNameChainingOrderTest2EE"};
   const char* const crls[] = {"TrustAnchorRootCRL", "NameOrderCACRL"};
-  ASSERT_FALSE(this->Verify(certs, crls));
+  ASSERT_FALSE(this->Verify(certs, crls, {}));
 }
 
 // 4.3.3 Valid Name Chaining Whitespace Test3
@@ -193,7 +193,7 @@
   const char* const certs[] = {"TrustAnchorRootCertificate", "GoodCACert",
                                "ValidNameChainingWhitespaceTest3EE"};
   const char* const crls[] = {"TrustAnchorRootCRL", "GoodCACRL"};
-  ASSERT_TRUE(this->Verify(certs, crls));
+  ASSERT_TRUE(this->Verify(certs, crls, {}));
 }
 
 // 4.3.4 Valid Name Chaining Whitespace Test4
@@ -202,7 +202,7 @@
   const char* const certs[] = {"TrustAnchorRootCertificate", "GoodCACert",
                                "ValidNameChainingWhitespaceTest4EE"};
   const char* const crls[] = {"TrustAnchorRootCRL", "GoodCACRL"};
-  ASSERT_TRUE(this->Verify(certs, crls));
+  ASSERT_TRUE(this->Verify(certs, crls, {}));
 }
 
 // 4.3.5 Valid Name Chaining Capitalization Test5
@@ -211,7 +211,7 @@
   const char* const certs[] = {"TrustAnchorRootCertificate", "GoodCACert",
                                "ValidNameChainingCapitalizationTest5EE"};
   const char* const crls[] = {"TrustAnchorRootCRL", "GoodCACRL"};
-  ASSERT_TRUE(this->Verify(certs, crls));
+  ASSERT_TRUE(this->Verify(certs, crls, {}));
 }
 
 // 4.3.6 Valid Name Chaining UIDs Test6
@@ -220,7 +220,7 @@
   const char* const certs[] = {"TrustAnchorRootCertificate", "UIDCACert",
                                "ValidNameUIDsTest6EE"};
   const char* const crls[] = {"TrustAnchorRootCRL", "UIDCACRL"};
-  ASSERT_TRUE(this->Verify(certs, crls));
+  ASSERT_TRUE(this->Verify(certs, crls, {}));
 }
 
 // 4.3.7 Valid RFC3280 Mandatory Attribute Types Test7
@@ -231,7 +231,7 @@
                                "ValidRFC3280MandatoryAttributeTypesTest7EE"};
   const char* const crls[] = {"TrustAnchorRootCRL",
                               "RFC3280MandatoryAttributeTypesCACRL"};
-  ASSERT_TRUE(this->Verify(certs, crls));
+  ASSERT_TRUE(this->Verify(certs, crls, {}));
 }
 
 // 4.3.8 Valid RFC3280 Optional Attribute Types Test8
@@ -242,7 +242,7 @@
                                "ValidRFC3280OptionalAttributeTypesTest8EE"};
   const char* const crls[] = {"TrustAnchorRootCRL",
                               "RFC3280OptionalAttributeTypesCACRL"};
-  ASSERT_TRUE(this->Verify(certs, crls));
+  ASSERT_TRUE(this->Verify(certs, crls, {}));
 }
 
 // 4.3.9 Valid UTF8String Encoded Names Test9
@@ -253,7 +253,7 @@
                                "ValidUTF8StringEncodedNamesTest9EE"};
   const char* const crls[] = {"TrustAnchorRootCRL",
                               "UTF8StringEncodedNamesCACRL"};
-  ASSERT_TRUE(this->Verify(certs, crls));
+  ASSERT_TRUE(this->Verify(certs, crls, {}));
 }
 
 // 4.3.10 Valid Rollover from PrintableString to UTF8String Test10
@@ -266,7 +266,7 @@
       "ValidRolloverfromPrintableStringtoUTF8StringTest10EE"};
   const char* const crls[] = {"TrustAnchorRootCRL",
                               "RolloverfromPrintableStringtoUTF8StringCACRL"};
-  ASSERT_TRUE(this->Verify(certs, crls));
+  ASSERT_TRUE(this->Verify(certs, crls, {}));
 }
 
 // 4.3.11 Valid UTF8String Case Insensitive Match Test11
@@ -277,7 +277,7 @@
                                "ValidUTF8StringCaseInsensitiveMatchTest11EE"};
   const char* const crls[] = {"TrustAnchorRootCRL",
                               "UTF8StringCaseInsensitiveMatchCACRL"};
-  ASSERT_TRUE(this->Verify(certs, crls));
+  ASSERT_TRUE(this->Verify(certs, crls, {}));
 }
 
 WRAPPED_REGISTER_TYPED_TEST_CASE_P(
@@ -305,7 +305,7 @@
   const char* const certs[] = {"TrustAnchorRootCertificate", "NoCRLCACert",
                                "InvalidMissingCRLTest1EE"};
   const char* const crls[] = {"TrustAnchorRootCRL"};
-  ASSERT_FALSE(this->Verify(certs, crls));
+  ASSERT_FALSE(this->Verify(certs, crls, {}));
 }
 
 // 4.4.2 Invalid Revoked CA Test2
@@ -315,7 +315,7 @@
                                "RevokedsubCACert", "InvalidRevokedCATest2EE"};
   const char* const crls[] = {"TrustAnchorRootCRL", "GoodCACRL",
                               "RevokedsubCACRL"};
-  ASSERT_FALSE(this->Verify(certs, crls));
+  ASSERT_FALSE(this->Verify(certs, crls, {}));
 }
 
 // 4.4.3 Invalid Revoked EE Test3
@@ -324,7 +324,7 @@
   const char* const certs[] = {"TrustAnchorRootCertificate", "GoodCACert",
                                "InvalidRevokedEETest3EE"};
   const char* const crls[] = {"TrustAnchorRootCRL", "GoodCACRL"};
-  ASSERT_FALSE(this->Verify(certs, crls));
+  ASSERT_FALSE(this->Verify(certs, crls, {}));
 }
 
 // 4.4.4 Invalid Bad CRL Signature Test4
@@ -334,7 +334,7 @@
                                "BadCRLSignatureCACert",
                                "InvalidBadCRLSignatureTest4EE"};
   const char* const crls[] = {"TrustAnchorRootCRL", "BadCRLSignatureCACRL"};
-  ASSERT_FALSE(this->Verify(certs, crls));
+  ASSERT_FALSE(this->Verify(certs, crls, {}));
 }
 
 // 4.4.5 Invalid Bad CRL Issuer Name Test5
@@ -344,7 +344,7 @@
                                "BadCRLIssuerNameCACert",
                                "InvalidBadCRLIssuerNameTest5EE"};
   const char* const crls[] = {"TrustAnchorRootCRL", "BadCRLIssuerNameCACRL"};
-  ASSERT_FALSE(this->Verify(certs, crls));
+  ASSERT_FALSE(this->Verify(certs, crls, {}));
 }
 
 // 4.4.6 Invalid Wrong CRL Test6
@@ -353,7 +353,7 @@
   const char* const certs[] = {"TrustAnchorRootCertificate", "WrongCRLCACert",
                                "InvalidWrongCRLTest6EE"};
   const char* const crls[] = {"TrustAnchorRootCRL", "WrongCRLCACRL"};
-  ASSERT_FALSE(this->Verify(certs, crls));
+  ASSERT_FALSE(this->Verify(certs, crls, {}));
 }
 
 // 4.4.7 Valid Two CRLs Test7
@@ -363,7 +363,7 @@
                                "ValidTwoCRLsTest7EE"};
   const char* const crls[] = {"TrustAnchorRootCRL", "TwoCRLsCAGoodCRL",
                               "TwoCRLsCABadCRL"};
-  ASSERT_TRUE(this->Verify(certs, crls));
+  ASSERT_TRUE(this->Verify(certs, crls, {}));
 }
 
 // 4.4.8 Invalid Unknown CRL Entry Extension Test8
@@ -374,7 +374,7 @@
                                "InvalidUnknownCRLEntryExtensionTest8EE"};
   const char* const crls[] = {"TrustAnchorRootCRL",
                               "UnknownCRLEntryExtensionCACRL"};
-  ASSERT_FALSE(this->Verify(certs, crls));
+  ASSERT_FALSE(this->Verify(certs, crls, {}));
 }
 
 // 4.4.9 Invalid Unknown CRL Extension Test9
@@ -384,7 +384,7 @@
                                "UnknownCRLExtensionCACert",
                                "InvalidUnknownCRLExtensionTest9EE"};
   const char* const crls[] = {"TrustAnchorRootCRL", "UnknownCRLExtensionCACRL"};
-  ASSERT_FALSE(this->Verify(certs, crls));
+  ASSERT_FALSE(this->Verify(certs, crls, {}));
 }
 
 // 4.4.10 Invalid Unknown CRL Extension Test10
@@ -394,7 +394,7 @@
                                "UnknownCRLExtensionCACert",
                                "InvalidUnknownCRLExtensionTest10EE"};
   const char* const crls[] = {"TrustAnchorRootCRL", "UnknownCRLExtensionCACRL"};
-  ASSERT_FALSE(this->Verify(certs, crls));
+  ASSERT_FALSE(this->Verify(certs, crls, {}));
 }
 
 // 4.4.11 Invalid Old CRL nextUpdate Test11
@@ -404,7 +404,7 @@
                                "OldCRLnextUpdateCACert",
                                "InvalidOldCRLnextUpdateTest11EE"};
   const char* const crls[] = {"TrustAnchorRootCRL", "OldCRLnextUpdateCACRL"};
-  ASSERT_FALSE(this->Verify(certs, crls));
+  ASSERT_FALSE(this->Verify(certs, crls, {}));
 }
 
 // 4.4.12 Invalid pre2000 CRL nextUpdate Test12
@@ -416,7 +416,7 @@
                                "uctiontoSection4.4formoreinformation."};
   const char* const crls[] = {"TrustAnchorRootCRL",
                               "pre2000CRLnextUpdateCACRL"};
-  ASSERT_FALSE(this->Verify(certs, crls));
+  ASSERT_FALSE(this->Verify(certs, crls, {}));
 }
 
 // 4.4.13 Valid GeneralizedTime CRL nextUpdate Test13
@@ -427,7 +427,7 @@
                                "ValidGeneralizedTimeCRLnextUpdateTest13EE"};
   const char* const crls[] = {"TrustAnchorRootCRL",
                               "GeneralizedTimeCRLnextUpdateCACRL"};
-  ASSERT_TRUE(this->Verify(certs, crls));
+  ASSERT_TRUE(this->Verify(certs, crls, {}));
 }
 
 // 4.4.14 Valid Negative Serial Number Test14
@@ -438,7 +438,7 @@
                                "ValidNegativeSerialNumberTest14EE"};
   const char* const crls[] = {"TrustAnchorRootCRL",
                               "NegativeSerialNumberCACRL"};
-  ASSERT_TRUE(this->Verify(certs, crls));
+  ASSERT_TRUE(this->Verify(certs, crls, {}));
 }
 
 // 4.4.15 Invalid Negative Serial Number Test15
@@ -449,7 +449,7 @@
                                "InvalidNegativeSerialNumberTest15EE"};
   const char* const crls[] = {"TrustAnchorRootCRL",
                               "NegativeSerialNumberCACRL"};
-  ASSERT_FALSE(this->Verify(certs, crls));
+  ASSERT_FALSE(this->Verify(certs, crls, {}));
 }
 
 // 4.4.16 Valid Long Serial Number Test16
@@ -459,7 +459,7 @@
                                "LongSerialNumberCACert",
                                "ValidLongSerialNumberTest16EE"};
   const char* const crls[] = {"TrustAnchorRootCRL", "LongSerialNumberCACRL"};
-  ASSERT_TRUE(this->Verify(certs, crls));
+  ASSERT_TRUE(this->Verify(certs, crls, {}));
 }
 
 // 4.4.17 Valid Long Serial Number Test17
@@ -469,7 +469,7 @@
                                "LongSerialNumberCACert",
                                "ValidLongSerialNumberTest17EE"};
   const char* const crls[] = {"TrustAnchorRootCRL", "LongSerialNumberCACRL"};
-  ASSERT_TRUE(this->Verify(certs, crls));
+  ASSERT_TRUE(this->Verify(certs, crls, {}));
 }
 
 // 4.4.18 Invalid Long Serial Number Test18
@@ -479,7 +479,7 @@
                                "LongSerialNumberCACert",
                                "InvalidLongSerialNumberTest18EE"};
   const char* const crls[] = {"TrustAnchorRootCRL", "LongSerialNumberCACRL"};
-  ASSERT_FALSE(this->Verify(certs, crls));
+  ASSERT_FALSE(this->Verify(certs, crls, {}));
 }
 
 // 4.4.19 Valid Separate Certificate and CRL Keys Test19
@@ -492,7 +492,7 @@
       "ValidSeparateCertificateandCRLKeysTest19EE"};
   const char* const crls[] = {"TrustAnchorRootCRL",
                               "SeparateCertificateandCRLKeysCRL"};
-  ASSERT_TRUE(this->Verify(certs, crls));
+  ASSERT_TRUE(this->Verify(certs, crls, {}));
 }
 
 // 4.4.20 Invalid Separate Certificate and CRL Keys Test20
@@ -505,7 +505,7 @@
       "InvalidSeparateCertificateandCRLKeysTest20EE"};
   const char* const crls[] = {"TrustAnchorRootCRL",
                               "SeparateCertificateandCRLKeysCRL"};
-  ASSERT_FALSE(this->Verify(certs, crls));
+  ASSERT_FALSE(this->Verify(certs, crls, {}));
 }
 
 // 4.4.21 Invalid Separate Certificate and CRL Keys Test21
@@ -518,7 +518,7 @@
       "InvalidSeparateCertificateandCRLKeysTest21EE"};
   const char* const crls[] = {"TrustAnchorRootCRL",
                               "SeparateCertificateandCRLKeysCA2CRL"};
-  ASSERT_FALSE(this->Verify(certs, crls));
+  ASSERT_FALSE(this->Verify(certs, crls, {}));
 }
 
 WRAPPED_REGISTER_TYPED_TEST_CASE_P(
@@ -559,7 +559,7 @@
                                "ValidBasicSelfIssuedOldWithNewTest1EE"};
   const char* const crls[] = {"TrustAnchorRootCRL",
                               "BasicSelfIssuedNewKeyCACRL"};
-  ASSERT_TRUE(this->Verify(certs, crls));
+  ASSERT_TRUE(this->Verify(certs, crls, {}));
 }
 
 // 4.5.2 Invalid Basic Self-Issued Old With New Test2
@@ -571,7 +571,7 @@
                                "InvalidBasicSelfIssuedOldWithNewTest2EE"};
   const char* const crls[] = {"TrustAnchorRootCRL",
                               "BasicSelfIssuedNewKeyCACRL"};
-  ASSERT_FALSE(this->Verify(certs, crls));
+  ASSERT_FALSE(this->Verify(certs, crls, {}));
 }
 
 // 4.5.3 Valid Basic Self-Issued New With Old Test3
@@ -584,7 +584,7 @@
   const char* const crls[] = {"TrustAnchorRootCRL",
                               "BasicSelfIssuedOldKeySelfIssuedCertCRL",
                               "BasicSelfIssuedOldKeyCACRL"};
-  ASSERT_TRUE(this->Verify(certs, crls));
+  ASSERT_TRUE(this->Verify(certs, crls, {}));
 }
 
 // 4.5.4 Valid Basic Self-Issued New With Old Test4
@@ -597,7 +597,7 @@
   const char* const crls[] = {"TrustAnchorRootCRL",
                               "BasicSelfIssuedOldKeySelfIssuedCertCRL",
                               "BasicSelfIssuedOldKeyCACRL"};
-  ASSERT_TRUE(this->Verify(certs, crls));
+  ASSERT_TRUE(this->Verify(certs, crls, {}));
 }
 
 // 4.5.5 Invalid Basic Self-Issued New With Old Test5
@@ -610,7 +610,7 @@
   const char* const crls[] = {"TrustAnchorRootCRL",
                               "BasicSelfIssuedOldKeySelfIssuedCertCRL",
                               "BasicSelfIssuedOldKeyCACRL"};
-  ASSERT_FALSE(this->Verify(certs, crls));
+  ASSERT_FALSE(this->Verify(certs, crls, {}));
 }
 
 // 4.5.6 Valid Basic Self-Issued CRL Signing Key Test6
@@ -623,7 +623,7 @@
   const char* const crls[] = {"TrustAnchorRootCRL",
                               "BasicSelfIssuedCRLSigningKeyCRLCertCRL",
                               "BasicSelfIssuedCRLSigningKeyCACRL"};
-  ASSERT_TRUE(this->Verify(certs, crls));
+  ASSERT_TRUE(this->Verify(certs, crls, {}));
 }
 
 // 4.5.7 Invalid Basic Self-Issued CRL Signing Key Test7
@@ -636,7 +636,7 @@
   const char* const crls[] = {"TrustAnchorRootCRL",
                               "BasicSelfIssuedCRLSigningKeyCRLCertCRL",
                               "BasicSelfIssuedCRLSigningKeyCACRL"};
-  ASSERT_FALSE(this->Verify(certs, crls));
+  ASSERT_FALSE(this->Verify(certs, crls, {}));
 }
 
 // 4.5.8 Invalid Basic Self-Issued CRL Signing Key Test8
@@ -649,7 +649,7 @@
   const char* const crls[] = {"TrustAnchorRootCRL",
                               "BasicSelfIssuedCRLSigningKeyCRLCertCRL",
                               "BasicSelfIssuedCRLSigningKeyCACRL"};
-  ASSERT_FALSE(this->Verify(certs, crls));
+  ASSERT_FALSE(this->Verify(certs, crls, {}));
 }
 
 WRAPPED_REGISTER_TYPED_TEST_CASE_P(
@@ -676,7 +676,7 @@
                                "InvalidMissingbasicConstraintsTest1EE"};
   const char* const crls[] = {"TrustAnchorRootCRL",
                               "MissingbasicConstraintsCACRL"};
-  ASSERT_FALSE(this->Verify(certs, crls));
+  ASSERT_FALSE(this->Verify(certs, crls, {}));
 }
 
 // 4.6.2 Invalid cA False Test2
@@ -687,7 +687,7 @@
                                "InvalidcAFalseTest2EE"};
   const char* const crls[] = {"TrustAnchorRootCRL",
                               "basicConstraintsCriticalcAFalseCACRL"};
-  ASSERT_FALSE(this->Verify(certs, crls));
+  ASSERT_FALSE(this->Verify(certs, crls, {}));
 }
 
 // 4.6.3 Invalid cA False Test3
@@ -698,7 +698,7 @@
                                "InvalidcAFalseTest3EE"};
   const char* const crls[] = {"TrustAnchorRootCRL",
                               "basicConstraintsNotCriticalcAFalseCACRL"};
-  ASSERT_FALSE(this->Verify(certs, crls));
+  ASSERT_FALSE(this->Verify(certs, crls, {}));
 }
 
 // 4.6.4 Valid basicConstraints Not Critical Test4
@@ -709,7 +709,7 @@
                                "ValidbasicConstraintsNotCriticalTest4EE"};
   const char* const crls[] = {"TrustAnchorRootCRL",
                               "basicConstraintsNotCriticalCACRL"};
-  ASSERT_TRUE(this->Verify(certs, crls));
+  ASSERT_TRUE(this->Verify(certs, crls, {}));
 }
 
 // 4.6.5 Invalid pathLenConstraint Test5
@@ -720,7 +720,7 @@
       "pathLenConstraint0subCACert", "InvalidpathLenConstraintTest5EE"};
   const char* const crls[] = {"TrustAnchorRootCRL", "pathLenConstraint0CACRL",
                               "pathLenConstraint0subCACRL"};
-  ASSERT_FALSE(this->Verify(certs, crls));
+  ASSERT_FALSE(this->Verify(certs, crls, {}));
 }
 
 // 4.6.6 Invalid pathLenConstraint Test6
@@ -731,7 +731,7 @@
       "pathLenConstraint0subCACert", "InvalidpathLenConstraintTest6EE"};
   const char* const crls[] = {"TrustAnchorRootCRL", "pathLenConstraint0CACRL",
                               "pathLenConstraint0subCACRL"};
-  ASSERT_FALSE(this->Verify(certs, crls));
+  ASSERT_FALSE(this->Verify(certs, crls, {}));
 }
 
 // 4.6.7 Valid pathLenConstraint Test7
@@ -741,7 +741,7 @@
                                "pathLenConstraint0CACert",
                                "ValidpathLenConstraintTest7EE"};
   const char* const crls[] = {"TrustAnchorRootCRL", "pathLenConstraint0CACRL"};
-  ASSERT_TRUE(this->Verify(certs, crls));
+  ASSERT_TRUE(this->Verify(certs, crls, {}));
 }
 
 // 4.6.8 Valid pathLenConstraint Test8
@@ -751,7 +751,7 @@
                                "pathLenConstraint0CACert",
                                "ValidpathLenConstraintTest8EE"};
   const char* const crls[] = {"TrustAnchorRootCRL", "pathLenConstraint0CACRL"};
-  ASSERT_TRUE(this->Verify(certs, crls));
+  ASSERT_TRUE(this->Verify(certs, crls, {}));
 }
 
 // 4.6.9 Invalid pathLenConstraint Test9
@@ -764,7 +764,7 @@
   const char* const crls[] = {"TrustAnchorRootCRL", "pathLenConstraint6CACRL",
                               "pathLenConstraint6subCA0CRL",
                               "pathLenConstraint6subsubCA00CRL"};
-  ASSERT_FALSE(this->Verify(certs, crls));
+  ASSERT_FALSE(this->Verify(certs, crls, {}));
 }
 
 // 4.6.10 Invalid pathLenConstraint Test10
@@ -777,7 +777,7 @@
   const char* const crls[] = {"TrustAnchorRootCRL", "pathLenConstraint6CACRL",
                               "pathLenConstraint6subCA0CRL",
                               "pathLenConstraint6subsubCA00CRL"};
-  ASSERT_FALSE(this->Verify(certs, crls));
+  ASSERT_FALSE(this->Verify(certs, crls, {}));
 }
 
 // 4.6.11 Invalid pathLenConstraint Test11
@@ -793,7 +793,7 @@
                               "pathLenConstraint6subCA1CRL",
                               "pathLenConstraint6subsubCA11CRL",
                               "pathLenConstraint6subsubsubCA11XCRL"};
-  ASSERT_FALSE(this->Verify(certs, crls));
+  ASSERT_FALSE(this->Verify(certs, crls, {}));
 }
 
 // 4.6.12 Invalid pathLenConstraint Test12
@@ -809,7 +809,7 @@
                               "pathLenConstraint6subCA1CRL",
                               "pathLenConstraint6subsubCA11CRL",
                               "pathLenConstraint6subsubsubCA11XCRL"};
-  ASSERT_FALSE(this->Verify(certs, crls));
+  ASSERT_FALSE(this->Verify(certs, crls, {}));
 }
 
 // 4.6.13 Valid pathLenConstraint Test13
@@ -825,7 +825,7 @@
                               "pathLenConstraint6subCA4CRL",
                               "pathLenConstraint6subsubCA41CRL",
                               "pathLenConstraint6subsubsubCA41XCRL"};
-  ASSERT_TRUE(this->Verify(certs, crls));
+  ASSERT_TRUE(this->Verify(certs, crls, {}));
 }
 
 // 4.6.14 Valid pathLenConstraint Test14
@@ -841,7 +841,7 @@
                               "pathLenConstraint6subCA4CRL",
                               "pathLenConstraint6subsubCA41CRL",
                               "pathLenConstraint6subsubsubCA41XCRL"};
-  ASSERT_TRUE(this->Verify(certs, crls));
+  ASSERT_TRUE(this->Verify(certs, crls, {}));
 }
 
 // 4.6.15 Valid Self-Issued pathLenConstraint Test15
@@ -852,7 +852,7 @@
                                "pathLenConstraint0SelfIssuedCACert",
                                "ValidSelfIssuedpathLenConstraintTest15EE"};
   const char* const crls[] = {"TrustAnchorRootCRL", "pathLenConstraint0CACRL"};
-  ASSERT_TRUE(this->Verify(certs, crls));
+  ASSERT_TRUE(this->Verify(certs, crls, {}));
 }
 
 // 4.6.16 Invalid Self-Issued pathLenConstraint Test16
@@ -864,7 +864,7 @@
       "InvalidSelfIssuedpathLenConstraintTest16EE"};
   const char* const crls[] = {"TrustAnchorRootCRL", "pathLenConstraint0CACRL",
                               "pathLenConstraint0subCA2CRL"};
-  ASSERT_FALSE(this->Verify(certs, crls));
+  ASSERT_FALSE(this->Verify(certs, crls, {}));
 }
 
 // 4.6.17 Valid Self-Issued pathLenConstraint Test17
@@ -878,7 +878,7 @@
                                "ValidSelfIssuedpathLenConstraintTest17EE"};
   const char* const crls[] = {"TrustAnchorRootCRL", "pathLenConstraint1CACRL",
                               "pathLenConstraint1subCACRL"};
-  ASSERT_TRUE(this->Verify(certs, crls));
+  ASSERT_TRUE(this->Verify(certs, crls, {}));
 }
 
 WRAPPED_REGISTER_TYPED_TEST_CASE_P(
@@ -913,7 +913,7 @@
       "InvalidkeyUsageCriticalkeyCertSignFalseTest1EE"};
   const char* const crls[] = {"TrustAnchorRootCRL",
                               "keyUsageCriticalkeyCertSignFalseCACRL"};
-  ASSERT_FALSE(this->Verify(certs, crls));
+  ASSERT_FALSE(this->Verify(certs, crls, {}));
 }
 
 // 4.7.2 Invalid keyUsage Not Critical keyCertSign False Test2
@@ -924,7 +924,7 @@
       "InvalidkeyUsageNotCriticalkeyCertSignFalseTest2EE"};
   const char* const crls[] = {"TrustAnchorRootCRL",
                               "keyUsageNotCriticalkeyCertSignFalseCACRL"};
-  ASSERT_FALSE(this->Verify(certs, crls));
+  ASSERT_FALSE(this->Verify(certs, crls, {}));
 }
 
 // 4.7.3 Valid keyUsage Not Critical Test3
@@ -934,7 +934,7 @@
                                "keyUsageNotCriticalCACert",
                                "ValidkeyUsageNotCriticalTest3EE"};
   const char* const crls[] = {"TrustAnchorRootCRL", "keyUsageNotCriticalCACRL"};
-  ASSERT_TRUE(this->Verify(certs, crls));
+  ASSERT_TRUE(this->Verify(certs, crls, {}));
 }
 
 // 4.7.4 Invalid keyUsage Critical cRLSign False Test4
@@ -945,7 +945,7 @@
                                "InvalidkeyUsageCriticalcRLSignFalseTest4EE"};
   const char* const crls[] = {"TrustAnchorRootCRL",
                               "keyUsageCriticalcRLSignFalseCACRL"};
-  ASSERT_FALSE(this->Verify(certs, crls));
+  ASSERT_FALSE(this->Verify(certs, crls, {}));
 }
 
 // 4.7.5 Invalid keyUsage Not Critical cRLSign False Test5
@@ -956,7 +956,7 @@
                                "InvalidkeyUsageNotCriticalcRLSignFalseTest5EE"};
   const char* const crls[] = {"TrustAnchorRootCRL",
                               "keyUsageNotCriticalcRLSignFalseCACRL"};
-  ASSERT_FALSE(this->Verify(certs, crls));
+  ASSERT_FALSE(this->Verify(certs, crls, {}));
 }
 
 WRAPPED_REGISTER_TYPED_TEST_CASE_P(
@@ -967,78 +967,1291 @@
     Section7InvalidkeyUsageCriticalcRLSignFalseTest4,
     Section7InvalidkeyUsageNotCriticalcRLSignFalseTest5);
 
-// Skipping section 4.8
-// Skipped 4.8.1 All Certificates Same Policy Test1
-// Skipped 4.8.2 All Certificates No Policies Test2
-// Skipped 4.8.3 Different Policies Test3
-// Skipped 4.8.4 Different Policies Test4
-// Skipped 4.8.5 Different Policies Test5
-// Skipped 4.8.6 Overlapping Policies Test6
-// Skipped 4.8.7 Different Policies Test7
-// Skipped 4.8.8 Different Policies Test8
-// Skipped 4.8.9 Different Policies Test9
-// Skipped 4.8.10 All Certificates Same Policies Test10
-// Skipped 4.8.11 All Certificates AnyPolicy Test11
-// Skipped 4.8.12 Different Policies Test12
-// Skipped 4.8.13 All Certificates Same Policies Test13
-// Skipped 4.8.14 AnyPolicy Test14
-// Skipped 4.8.15 User Notice Qualifier Test15
-// Skipped 4.8.16 User Notice Qualifier Test16
-// Skipped 4.8.17 User Notice Qualifier Test17
-// Skipped 4.8.18 User Notice Qualifier Test18
-// Skipped 4.8.19 User Notice Qualifier Test19
-// Skipped 4.8.20 CPS Pointer Qualifier Test20
+template <typename PkitsTestDelegate>
+class PkitsTest08CertificatePolicies : public PkitsTest<PkitsTestDelegate> {};
+TYPED_TEST_CASE_P(PkitsTest08CertificatePolicies);
 
-// Skipping section 4.9
-// Skipped 4.9.1 Valid RequireExplicitPolicy Test1
-// Skipped 4.9.2 Valid RequireExplicitPolicy Test2
-// Skipped 4.9.3 Invalid RequireExplicitPolicy Test3
-// Skipped 4.9.4 Valid RequireExplicitPolicy Test4
-// Skipped 4.9.5 Invalid RequireExplicitPolicy Test5
-// Skipped 4.9.6 Valid Self-Issued requireExplicitPolicy Test6
-// Skipped 4.9.7 Invalid Self-Issued requireExplicitPolicy Test7
-// Skipped 4.9.8 Invalid Self-Issued requireExplicitPolicy Test8
+// 4.8.1 All Certificates Same Policy Test1
+WRAPPED_TYPED_TEST_P(PkitsTest08CertificatePolicies,
+                     Section8AllCertificatesSamePolicyTest1Subpart1) {
+  const char* const certs[] = {"TrustAnchorRootCertificate", "GoodCACert",
+                               "ValidCertificatePathTest1EE"};
+  const char* const crls[] = {"TrustAnchorRootCRL", "GoodCACRL"};
 
-// Skipping section 4.10
-// Skipped 4.10.1 Valid Policy Mapping Test1
-// Skipped 4.10.2 Invalid Policy Mapping Test2
-// Skipped 4.10.3 Valid Policy Mapping Test3
-// Skipped 4.10.4 Invalid Policy Mapping Test4
-// Skipped 4.10.5 Valid Policy Mapping Test5
-// Skipped 4.10.6 Valid Policy Mapping Test6
-// Skipped 4.10.7 Invalid Mapping From anyPolicy Test7
-// Skipped 4.10.8 Invalid Mapping To anyPolicy Test8
-// Skipped 4.10.9 Valid Policy Mapping Test9
-// Skipped 4.10.10 Invalid Policy Mapping Test10
-// Skipped 4.10.11 Valid Policy Mapping Test11
-// Skipped 4.10.12 Valid Policy Mapping Test12
-// Skipped 4.10.13 Valid Policy Mapping Test13
-// Skipped 4.10.14 Valid Policy Mapping Test14
+  // Custom settings
+  PkitsTestSettings settings;
+  settings.initial_explicit_policy = true;
 
-// Skipping section 4.11
-// Skipped 4.11.1 Invalid inhibitPolicyMapping Test1
-// Skipped 4.11.2 Valid inhibitPolicyMapping Test2
-// Skipped 4.11.3 Invalid inhibitPolicyMapping Test3
-// Skipped 4.11.4 Valid inhibitPolicyMapping Test4
-// Skipped 4.11.5 Invalid inhibitPolicyMapping Test5
-// Skipped 4.11.6 Invalid inhibitPolicyMapping Test6
-// Skipped 4.11.7 Valid Self-Issued inhibitPolicyMapping Test7
-// Skipped 4.11.8 Invalid Self-Issued inhibitPolicyMapping Test8
-// Skipped 4.11.9 Invalid Self-Issued inhibitPolicyMapping Test9
-// Skipped 4.11.10 Invalid Self-Issued inhibitPolicyMapping Test10
-// Skipped 4.11.11 Invalid Self-Issued inhibitPolicyMapping Test11
+  ASSERT_TRUE(this->Verify(certs, crls, settings));
+}
 
-// Skipping section 4.12
-// Skipped 4.12.1 Invalid inhibitAnyPolicy Test1
-// Skipped 4.12.2 Valid inhibitAnyPolicy Test2
-// Skipped 4.12.3 inhibitAnyPolicy Test3
-// Skipped 4.12.4 Invalid inhibitAnyPolicy Test4
-// Skipped 4.12.5 Invalid inhibitAnyPolicy Test5
-// Skipped 4.12.6 Invalid inhibitAnyPolicy Test6
-// Skipped 4.12.7 Valid Self-Issued inhibitAnyPolicy Test7
-// Skipped 4.12.8 Invalid Self-Issued inhibitAnyPolicy Test8
-// Skipped 4.12.9 Valid Self-Issued inhibitAnyPolicy Test9
-// Skipped 4.12.10 Invalid Self-Issued inhibitAnyPolicy Test10
+// 4.8.1 All Certificates Same Policy Test1
+WRAPPED_TYPED_TEST_P(PkitsTest08CertificatePolicies,
+                     Section8AllCertificatesSamePolicyTest1Subpart2) {
+  const char* const certs[] = {"TrustAnchorRootCertificate", "GoodCACert",
+                               "ValidCertificatePathTest1EE"};
+  const char* const crls[] = {"TrustAnchorRootCRL", "GoodCACRL"};
+
+  // Custom settings
+  PkitsTestSettings settings;
+  settings.SetInitialPolicySet("NIST-test-policy-1");
+  settings.initial_explicit_policy = true;
+
+  ASSERT_TRUE(this->Verify(certs, crls, settings));
+}
+
+// 4.8.1 All Certificates Same Policy Test1
+WRAPPED_TYPED_TEST_P(PkitsTest08CertificatePolicies,
+                     Section8AllCertificatesSamePolicyTest1Subpart3) {
+  const char* const certs[] = {"TrustAnchorRootCertificate", "GoodCACert",
+                               "ValidCertificatePathTest1EE"};
+  const char* const crls[] = {"TrustAnchorRootCRL", "GoodCACRL"};
+
+  // Custom settings
+  PkitsTestSettings settings;
+  settings.SetInitialPolicySet("NIST-test-policy-2");
+  settings.initial_explicit_policy = true;
+
+  ASSERT_FALSE(this->Verify(certs, crls, settings));
+}
+
+// 4.8.1 All Certificates Same Policy Test1
+WRAPPED_TYPED_TEST_P(PkitsTest08CertificatePolicies,
+                     Section8AllCertificatesSamePolicyTest1Subpart4) {
+  const char* const certs[] = {"TrustAnchorRootCertificate", "GoodCACert",
+                               "ValidCertificatePathTest1EE"};
+  const char* const crls[] = {"TrustAnchorRootCRL", "GoodCACRL"};
+
+  // Custom settings
+  PkitsTestSettings settings;
+  settings.SetInitialPolicySet("NIST-test-policy-1,NIST-test-policy-2");
+  settings.initial_explicit_policy = true;
+
+  ASSERT_TRUE(this->Verify(certs, crls, settings));
+}
+
+// 4.8.2 All Certificates No Policies Test2
+WRAPPED_TYPED_TEST_P(PkitsTest08CertificatePolicies,
+                     Section8AllCertificatesNoPoliciesTest2Subpart1) {
+  const char* const certs[] = {"TrustAnchorRootCertificate", "NoPoliciesCACert",
+                               "AllCertificatesNoPoliciesTest2EE"};
+  const char* const crls[] = {"TrustAnchorRootCRL", "NoPoliciesCACRL"};
+  ASSERT_TRUE(this->Verify(certs, crls, {}));
+}
+
+// 4.8.2 All Certificates No Policies Test2
+WRAPPED_TYPED_TEST_P(PkitsTest08CertificatePolicies,
+                     Section8AllCertificatesNoPoliciesTest2Subpart2) {
+  const char* const certs[] = {"TrustAnchorRootCertificate", "NoPoliciesCACert",
+                               "AllCertificatesNoPoliciesTest2EE"};
+  const char* const crls[] = {"TrustAnchorRootCRL", "NoPoliciesCACRL"};
+
+  // Custom settings
+  PkitsTestSettings settings;
+  settings.initial_explicit_policy = true;
+
+  ASSERT_FALSE(this->Verify(certs, crls, settings));
+}
+
+// 4.8.3 Different Policies Test3
+WRAPPED_TYPED_TEST_P(PkitsTest08CertificatePolicies,
+                     Section8DifferentPoliciesTest3Subpart1) {
+  const char* const certs[] = {"TrustAnchorRootCertificate", "GoodCACert",
+                               "PoliciesP2subCACert",
+                               "DifferentPoliciesTest3EE"};
+  const char* const crls[] = {"TrustAnchorRootCRL", "GoodCACRL",
+                              "PoliciesP2subCACRL"};
+  ASSERT_TRUE(this->Verify(certs, crls, {}));
+}
+
+// 4.8.3 Different Policies Test3
+WRAPPED_TYPED_TEST_P(PkitsTest08CertificatePolicies,
+                     Section8DifferentPoliciesTest3Subpart2) {
+  const char* const certs[] = {"TrustAnchorRootCertificate", "GoodCACert",
+                               "PoliciesP2subCACert",
+                               "DifferentPoliciesTest3EE"};
+  const char* const crls[] = {"TrustAnchorRootCRL", "GoodCACRL",
+                              "PoliciesP2subCACRL"};
+
+  // Custom settings
+  PkitsTestSettings settings;
+  settings.initial_explicit_policy = true;
+
+  ASSERT_FALSE(this->Verify(certs, crls, settings));
+}
+
+// 4.8.3 Different Policies Test3
+WRAPPED_TYPED_TEST_P(PkitsTest08CertificatePolicies,
+                     Section8DifferentPoliciesTest3Subpart3) {
+  const char* const certs[] = {"TrustAnchorRootCertificate", "GoodCACert",
+                               "PoliciesP2subCACert",
+                               "DifferentPoliciesTest3EE"};
+  const char* const crls[] = {"TrustAnchorRootCRL", "GoodCACRL",
+                              "PoliciesP2subCACRL"};
+
+  // Custom settings
+  PkitsTestSettings settings;
+  settings.SetInitialPolicySet("NIST-test-policy-1,NIST-test-policy-2");
+  settings.initial_explicit_policy = true;
+
+  ASSERT_FALSE(this->Verify(certs, crls, settings));
+}
+
+// 4.8.4 Different Policies Test4
+WRAPPED_TYPED_TEST_P(PkitsTest08CertificatePolicies,
+                     Section8DifferentPoliciesTest4) {
+  const char* const certs[] = {"TrustAnchorRootCertificate", "GoodCACert",
+                               "GoodsubCACert", "DifferentPoliciesTest4EE"};
+  const char* const crls[] = {"TrustAnchorRootCRL", "GoodCACRL",
+                              "GoodsubCACRL"};
+  ASSERT_FALSE(this->Verify(certs, crls, {}));
+}
+
+// 4.8.5 Different Policies Test5
+WRAPPED_TYPED_TEST_P(PkitsTest08CertificatePolicies,
+                     Section8DifferentPoliciesTest5) {
+  const char* const certs[] = {"TrustAnchorRootCertificate", "GoodCACert",
+                               "PoliciesP2subCA2Cert",
+                               "DifferentPoliciesTest5EE"};
+  const char* const crls[] = {"TrustAnchorRootCRL", "GoodCACRL",
+                              "PoliciesP2subCA2CRL"};
+  ASSERT_FALSE(this->Verify(certs, crls, {}));
+}
+
+// 4.8.6 Overlapping Policies Test6
+WRAPPED_TYPED_TEST_P(PkitsTest08CertificatePolicies,
+                     Section8OverlappingPoliciesTest6Subpart1) {
+  const char* const certs[] = {
+      "TrustAnchorRootCertificate", "PoliciesP1234CACert",
+      "PoliciesP1234subCAP123Cert", "PoliciesP1234subsubCAP123P12Cert",
+      "OverlappingPoliciesTest6EE"};
+  const char* const crls[] = {"TrustAnchorRootCRL", "PoliciesP1234CACRL",
+                              "PoliciesP1234subCAP123CRL",
+                              "PoliciesP1234subsubCAP123P12CRL"};
+  ASSERT_TRUE(this->Verify(certs, crls, {}));
+}
+
+// 4.8.6 Overlapping Policies Test6
+WRAPPED_TYPED_TEST_P(PkitsTest08CertificatePolicies,
+                     Section8OverlappingPoliciesTest6Subpart2) {
+  const char* const certs[] = {
+      "TrustAnchorRootCertificate", "PoliciesP1234CACert",
+      "PoliciesP1234subCAP123Cert", "PoliciesP1234subsubCAP123P12Cert",
+      "OverlappingPoliciesTest6EE"};
+  const char* const crls[] = {"TrustAnchorRootCRL", "PoliciesP1234CACRL",
+                              "PoliciesP1234subCAP123CRL",
+                              "PoliciesP1234subsubCAP123P12CRL"};
+
+  // Custom settings
+  PkitsTestSettings settings;
+  settings.SetInitialPolicySet("NIST-test-policy-1");
+
+  ASSERT_TRUE(this->Verify(certs, crls, settings));
+}
+
+// 4.8.6 Overlapping Policies Test6
+WRAPPED_TYPED_TEST_P(PkitsTest08CertificatePolicies,
+                     Section8OverlappingPoliciesTest6Subpart3) {
+  const char* const certs[] = {
+      "TrustAnchorRootCertificate", "PoliciesP1234CACert",
+      "PoliciesP1234subCAP123Cert", "PoliciesP1234subsubCAP123P12Cert",
+      "OverlappingPoliciesTest6EE"};
+  const char* const crls[] = {"TrustAnchorRootCRL", "PoliciesP1234CACRL",
+                              "PoliciesP1234subCAP123CRL",
+                              "PoliciesP1234subsubCAP123P12CRL"};
+
+  // Custom settings
+  PkitsTestSettings settings;
+  settings.SetInitialPolicySet("NIST-test-policy-2");
+
+  ASSERT_FALSE(this->Verify(certs, crls, settings));
+}
+
+// 4.8.7 Different Policies Test7
+WRAPPED_TYPED_TEST_P(PkitsTest08CertificatePolicies,
+                     Section8DifferentPoliciesTest7) {
+  const char* const certs[] = {"TrustAnchorRootCertificate",
+                               "PoliciesP123CACert", "PoliciesP123subCAP12Cert",
+                               "PoliciesP123subsubCAP12P1Cert",
+                               "DifferentPoliciesTest7EE"};
+  const char* const crls[] = {"TrustAnchorRootCRL", "PoliciesP123CACRL",
+                              "PoliciesP123subCAP12CRL",
+                              "PoliciesP123subsubCAP12P1CRL"};
+  ASSERT_FALSE(this->Verify(certs, crls, {}));
+}
+
+// 4.8.8 Different Policies Test8
+WRAPPED_TYPED_TEST_P(PkitsTest08CertificatePolicies,
+                     Section8DifferentPoliciesTest8) {
+  const char* const certs[] = {"TrustAnchorRootCertificate",
+                               "PoliciesP12CACert", "PoliciesP12subCAP1Cert",
+                               "PoliciesP12subsubCAP1P2Cert",
+                               "DifferentPoliciesTest8EE"};
+  const char* const crls[] = {"TrustAnchorRootCRL", "PoliciesP12CACRL",
+                              "PoliciesP12subCAP1CRL",
+                              "PoliciesP12subsubCAP1P2CRL"};
+  ASSERT_FALSE(this->Verify(certs, crls, {}));
+}
+
+// 4.8.9 Different Policies Test9
+WRAPPED_TYPED_TEST_P(PkitsTest08CertificatePolicies,
+                     Section8DifferentPoliciesTest9) {
+  const char* const certs[] = {
+      "TrustAnchorRootCertificate",         "PoliciesP123CACert",
+      "PoliciesP123subCAP12Cert",           "PoliciesP123subsubCAP12P2Cert",
+      "PoliciesP123subsubsubCAP12P2P1Cert", "DifferentPoliciesTest9EE"};
+  const char* const crls[] = {
+      "TrustAnchorRootCRL", "PoliciesP123CACRL", "PoliciesP123subCAP12CRL",
+      "PoliciesP123subsubCAP2P2CRL", "PoliciesP123subsubsubCAP12P2P1CRL"};
+  ASSERT_FALSE(this->Verify(certs, crls, {}));
+}
+
+// 4.8.10 All Certificates Same Policies Test10
+WRAPPED_TYPED_TEST_P(PkitsTest08CertificatePolicies,
+                     Section8AllCertificatesSamePoliciesTest10Subpart1) {
+  const char* const certs[] = {"TrustAnchorRootCertificate",
+                               "PoliciesP12CACert",
+                               "AllCertificatesSamePoliciesTest10EE"};
+  const char* const crls[] = {"TrustAnchorRootCRL", "PoliciesP12CACRL"};
+  ASSERT_TRUE(this->Verify(certs, crls, {}));
+}
+
+// 4.8.10 All Certificates Same Policies Test10
+WRAPPED_TYPED_TEST_P(PkitsTest08CertificatePolicies,
+                     Section8AllCertificatesSamePoliciesTest10Subpart2) {
+  const char* const certs[] = {"TrustAnchorRootCertificate",
+                               "PoliciesP12CACert",
+                               "AllCertificatesSamePoliciesTest10EE"};
+  const char* const crls[] = {"TrustAnchorRootCRL", "PoliciesP12CACRL"};
+
+  // Custom settings
+  PkitsTestSettings settings;
+  settings.SetInitialPolicySet("NIST-test-policy-1");
+
+  ASSERT_TRUE(this->Verify(certs, crls, settings));
+}
+
+// 4.8.10 All Certificates Same Policies Test10
+WRAPPED_TYPED_TEST_P(PkitsTest08CertificatePolicies,
+                     Section8AllCertificatesSamePoliciesTest10Subpart3) {
+  const char* const certs[] = {"TrustAnchorRootCertificate",
+                               "PoliciesP12CACert",
+                               "AllCertificatesSamePoliciesTest10EE"};
+  const char* const crls[] = {"TrustAnchorRootCRL", "PoliciesP12CACRL"};
+
+  // Custom settings
+  PkitsTestSettings settings;
+  settings.SetInitialPolicySet("NIST-test-policy-2");
+
+  ASSERT_TRUE(this->Verify(certs, crls, settings));
+}
+
+// 4.8.11 All Certificates AnyPolicy Test11
+WRAPPED_TYPED_TEST_P(PkitsTest08CertificatePolicies,
+                     Section8AllCertificatesAnyPolicyTest11Subpart1) {
+  const char* const certs[] = {"TrustAnchorRootCertificate", "anyPolicyCACert",
+                               "AllCertificatesanyPolicyTest11EE"};
+  const char* const crls[] = {"TrustAnchorRootCRL", "anyPolicyCACRL"};
+  ASSERT_TRUE(this->Verify(certs, crls, {}));
+}
+
+// 4.8.11 All Certificates AnyPolicy Test11
+WRAPPED_TYPED_TEST_P(PkitsTest08CertificatePolicies,
+                     Section8AllCertificatesAnyPolicyTest11Subpart2) {
+  const char* const certs[] = {"TrustAnchorRootCertificate", "anyPolicyCACert",
+                               "AllCertificatesanyPolicyTest11EE"};
+  const char* const crls[] = {"TrustAnchorRootCRL", "anyPolicyCACRL"};
+
+  // Custom settings
+  PkitsTestSettings settings;
+  settings.SetInitialPolicySet("NIST-test-policy-1");
+
+  ASSERT_TRUE(this->Verify(certs, crls, settings));
+}
+
+// 4.8.12 Different Policies Test12
+WRAPPED_TYPED_TEST_P(PkitsTest08CertificatePolicies,
+                     Section8DifferentPoliciesTest12) {
+  const char* const certs[] = {"TrustAnchorRootCertificate", "PoliciesP3CACert",
+                               "DifferentPoliciesTest12EE"};
+  const char* const crls[] = {"TrustAnchorRootCRL", "PoliciesP3CACRL"};
+  ASSERT_FALSE(this->Verify(certs, crls, {}));
+}
+
+// 4.8.13 All Certificates Same Policies Test13
+WRAPPED_TYPED_TEST_P(PkitsTest08CertificatePolicies,
+                     Section8AllCertificatesSamePoliciesTest13Subpart1) {
+  const char* const certs[] = {"TrustAnchorRootCertificate",
+                               "PoliciesP123CACert",
+                               "AllCertificatesSamePoliciesTest13EE"};
+  const char* const crls[] = {"TrustAnchorRootCRL", "PoliciesP123CACRL"};
+
+  // Custom settings
+  PkitsTestSettings settings;
+  settings.SetInitialPolicySet("NIST-test-policy-1");
+
+  ASSERT_TRUE(this->Verify(certs, crls, settings));
+}
+
+// 4.8.13 All Certificates Same Policies Test13
+WRAPPED_TYPED_TEST_P(PkitsTest08CertificatePolicies,
+                     Section8AllCertificatesSamePoliciesTest13Subpart2) {
+  const char* const certs[] = {"TrustAnchorRootCertificate",
+                               "PoliciesP123CACert",
+                               "AllCertificatesSamePoliciesTest13EE"};
+  const char* const crls[] = {"TrustAnchorRootCRL", "PoliciesP123CACRL"};
+
+  // Custom settings
+  PkitsTestSettings settings;
+  settings.SetInitialPolicySet("NIST-test-policy-2");
+
+  ASSERT_TRUE(this->Verify(certs, crls, settings));
+}
+
+// 4.8.13 All Certificates Same Policies Test13
+WRAPPED_TYPED_TEST_P(PkitsTest08CertificatePolicies,
+                     Section8AllCertificatesSamePoliciesTest13Subpart3) {
+  const char* const certs[] = {"TrustAnchorRootCertificate",
+                               "PoliciesP123CACert",
+                               "AllCertificatesSamePoliciesTest13EE"};
+  const char* const crls[] = {"TrustAnchorRootCRL", "PoliciesP123CACRL"};
+
+  // Custom settings
+  PkitsTestSettings settings;
+  settings.SetInitialPolicySet("NIST-test-policy-3");
+
+  ASSERT_TRUE(this->Verify(certs, crls, settings));
+}
+
+// 4.8.14 AnyPolicy Test14
+WRAPPED_TYPED_TEST_P(PkitsTest08CertificatePolicies,
+                     Section8AnyPolicyTest14Subpart1) {
+  const char* const certs[] = {"TrustAnchorRootCertificate", "anyPolicyCACert",
+                               "AnyPolicyTest14EE"};
+  const char* const crls[] = {"TrustAnchorRootCRL", "anyPolicyCACRL"};
+
+  // Custom settings
+  PkitsTestSettings settings;
+  settings.SetInitialPolicySet("NIST-test-policy-1");
+
+  ASSERT_TRUE(this->Verify(certs, crls, settings));
+}
+
+// 4.8.14 AnyPolicy Test14
+WRAPPED_TYPED_TEST_P(PkitsTest08CertificatePolicies,
+                     Section8AnyPolicyTest14Subpart2) {
+  const char* const certs[] = {"TrustAnchorRootCertificate", "anyPolicyCACert",
+                               "AnyPolicyTest14EE"};
+  const char* const crls[] = {"TrustAnchorRootCRL", "anyPolicyCACRL"};
+
+  // Custom settings
+  PkitsTestSettings settings;
+  settings.SetInitialPolicySet("NIST-test-policy-2");
+
+  ASSERT_FALSE(this->Verify(certs, crls, settings));
+}
+
+// 4.8.15 User Notice Qualifier Test15
+WRAPPED_TYPED_TEST_P(PkitsTest08CertificatePolicies,
+                     Section8UserNoticeQualifierTest15) {
+  const char* const certs[] = {"TrustAnchorRootCertificate",
+                               "UserNoticeQualifierTest15EE"};
+  const char* const crls[] = {"TrustAnchorRootCRL"};
+  ASSERT_TRUE(this->Verify(certs, crls, {}));
+}
+
+// 4.8.16 User Notice Qualifier Test16
+WRAPPED_TYPED_TEST_P(PkitsTest08CertificatePolicies,
+                     Section8UserNoticeQualifierTest16) {
+  const char* const certs[] = {"TrustAnchorRootCertificate", "GoodCACert",
+                               "UserNoticeQualifierTest16EE"};
+  const char* const crls[] = {"TrustAnchorRootCRL", "GoodCACRL"};
+  ASSERT_TRUE(this->Verify(certs, crls, {}));
+}
+
+// 4.8.17 User Notice Qualifier Test17
+WRAPPED_TYPED_TEST_P(PkitsTest08CertificatePolicies,
+                     Section8UserNoticeQualifierTest17) {
+  const char* const certs[] = {"TrustAnchorRootCertificate", "GoodCACert",
+                               "UserNoticeQualifierTest17EE"};
+  const char* const crls[] = {"TrustAnchorRootCRL", "GoodCACRL"};
+  ASSERT_TRUE(this->Verify(certs, crls, {}));
+}
+
+// 4.8.18 User Notice Qualifier Test18
+WRAPPED_TYPED_TEST_P(PkitsTest08CertificatePolicies,
+                     Section8UserNoticeQualifierTest18Subpart1) {
+  const char* const certs[] = {"TrustAnchorRootCertificate",
+                               "PoliciesP12CACert",
+                               "UserNoticeQualifierTest18EE"};
+  const char* const crls[] = {"TrustAnchorRootCRL", "PoliciesP12CACRL"};
+
+  // Custom settings
+  PkitsTestSettings settings;
+  settings.SetInitialPolicySet("NIST-test-policy-1");
+
+  ASSERT_TRUE(this->Verify(certs, crls, settings));
+}
+
+// 4.8.18 User Notice Qualifier Test18
+WRAPPED_TYPED_TEST_P(PkitsTest08CertificatePolicies,
+                     Section8UserNoticeQualifierTest18Subpart2) {
+  const char* const certs[] = {"TrustAnchorRootCertificate",
+                               "PoliciesP12CACert",
+                               "UserNoticeQualifierTest18EE"};
+  const char* const crls[] = {"TrustAnchorRootCRL", "PoliciesP12CACRL"};
+
+  // Custom settings
+  PkitsTestSettings settings;
+  settings.SetInitialPolicySet("NIST-test-policy-2");
+
+  ASSERT_TRUE(this->Verify(certs, crls, settings));
+}
+
+// 4.8.19 User Notice Qualifier Test19
+WRAPPED_TYPED_TEST_P(PkitsTest08CertificatePolicies,
+                     Section8UserNoticeQualifierTest19) {
+  const char* const certs[] = {"TrustAnchorRootCertificate",
+                               "UserNoticeQualifierTest19EE"};
+  const char* const crls[] = {"TrustAnchorRootCRL"};
+  ASSERT_TRUE(this->Verify(certs, crls, {}));
+}
+
+// 4.8.20 CPS Pointer Qualifier Test20
+WRAPPED_TYPED_TEST_P(PkitsTest08CertificatePolicies,
+                     Section8CPSPointerQualifierTest20) {
+  const char* const certs[] = {"TrustAnchorRootCertificate", "GoodCACert",
+                               "CPSPointerQualifierTest20EE"};
+  const char* const crls[] = {"TrustAnchorRootCRL", "GoodCACRL"};
+
+  // Custom settings
+  PkitsTestSettings settings;
+  settings.SetInitialPolicySet("NIST-test-policy-1");
+  settings.initial_explicit_policy = true;
+
+  ASSERT_TRUE(this->Verify(certs, crls, settings));
+}
+
+WRAPPED_REGISTER_TYPED_TEST_CASE_P(
+    PkitsTest08CertificatePolicies,
+    Section8AllCertificatesSamePolicyTest1Subpart1,
+    Section8AllCertificatesSamePolicyTest1Subpart2,
+    Section8AllCertificatesSamePolicyTest1Subpart3,
+    Section8AllCertificatesSamePolicyTest1Subpart4,
+    Section8AllCertificatesNoPoliciesTest2Subpart1,
+    Section8AllCertificatesNoPoliciesTest2Subpart2,
+    Section8DifferentPoliciesTest3Subpart1,
+    Section8DifferentPoliciesTest3Subpart2,
+    Section8DifferentPoliciesTest3Subpart3,
+    Section8DifferentPoliciesTest4,
+    Section8DifferentPoliciesTest5,
+    Section8OverlappingPoliciesTest6Subpart1,
+    Section8OverlappingPoliciesTest6Subpart2,
+    Section8OverlappingPoliciesTest6Subpart3,
+    Section8DifferentPoliciesTest7,
+    Section8DifferentPoliciesTest8,
+    Section8DifferentPoliciesTest9,
+    Section8AllCertificatesSamePoliciesTest10Subpart1,
+    Section8AllCertificatesSamePoliciesTest10Subpart2,
+    Section8AllCertificatesSamePoliciesTest10Subpart3,
+    Section8AllCertificatesAnyPolicyTest11Subpart1,
+    Section8AllCertificatesAnyPolicyTest11Subpart2,
+    Section8DifferentPoliciesTest12,
+    Section8AllCertificatesSamePoliciesTest13Subpart1,
+    Section8AllCertificatesSamePoliciesTest13Subpart2,
+    Section8AllCertificatesSamePoliciesTest13Subpart3,
+    Section8AnyPolicyTest14Subpart1,
+    Section8AnyPolicyTest14Subpart2,
+    Section8UserNoticeQualifierTest15,
+    Section8UserNoticeQualifierTest16,
+    Section8UserNoticeQualifierTest17,
+    Section8UserNoticeQualifierTest18Subpart1,
+    Section8UserNoticeQualifierTest18Subpart2,
+    Section8UserNoticeQualifierTest19,
+    Section8CPSPointerQualifierTest20);
+
+template <typename PkitsTestDelegate>
+class PkitsTest09RequireExplicitPolicy : public PkitsTest<PkitsTestDelegate> {};
+TYPED_TEST_CASE_P(PkitsTest09RequireExplicitPolicy);
+
+// 4.9.1 Valid RequireExplicitPolicy Test1
+WRAPPED_TYPED_TEST_P(PkitsTest09RequireExplicitPolicy,
+                     Section9ValidRequireExplicitPolicyTest1) {
+  const char* const certs[] = {"TrustAnchorRootCertificate",
+                               "requireExplicitPolicy10CACert",
+                               "requireExplicitPolicy10subCACert",
+                               "requireExplicitPolicy10subsubCACert",
+                               "requireExplicitPolicy10subsubsubCACert",
+                               "ValidrequireExplicitPolicyTest1EE"};
+  const char* const crls[] = {
+      "TrustAnchorRootCRL", "requireExplicitPolicy10CACRL",
+      "requireExplicitPolicy10subCACRL", "requireExplicitPolicy10subsubCACRL",
+      "requireExplicitPolicy10subsubsubCACRL"};
+  ASSERT_TRUE(this->Verify(certs, crls, {}));
+}
+
+// 4.9.2 Valid RequireExplicitPolicy Test2
+WRAPPED_TYPED_TEST_P(PkitsTest09RequireExplicitPolicy,
+                     Section9ValidRequireExplicitPolicyTest2) {
+  const char* const certs[] = {"TrustAnchorRootCertificate",
+                               "requireExplicitPolicy5CACert",
+                               "requireExplicitPolicy5subCACert",
+                               "requireExplicitPolicy5subsubCACert",
+                               "requireExplicitPolicy5subsubsubCACert",
+                               "ValidrequireExplicitPolicyTest2EE"};
+  const char* const crls[] = {
+      "TrustAnchorRootCRL", "requireExplicitPolicy5CACRL",
+      "requireExplicitPolicy5subCACRL", "requireExplicitPolicy5subsubCACRL",
+      "requireExplicitPolicy5subsubsubCACRL"};
+  ASSERT_TRUE(this->Verify(certs, crls, {}));
+}
+
+// 4.9.3 Invalid RequireExplicitPolicy Test3
+WRAPPED_TYPED_TEST_P(PkitsTest09RequireExplicitPolicy,
+                     Section9InvalidRequireExplicitPolicyTest3) {
+  const char* const certs[] = {"TrustAnchorRootCertificate",
+                               "requireExplicitPolicy4CACert",
+                               "requireExplicitPolicy4subCACert",
+                               "requireExplicitPolicy4subsubCACert",
+                               "requireExplicitPolicy4subsubsubCACert",
+                               "InvalidrequireExplicitPolicyTest3EE"};
+  const char* const crls[] = {
+      "TrustAnchorRootCRL", "requireExplicitPolicy4CACRL",
+      "requireExplicitPolicy4subCACRL", "requireExplicitPolicy4subsubCACRL",
+      "requireExplicitPolicy4subsubsubCACRL"};
+  ASSERT_FALSE(this->Verify(certs, crls, {}));
+}
+
+// 4.9.4 Valid RequireExplicitPolicy Test4
+WRAPPED_TYPED_TEST_P(PkitsTest09RequireExplicitPolicy,
+                     Section9ValidRequireExplicitPolicyTest4) {
+  const char* const certs[] = {"TrustAnchorRootCertificate",
+                               "requireExplicitPolicy0CACert",
+                               "requireExplicitPolicy0subCACert",
+                               "requireExplicitPolicy0subsubCACert",
+                               "requireExplicitPolicy0subsubsubCACert",
+                               "ValidrequireExplicitPolicyTest4EE"};
+  const char* const crls[] = {
+      "TrustAnchorRootCRL", "requireExplicitPolicy0CACRL",
+      "requireExplicitPolicy0subCACRL", "requireExplicitPolicy0subsubCACRL",
+      "requireExplicitPolicy0subsubsubCACRL"};
+  ASSERT_TRUE(this->Verify(certs, crls, {}));
+}
+
+// 4.9.5 Invalid RequireExplicitPolicy Test5
+WRAPPED_TYPED_TEST_P(PkitsTest09RequireExplicitPolicy,
+                     Section9InvalidRequireExplicitPolicyTest5) {
+  const char* const certs[] = {"TrustAnchorRootCertificate",
+                               "requireExplicitPolicy7CACert",
+                               "requireExplicitPolicy7subCARE2Cert",
+                               "requireExplicitPolicy7subsubCARE2RE4Cert",
+                               "requireExplicitPolicy7subsubsubCARE2RE4Cert",
+                               "InvalidrequireExplicitPolicyTest5EE"};
+  const char* const crls[] = {"TrustAnchorRootCRL",
+                              "requireExplicitPolicy7CACRL",
+                              "requireExplicitPolicy7subCARE2CRL",
+                              "requireExplicitPolicy7subsubCARE2RE4CRL",
+                              "requireExplicitPolicy7subsubsubCARE2RE4CRL"};
+  ASSERT_FALSE(this->Verify(certs, crls, {}));
+}
+
+// 4.9.6 Valid Self-Issued requireExplicitPolicy Test6
+WRAPPED_TYPED_TEST_P(PkitsTest09RequireExplicitPolicy,
+                     Section9ValidSelfIssuedrequireExplicitPolicyTest6) {
+  const char* const certs[] = {"TrustAnchorRootCertificate",
+                               "requireExplicitPolicy2CACert",
+                               "requireExplicitPolicy2SelfIssuedCACert",
+                               "ValidSelfIssuedrequireExplicitPolicyTest6EE"};
+  const char* const crls[] = {"TrustAnchorRootCRL",
+                              "requireExplicitPolicy2CACRL"};
+  ASSERT_TRUE(this->Verify(certs, crls, {}));
+}
+
+// 4.9.7 Invalid Self-Issued requireExplicitPolicy Test7
+WRAPPED_TYPED_TEST_P(PkitsTest09RequireExplicitPolicy,
+                     Section9InvalidSelfIssuedrequireExplicitPolicyTest7) {
+  const char* const certs[] = {"TrustAnchorRootCertificate",
+                               "requireExplicitPolicy2CACert",
+                               "requireExplicitPolicy2SelfIssuedCACert",
+                               "requireExplicitPolicy2subCACert",
+                               "InvalidSelfIssuedrequireExplicitPolicyTest7EE"};
+  const char* const crls[] = {"TrustAnchorRootCRL",
+                              "requireExplicitPolicy2CACRL",
+                              "requireExplicitPolicy2subCACRL"};
+  ASSERT_FALSE(this->Verify(certs, crls, {}));
+}
+
+// 4.9.8 Invalid Self-Issued requireExplicitPolicy Test8
+WRAPPED_TYPED_TEST_P(PkitsTest09RequireExplicitPolicy,
+                     Section9InvalidSelfIssuedrequireExplicitPolicyTest8) {
+  const char* const certs[] = {"TrustAnchorRootCertificate",
+                               "requireExplicitPolicy2CACert",
+                               "requireExplicitPolicy2SelfIssuedCACert",
+                               "requireExplicitPolicy2subCACert",
+                               "requireExplicitPolicy2SelfIssuedsubCACert",
+                               "InvalidSelfIssuedrequireExplicitPolicyTest8EE"};
+  const char* const crls[] = {"TrustAnchorRootCRL",
+                              "requireExplicitPolicy2CACRL",
+                              "requireExplicitPolicy2subCACRL"};
+  ASSERT_FALSE(this->Verify(certs, crls, {}));
+}
+
+WRAPPED_REGISTER_TYPED_TEST_CASE_P(
+    PkitsTest09RequireExplicitPolicy,
+    Section9ValidRequireExplicitPolicyTest1,
+    Section9ValidRequireExplicitPolicyTest2,
+    Section9InvalidRequireExplicitPolicyTest3,
+    Section9ValidRequireExplicitPolicyTest4,
+    Section9InvalidRequireExplicitPolicyTest5,
+    Section9ValidSelfIssuedrequireExplicitPolicyTest6,
+    Section9InvalidSelfIssuedrequireExplicitPolicyTest7,
+    Section9InvalidSelfIssuedrequireExplicitPolicyTest8);
+
+template <typename PkitsTestDelegate>
+class PkitsTest10PolicyMappings : public PkitsTest<PkitsTestDelegate> {};
+TYPED_TEST_CASE_P(PkitsTest10PolicyMappings);
+
+// 4.10.1 Valid Policy Mapping Test1
+WRAPPED_TYPED_TEST_P(PkitsTest10PolicyMappings,
+                     Section10ValidPolicyMappingTest1Subpart1) {
+  const char* const certs[] = {"TrustAnchorRootCertificate",
+                               "Mapping1to2CACert",
+                               "ValidPolicyMappingTest1EE"};
+  const char* const crls[] = {"TrustAnchorRootCRL", "Mapping1to2CACRL"};
+
+  // Custom settings
+  PkitsTestSettings settings;
+  settings.SetInitialPolicySet("NIST-test-policy-1");
+
+  ASSERT_TRUE(this->Verify(certs, crls, settings));
+}
+
+// 4.10.1 Valid Policy Mapping Test1
+WRAPPED_TYPED_TEST_P(PkitsTest10PolicyMappings,
+                     Section10ValidPolicyMappingTest1Subpart2) {
+  const char* const certs[] = {"TrustAnchorRootCertificate",
+                               "Mapping1to2CACert",
+                               "ValidPolicyMappingTest1EE"};
+  const char* const crls[] = {"TrustAnchorRootCRL", "Mapping1to2CACRL"};
+
+  // Custom settings
+  PkitsTestSettings settings;
+  settings.SetInitialPolicySet("NIST-test-policy-2");
+
+  ASSERT_FALSE(this->Verify(certs, crls, settings));
+}
+
+// 4.10.1 Valid Policy Mapping Test1
+WRAPPED_TYPED_TEST_P(PkitsTest10PolicyMappings,
+                     Section10ValidPolicyMappingTest1Subpart3) {
+  const char* const certs[] = {"TrustAnchorRootCertificate",
+                               "Mapping1to2CACert",
+                               "ValidPolicyMappingTest1EE"};
+  const char* const crls[] = {"TrustAnchorRootCRL", "Mapping1to2CACRL"};
+
+  // Custom settings
+  PkitsTestSettings settings;
+  settings.initial_policy_mapping_inhibit = true;
+
+  ASSERT_FALSE(this->Verify(certs, crls, settings));
+}
+
+// 4.10.2 Invalid Policy Mapping Test2
+WRAPPED_TYPED_TEST_P(PkitsTest10PolicyMappings,
+                     Section10InvalidPolicyMappingTest2Subpart1) {
+  const char* const certs[] = {"TrustAnchorRootCertificate",
+                               "Mapping1to2CACert",
+                               "InvalidPolicyMappingTest2EE"};
+  const char* const crls[] = {"TrustAnchorRootCRL", "Mapping1to2CACRL"};
+  ASSERT_FALSE(this->Verify(certs, crls, {}));
+}
+
+// 4.10.2 Invalid Policy Mapping Test2
+WRAPPED_TYPED_TEST_P(PkitsTest10PolicyMappings,
+                     Section10InvalidPolicyMappingTest2Subpart2) {
+  const char* const certs[] = {"TrustAnchorRootCertificate",
+                               "Mapping1to2CACert",
+                               "InvalidPolicyMappingTest2EE"};
+  const char* const crls[] = {"TrustAnchorRootCRL", "Mapping1to2CACRL"};
+
+  // Custom settings
+  PkitsTestSettings settings;
+  settings.initial_policy_mapping_inhibit = true;
+
+  ASSERT_FALSE(this->Verify(certs, crls, settings));
+}
+
+// 4.10.3 Valid Policy Mapping Test3
+WRAPPED_TYPED_TEST_P(PkitsTest10PolicyMappings,
+                     Section10ValidPolicyMappingTest3Subpart1) {
+  const char* const certs[] = {
+      "TrustAnchorRootCertificate", "P12Mapping1to3CACert",
+      "P12Mapping1to3subCACert", "P12Mapping1to3subsubCACert",
+      "ValidPolicyMappingTest3EE"};
+  const char* const crls[] = {"TrustAnchorRootCRL", "P12Mapping1to3CACRL",
+                              "P12Mapping1to3subCACRL",
+                              "P12Mapping1to3subsubCACRL"};
+
+  // Custom settings
+  PkitsTestSettings settings;
+  settings.SetInitialPolicySet("NIST-test-policy-1");
+
+  ASSERT_FALSE(this->Verify(certs, crls, settings));
+}
+
+// 4.10.3 Valid Policy Mapping Test3
+WRAPPED_TYPED_TEST_P(PkitsTest10PolicyMappings,
+                     Section10ValidPolicyMappingTest3Subpart2) {
+  const char* const certs[] = {
+      "TrustAnchorRootCertificate", "P12Mapping1to3CACert",
+      "P12Mapping1to3subCACert", "P12Mapping1to3subsubCACert",
+      "ValidPolicyMappingTest3EE"};
+  const char* const crls[] = {"TrustAnchorRootCRL", "P12Mapping1to3CACRL",
+                              "P12Mapping1to3subCACRL",
+                              "P12Mapping1to3subsubCACRL"};
+
+  // Custom settings
+  PkitsTestSettings settings;
+  settings.SetInitialPolicySet("NIST-test-policy-2");
+
+  ASSERT_TRUE(this->Verify(certs, crls, settings));
+}
+
+// 4.10.4 Invalid Policy Mapping Test4
+WRAPPED_TYPED_TEST_P(PkitsTest10PolicyMappings,
+                     Section10InvalidPolicyMappingTest4) {
+  const char* const certs[] = {
+      "TrustAnchorRootCertificate", "P12Mapping1to3CACert",
+      "P12Mapping1to3subCACert", "P12Mapping1to3subsubCACert",
+      "InvalidPolicyMappingTest4EE"};
+  const char* const crls[] = {"TrustAnchorRootCRL", "P12Mapping1to3CACRL",
+                              "P12Mapping1to3subCACRL",
+                              "P12Mapping1to3subsubCACRL"};
+  ASSERT_FALSE(this->Verify(certs, crls, {}));
+}
+
+// 4.10.5 Valid Policy Mapping Test5
+WRAPPED_TYPED_TEST_P(PkitsTest10PolicyMappings,
+                     Section10ValidPolicyMappingTest5Subpart1) {
+  const char* const certs[] = {
+      "TrustAnchorRootCertificate", "P1Mapping1to234CACert",
+      "P1Mapping1to234subCACert", "ValidPolicyMappingTest5EE"};
+  const char* const crls[] = {"TrustAnchorRootCRL", "P1Mapping1to234CACRL",
+                              "P1Mapping1to234subCACRL"};
+
+  // Custom settings
+  PkitsTestSettings settings;
+  settings.SetInitialPolicySet("NIST-test-policy-1");
+
+  ASSERT_TRUE(this->Verify(certs, crls, settings));
+}
+
+// 4.10.5 Valid Policy Mapping Test5
+WRAPPED_TYPED_TEST_P(PkitsTest10PolicyMappings,
+                     Section10ValidPolicyMappingTest5Subpart2) {
+  const char* const certs[] = {
+      "TrustAnchorRootCertificate", "P1Mapping1to234CACert",
+      "P1Mapping1to234subCACert", "ValidPolicyMappingTest5EE"};
+  const char* const crls[] = {"TrustAnchorRootCRL", "P1Mapping1to234CACRL",
+                              "P1Mapping1to234subCACRL"};
+
+  // Custom settings
+  PkitsTestSettings settings;
+  settings.SetInitialPolicySet("NIST-test-policy-6");
+
+  ASSERT_FALSE(this->Verify(certs, crls, settings));
+}
+
+// 4.10.6 Valid Policy Mapping Test6
+WRAPPED_TYPED_TEST_P(PkitsTest10PolicyMappings,
+                     Section10ValidPolicyMappingTest6Subpart1) {
+  const char* const certs[] = {
+      "TrustAnchorRootCertificate", "P1Mapping1to234CACert",
+      "P1Mapping1to234subCACert", "ValidPolicyMappingTest6EE"};
+  const char* const crls[] = {"TrustAnchorRootCRL", "P1Mapping1to234CACRL",
+                              "P1Mapping1to234subCACRL"};
+
+  // Custom settings
+  PkitsTestSettings settings;
+  settings.SetInitialPolicySet("NIST-test-policy-1");
+
+  ASSERT_TRUE(this->Verify(certs, crls, settings));
+}
+
+// 4.10.6 Valid Policy Mapping Test6
+WRAPPED_TYPED_TEST_P(PkitsTest10PolicyMappings,
+                     Section10ValidPolicyMappingTest6Subpart2) {
+  const char* const certs[] = {
+      "TrustAnchorRootCertificate", "P1Mapping1to234CACert",
+      "P1Mapping1to234subCACert", "ValidPolicyMappingTest6EE"};
+  const char* const crls[] = {"TrustAnchorRootCRL", "P1Mapping1to234CACRL",
+                              "P1Mapping1to234subCACRL"};
+
+  // Custom settings
+  PkitsTestSettings settings;
+  settings.SetInitialPolicySet("NIST-test-policy-6");
+
+  ASSERT_FALSE(this->Verify(certs, crls, settings));
+}
+
+// 4.10.7 Invalid Mapping From anyPolicy Test7
+WRAPPED_TYPED_TEST_P(PkitsTest10PolicyMappings,
+                     Section10InvalidMappingFromanyPolicyTest7) {
+  const char* const certs[] = {"TrustAnchorRootCertificate",
+                               "MappingFromanyPolicyCACert",
+                               "InvalidMappingFromanyPolicyTest7EE"};
+  const char* const crls[] = {"TrustAnchorRootCRL",
+                              "MappingFromanyPolicyCACRL"};
+  ASSERT_FALSE(this->Verify(certs, crls, {}));
+}
+
+// 4.10.8 Invalid Mapping To anyPolicy Test8
+WRAPPED_TYPED_TEST_P(PkitsTest10PolicyMappings,
+                     Section10InvalidMappingToanyPolicyTest8) {
+  const char* const certs[] = {"TrustAnchorRootCertificate",
+                               "MappingToanyPolicyCACert",
+                               "InvalidMappingToanyPolicyTest8EE"};
+  const char* const crls[] = {"TrustAnchorRootCRL", "MappingToanyPolicyCACRL"};
+  ASSERT_FALSE(this->Verify(certs, crls, {}));
+}
+
+// 4.10.9 Valid Policy Mapping Test9
+WRAPPED_TYPED_TEST_P(PkitsTest10PolicyMappings,
+                     Section10ValidPolicyMappingTest9) {
+  const char* const certs[] = {"TrustAnchorRootCertificate",
+                               "PanyPolicyMapping1to2CACert",
+                               "ValidPolicyMappingTest9EE"};
+  const char* const crls[] = {"TrustAnchorRootCRL",
+                              "PanyPolicyMapping1to2CACRL"};
+  ASSERT_TRUE(this->Verify(certs, crls, {}));
+}
+
+// 4.10.10 Invalid Policy Mapping Test10
+WRAPPED_TYPED_TEST_P(PkitsTest10PolicyMappings,
+                     Section10InvalidPolicyMappingTest10) {
+  const char* const certs[] = {"TrustAnchorRootCertificate", "GoodCACert",
+                               "GoodsubCAPanyPolicyMapping1to2CACert",
+                               "InvalidPolicyMappingTest10EE"};
+  const char* const crls[] = {"TrustAnchorRootCRL", "GoodCACRL",
+                              "GoodsubCAPanyPolicyMapping1to2CACRL"};
+  ASSERT_FALSE(this->Verify(certs, crls, {}));
+}
+
+// 4.10.11 Valid Policy Mapping Test11
+WRAPPED_TYPED_TEST_P(PkitsTest10PolicyMappings,
+                     Section10ValidPolicyMappingTest11) {
+  const char* const certs[] = {"TrustAnchorRootCertificate", "GoodCACert",
+                               "GoodsubCAPanyPolicyMapping1to2CACert",
+                               "ValidPolicyMappingTest11EE"};
+  const char* const crls[] = {"TrustAnchorRootCRL", "GoodCACRL",
+                              "GoodsubCAPanyPolicyMapping1to2CACRL"};
+  ASSERT_TRUE(this->Verify(certs, crls, {}));
+}
+
+// 4.10.12 Valid Policy Mapping Test12
+WRAPPED_TYPED_TEST_P(PkitsTest10PolicyMappings,
+                     Section10ValidPolicyMappingTest12Subpart1) {
+  const char* const certs[] = {"TrustAnchorRootCertificate",
+                               "P12Mapping1to3CACert",
+                               "ValidPolicyMappingTest12EE"};
+  const char* const crls[] = {"TrustAnchorRootCRL", "P12Mapping1to3CACRL"};
+
+  // Custom settings
+  PkitsTestSettings settings;
+  settings.SetInitialPolicySet("NIST-test-policy-1");
+
+  ASSERT_TRUE(this->Verify(certs, crls, settings));
+}
+
+// 4.10.12 Valid Policy Mapping Test12
+WRAPPED_TYPED_TEST_P(PkitsTest10PolicyMappings,
+                     Section10ValidPolicyMappingTest12Subpart2) {
+  const char* const certs[] = {"TrustAnchorRootCertificate",
+                               "P12Mapping1to3CACert",
+                               "ValidPolicyMappingTest12EE"};
+  const char* const crls[] = {"TrustAnchorRootCRL", "P12Mapping1to3CACRL"};
+
+  // Custom settings
+  PkitsTestSettings settings;
+  settings.SetInitialPolicySet("NIST-test-policy-2");
+
+  ASSERT_TRUE(this->Verify(certs, crls, settings));
+}
+
+// 4.10.13 Valid Policy Mapping Test13
+WRAPPED_TYPED_TEST_P(PkitsTest10PolicyMappings,
+                     Section10ValidPolicyMappingTest13) {
+  const char* const certs[] = {"TrustAnchorRootCertificate",
+                               "P1anyPolicyMapping1to2CACert",
+                               "ValidPolicyMappingTest13EE"};
+  const char* const crls[] = {"TrustAnchorRootCRL",
+                              "P1anyPolicyMapping1to2CACRL"};
+  ASSERT_TRUE(this->Verify(certs, crls, {}));
+}
+
+// 4.10.14 Valid Policy Mapping Test14
+WRAPPED_TYPED_TEST_P(PkitsTest10PolicyMappings,
+                     Section10ValidPolicyMappingTest14) {
+  const char* const certs[] = {"TrustAnchorRootCertificate",
+                               "P1anyPolicyMapping1to2CACert",
+                               "ValidPolicyMappingTest14EE"};
+  const char* const crls[] = {"TrustAnchorRootCRL",
+                              "P1anyPolicyMapping1to2CACRL"};
+  ASSERT_TRUE(this->Verify(certs, crls, {}));
+}
+
+WRAPPED_REGISTER_TYPED_TEST_CASE_P(PkitsTest10PolicyMappings,
+                                   Section10ValidPolicyMappingTest1Subpart1,
+                                   Section10ValidPolicyMappingTest1Subpart2,
+                                   Section10ValidPolicyMappingTest1Subpart3,
+                                   Section10InvalidPolicyMappingTest2Subpart1,
+                                   Section10InvalidPolicyMappingTest2Subpart2,
+                                   Section10ValidPolicyMappingTest3Subpart1,
+                                   Section10ValidPolicyMappingTest3Subpart2,
+                                   Section10InvalidPolicyMappingTest4,
+                                   Section10ValidPolicyMappingTest5Subpart1,
+                                   Section10ValidPolicyMappingTest5Subpart2,
+                                   Section10ValidPolicyMappingTest6Subpart1,
+                                   Section10ValidPolicyMappingTest6Subpart2,
+                                   Section10InvalidMappingFromanyPolicyTest7,
+                                   Section10InvalidMappingToanyPolicyTest8,
+                                   Section10ValidPolicyMappingTest9,
+                                   Section10InvalidPolicyMappingTest10,
+                                   Section10ValidPolicyMappingTest11,
+                                   Section10ValidPolicyMappingTest12Subpart1,
+                                   Section10ValidPolicyMappingTest12Subpart2,
+                                   Section10ValidPolicyMappingTest13,
+                                   Section10ValidPolicyMappingTest14);
+
+template <typename PkitsTestDelegate>
+class PkitsTest11InhibitPolicyMapping : public PkitsTest<PkitsTestDelegate> {};
+TYPED_TEST_CASE_P(PkitsTest11InhibitPolicyMapping);
+
+// 4.11.1 Invalid inhibitPolicyMapping Test1
+WRAPPED_TYPED_TEST_P(PkitsTest11InhibitPolicyMapping,
+                     Section11InvalidinhibitPolicyMappingTest1) {
+  const char* const certs[] = {
+      "TrustAnchorRootCertificate", "inhibitPolicyMapping0CACert",
+      "inhibitPolicyMapping0subCACert", "InvalidinhibitPolicyMappingTest1EE"};
+  const char* const crls[] = {"TrustAnchorRootCRL",
+                              "inhibitPolicyMapping0CACRL",
+                              "inhibitPolicyMapping0subCACRL"};
+  ASSERT_FALSE(this->Verify(certs, crls, {}));
+}
+
+// 4.11.2 Valid inhibitPolicyMapping Test2
+WRAPPED_TYPED_TEST_P(PkitsTest11InhibitPolicyMapping,
+                     Section11ValidinhibitPolicyMappingTest2) {
+  const char* const certs[] = {
+      "TrustAnchorRootCertificate", "inhibitPolicyMapping1P12CACert",
+      "inhibitPolicyMapping1P12subCACert", "ValidinhibitPolicyMappingTest2EE"};
+  const char* const crls[] = {"TrustAnchorRootCRL",
+                              "inhibitPolicyMapping1P12CACRL",
+                              "inhibitPolicyMapping1P12subCACRL"};
+  ASSERT_TRUE(this->Verify(certs, crls, {}));
+}
+
+// 4.11.3 Invalid inhibitPolicyMapping Test3
+WRAPPED_TYPED_TEST_P(PkitsTest11InhibitPolicyMapping,
+                     Section11InvalidinhibitPolicyMappingTest3) {
+  const char* const certs[] = {"TrustAnchorRootCertificate",
+                               "inhibitPolicyMapping1P12CACert",
+                               "inhibitPolicyMapping1P12subCACert",
+                               "inhibitPolicyMapping1P12subsubCACert",
+                               "InvalidinhibitPolicyMappingTest3EE"};
+  const char* const crls[] = {"TrustAnchorRootCRL",
+                              "inhibitPolicyMapping1P12CACRL",
+                              "inhibitPolicyMapping1P12subCACRL",
+                              "inhibitPolicyMapping1P12subsubCACRL"};
+  ASSERT_FALSE(this->Verify(certs, crls, {}));
+}
+
+// 4.11.4 Valid inhibitPolicyMapping Test4
+WRAPPED_TYPED_TEST_P(PkitsTest11InhibitPolicyMapping,
+                     Section11ValidinhibitPolicyMappingTest4) {
+  const char* const certs[] = {"TrustAnchorRootCertificate",
+                               "inhibitPolicyMapping1P12CACert",
+                               "inhibitPolicyMapping1P12subCACert",
+                               "inhibitPolicyMapping1P12subsubCACert",
+                               "ValidinhibitPolicyMappingTest4EE"};
+  const char* const crls[] = {"TrustAnchorRootCRL",
+                              "inhibitPolicyMapping1P12CACRL",
+                              "inhibitPolicyMapping1P12subCACRL",
+                              "inhibitPolicyMapping1P12subsubCACRL"};
+  ASSERT_TRUE(this->Verify(certs, crls, {}));
+}
+
+// 4.11.5 Invalid inhibitPolicyMapping Test5
+WRAPPED_TYPED_TEST_P(PkitsTest11InhibitPolicyMapping,
+                     Section11InvalidinhibitPolicyMappingTest5) {
+  const char* const certs[] = {"TrustAnchorRootCertificate",
+                               "inhibitPolicyMapping5CACert",
+                               "inhibitPolicyMapping5subCACert",
+                               "inhibitPolicyMapping5subsubCACert",
+                               "inhibitPolicyMapping5subsubsubCACert",
+                               "InvalidinhibitPolicyMappingTest5EE"};
+  const char* const crls[] = {
+      "TrustAnchorRootCRL", "inhibitPolicyMapping5CACRL",
+      "inhibitPolicyMapping5subCACRL", "inhibitPolicyMapping5subsubCACRL",
+      "inhibitPolicyMapping5subsubsubCACRL"};
+  ASSERT_FALSE(this->Verify(certs, crls, {}));
+}
+
+// 4.11.6 Invalid inhibitPolicyMapping Test6
+WRAPPED_TYPED_TEST_P(PkitsTest11InhibitPolicyMapping,
+                     Section11InvalidinhibitPolicyMappingTest6) {
+  const char* const certs[] = {"TrustAnchorRootCertificate",
+                               "inhibitPolicyMapping1P12CACert",
+                               "inhibitPolicyMapping1P12subCAIPM5Cert",
+                               "inhibitPolicyMapping1P12subsubCAIPM5Cert",
+                               "InvalidinhibitPolicyMappingTest6EE"};
+  const char* const crls[] = {"TrustAnchorRootCRL",
+                              "inhibitPolicyMapping1P12CACRL",
+                              "inhibitPolicyMapping1P12subCAIPM5CRL",
+                              "inhibitPolicyMapping1P12subsubCAIPM5CRL"};
+  ASSERT_FALSE(this->Verify(certs, crls, {}));
+}
+
+// 4.11.7 Valid Self-Issued inhibitPolicyMapping Test7
+WRAPPED_TYPED_TEST_P(PkitsTest11InhibitPolicyMapping,
+                     Section11ValidSelfIssuedinhibitPolicyMappingTest7) {
+  const char* const certs[] = {"TrustAnchorRootCertificate",
+                               "inhibitPolicyMapping1P1CACert",
+                               "inhibitPolicyMapping1P1SelfIssuedCACert",
+                               "inhibitPolicyMapping1P1subCACert",
+                               "ValidSelfIssuedinhibitPolicyMappingTest7EE"};
+  const char* const crls[] = {"TrustAnchorRootCRL",
+                              "inhibitPolicyMapping1P1CACRL",
+                              "inhibitPolicyMapping1P1subCACRL"};
+  ASSERT_TRUE(this->Verify(certs, crls, {}));
+}
+
+// 4.11.8 Invalid Self-Issued inhibitPolicyMapping Test8
+WRAPPED_TYPED_TEST_P(PkitsTest11InhibitPolicyMapping,
+                     Section11InvalidSelfIssuedinhibitPolicyMappingTest8) {
+  const char* const certs[] = {"TrustAnchorRootCertificate",
+                               "inhibitPolicyMapping1P1CACert",
+                               "inhibitPolicyMapping1P1SelfIssuedCACert",
+                               "inhibitPolicyMapping1P1subCACert",
+                               "inhibitPolicyMapping1P1subsubCACert",
+                               "InvalidSelfIssuedinhibitPolicyMappingTest8EE"};
+  const char* const crls[] = {
+      "TrustAnchorRootCRL", "inhibitPolicyMapping1P1CACRL",
+      "inhibitPolicyMapping1P1subCACRL", "inhibitPolicyMapping1P1subsubCACRL"};
+  ASSERT_FALSE(this->Verify(certs, crls, {}));
+}
+
+// 4.11.9 Invalid Self-Issued inhibitPolicyMapping Test9
+WRAPPED_TYPED_TEST_P(PkitsTest11InhibitPolicyMapping,
+                     Section11InvalidSelfIssuedinhibitPolicyMappingTest9) {
+  const char* const certs[] = {"TrustAnchorRootCertificate",
+                               "inhibitPolicyMapping1P1CACert",
+                               "inhibitPolicyMapping1P1SelfIssuedCACert",
+                               "inhibitPolicyMapping1P1subCACert",
+                               "inhibitPolicyMapping1P1subsubCACert",
+                               "InvalidSelfIssuedinhibitPolicyMappingTest9EE"};
+  const char* const crls[] = {
+      "TrustAnchorRootCRL", "inhibitPolicyMapping1P1CACRL",
+      "inhibitPolicyMapping1P1subCACRL", "inhibitPolicyMapping1P1subsubCACRL"};
+  ASSERT_FALSE(this->Verify(certs, crls, {}));
+}
+
+// 4.11.10 Invalid Self-Issued inhibitPolicyMapping Test10
+WRAPPED_TYPED_TEST_P(PkitsTest11InhibitPolicyMapping,
+                     Section11InvalidSelfIssuedinhibitPolicyMappingTest10) {
+  const char* const certs[] = {"TrustAnchorRootCertificate",
+                               "inhibitPolicyMapping1P1CACert",
+                               "inhibitPolicyMapping1P1SelfIssuedCACert",
+                               "inhibitPolicyMapping1P1subCACert",
+                               "inhibitPolicyMapping1P1SelfIssuedsubCACert",
+                               "InvalidSelfIssuedinhibitPolicyMappingTest10EE"};
+  const char* const crls[] = {"TrustAnchorRootCRL",
+                              "inhibitPolicyMapping1P1CACRL",
+                              "inhibitPolicyMapping1P1subCACRL"};
+  ASSERT_FALSE(this->Verify(certs, crls, {}));
+}
+
+// 4.11.11 Invalid Self-Issued inhibitPolicyMapping Test11
+WRAPPED_TYPED_TEST_P(PkitsTest11InhibitPolicyMapping,
+                     Section11InvalidSelfIssuedinhibitPolicyMappingTest11) {
+  const char* const certs[] = {"TrustAnchorRootCertificate",
+                               "inhibitPolicyMapping1P1CACert",
+                               "inhibitPolicyMapping1P1SelfIssuedCACert",
+                               "inhibitPolicyMapping1P1subCACert",
+                               "inhibitPolicyMapping1P1SelfIssuedsubCACert",
+                               "InvalidSelfIssuedinhibitPolicyMappingTest11EE"};
+  const char* const crls[] = {"TrustAnchorRootCRL",
+                              "inhibitPolicyMapping1P1CACRL",
+                              "inhibitPolicyMapping1P1subCACRL"};
+  ASSERT_FALSE(this->Verify(certs, crls, {}));
+}
+
+WRAPPED_REGISTER_TYPED_TEST_CASE_P(
+    PkitsTest11InhibitPolicyMapping,
+    Section11InvalidinhibitPolicyMappingTest1,
+    Section11ValidinhibitPolicyMappingTest2,
+    Section11InvalidinhibitPolicyMappingTest3,
+    Section11ValidinhibitPolicyMappingTest4,
+    Section11InvalidinhibitPolicyMappingTest5,
+    Section11InvalidinhibitPolicyMappingTest6,
+    Section11ValidSelfIssuedinhibitPolicyMappingTest7,
+    Section11InvalidSelfIssuedinhibitPolicyMappingTest8,
+    Section11InvalidSelfIssuedinhibitPolicyMappingTest9,
+    Section11InvalidSelfIssuedinhibitPolicyMappingTest10,
+    Section11InvalidSelfIssuedinhibitPolicyMappingTest11);
+
+template <typename PkitsTestDelegate>
+class PkitsTest12InhibitAnyPolicy : public PkitsTest<PkitsTestDelegate> {};
+TYPED_TEST_CASE_P(PkitsTest12InhibitAnyPolicy);
+
+// 4.12.1 Invalid inhibitAnyPolicy Test1
+WRAPPED_TYPED_TEST_P(PkitsTest12InhibitAnyPolicy,
+                     Section12InvalidinhibitAnyPolicyTest1) {
+  const char* const certs[] = {"TrustAnchorRootCertificate",
+                               "inhibitAnyPolicy0CACert",
+                               "InvalidinhibitAnyPolicyTest1EE"};
+  const char* const crls[] = {"TrustAnchorRootCRL", "inhibitAnyPolicy0CACRL"};
+  ASSERT_FALSE(this->Verify(certs, crls, {}));
+}
+
+// 4.12.2 Valid inhibitAnyPolicy Test2
+WRAPPED_TYPED_TEST_P(PkitsTest12InhibitAnyPolicy,
+                     Section12ValidinhibitAnyPolicyTest2) {
+  const char* const certs[] = {"TrustAnchorRootCertificate",
+                               "inhibitAnyPolicy0CACert",
+                               "ValidinhibitAnyPolicyTest2EE"};
+  const char* const crls[] = {"TrustAnchorRootCRL", "inhibitAnyPolicy0CACRL"};
+  ASSERT_TRUE(this->Verify(certs, crls, {}));
+}
+
+// 4.12.3 inhibitAnyPolicy Test3
+WRAPPED_TYPED_TEST_P(PkitsTest12InhibitAnyPolicy,
+                     Section12inhibitAnyPolicyTest3Subpart1) {
+  const char* const certs[] = {
+      "TrustAnchorRootCertificate", "inhibitAnyPolicy1CACert",
+      "inhibitAnyPolicy1subCA1Cert", "inhibitAnyPolicyTest3EE"};
+  const char* const crls[] = {"TrustAnchorRootCRL", "inhibitAnyPolicy1CACRL",
+                              "inhibitAnyPolicy1subCA1CRL"};
+  ASSERT_TRUE(this->Verify(certs, crls, {}));
+}
+
+// 4.12.3 inhibitAnyPolicy Test3
+WRAPPED_TYPED_TEST_P(PkitsTest12InhibitAnyPolicy,
+                     Section12inhibitAnyPolicyTest3Subpart2) {
+  const char* const certs[] = {
+      "TrustAnchorRootCertificate", "inhibitAnyPolicy1CACert",
+      "inhibitAnyPolicy1subCA1Cert", "inhibitAnyPolicyTest3EE"};
+  const char* const crls[] = {"TrustAnchorRootCRL", "inhibitAnyPolicy1CACRL",
+                              "inhibitAnyPolicy1subCA1CRL"};
+
+  // Custom settings
+  PkitsTestSettings settings;
+  settings.initial_inhibit_any_policy = true;
+
+  ASSERT_FALSE(this->Verify(certs, crls, settings));
+}
+
+// 4.12.4 Invalid inhibitAnyPolicy Test4
+WRAPPED_TYPED_TEST_P(PkitsTest12InhibitAnyPolicy,
+                     Section12InvalidinhibitAnyPolicyTest4) {
+  const char* const certs[] = {
+      "TrustAnchorRootCertificate", "inhibitAnyPolicy1CACert",
+      "inhibitAnyPolicy1subCA1Cert", "InvalidinhibitAnyPolicyTest4EE"};
+  const char* const crls[] = {"TrustAnchorRootCRL", "inhibitAnyPolicy1CACRL",
+                              "inhibitAnyPolicy1subCA1CRL"};
+  ASSERT_FALSE(this->Verify(certs, crls, {}));
+}
+
+// 4.12.5 Invalid inhibitAnyPolicy Test5
+WRAPPED_TYPED_TEST_P(PkitsTest12InhibitAnyPolicy,
+                     Section12InvalidinhibitAnyPolicyTest5) {
+  const char* const certs[] = {
+      "TrustAnchorRootCertificate", "inhibitAnyPolicy5CACert",
+      "inhibitAnyPolicy5subCACert", "inhibitAnyPolicy5subsubCACert",
+      "InvalidinhibitAnyPolicyTest5EE"};
+  const char* const crls[] = {"TrustAnchorRootCRL", "inhibitAnyPolicy5CACRL",
+                              "inhibitAnyPolicy5subCACRL",
+                              "inhibitAnyPolicy5subsubCACRL"};
+  ASSERT_FALSE(this->Verify(certs, crls, {}));
+}
+
+// 4.12.6 Invalid inhibitAnyPolicy Test6
+WRAPPED_TYPED_TEST_P(PkitsTest12InhibitAnyPolicy,
+                     Section12InvalidinhibitAnyPolicyTest6) {
+  const char* const certs[] = {
+      "TrustAnchorRootCertificate", "inhibitAnyPolicy1CACert",
+      "inhibitAnyPolicy1subCAIAP5Cert", "InvalidinhibitAnyPolicyTest6EE"};
+  const char* const crls[] = {"TrustAnchorRootCRL", "inhibitAnyPolicy1CACRL",
+                              "inhibitAnyPolicy1subCAIAP5CRL"};
+  ASSERT_FALSE(this->Verify(certs, crls, {}));
+}
+
+// 4.12.7 Valid Self-Issued inhibitAnyPolicy Test7
+WRAPPED_TYPED_TEST_P(PkitsTest12InhibitAnyPolicy,
+                     Section12ValidSelfIssuedinhibitAnyPolicyTest7) {
+  const char* const certs[] = {
+      "TrustAnchorRootCertificate", "inhibitAnyPolicy1CACert",
+      "inhibitAnyPolicy1SelfIssuedCACert", "inhibitAnyPolicy1subCA2Cert",
+      "ValidSelfIssuedinhibitAnyPolicyTest7EE"};
+  const char* const crls[] = {"TrustAnchorRootCRL", "inhibitAnyPolicy1CACRL",
+                              "inhibitAnyPolicy1subCA2CRL"};
+  ASSERT_TRUE(this->Verify(certs, crls, {}));
+}
+
+// 4.12.8 Invalid Self-Issued inhibitAnyPolicy Test8
+WRAPPED_TYPED_TEST_P(PkitsTest12InhibitAnyPolicy,
+                     Section12InvalidSelfIssuedinhibitAnyPolicyTest8) {
+  const char* const certs[] = {"TrustAnchorRootCertificate",
+                               "inhibitAnyPolicy1CACert",
+                               "inhibitAnyPolicy1SelfIssuedCACert",
+                               "inhibitAnyPolicy1subCA2Cert",
+                               "inhibitAnyPolicy1subsubCA2Cert",
+                               "InvalidSelfIssuedinhibitAnyPolicyTest8EE"};
+  const char* const crls[] = {"TrustAnchorRootCRL", "inhibitAnyPolicy1CACRL",
+                              "inhibitAnyPolicy1subCA2CRL",
+                              "inhibitAnyPolicy1subsubCA2CRL"};
+  ASSERT_FALSE(this->Verify(certs, crls, {}));
+}
+
+// 4.12.9 Valid Self-Issued inhibitAnyPolicy Test9
+WRAPPED_TYPED_TEST_P(PkitsTest12InhibitAnyPolicy,
+                     Section12ValidSelfIssuedinhibitAnyPolicyTest9) {
+  const char* const certs[] = {"TrustAnchorRootCertificate",
+                               "inhibitAnyPolicy1CACert",
+                               "inhibitAnyPolicy1SelfIssuedCACert",
+                               "inhibitAnyPolicy1subCA2Cert",
+                               "inhibitAnyPolicy1SelfIssuedsubCA2Cert",
+                               "ValidSelfIssuedinhibitAnyPolicyTest9EE"};
+  const char* const crls[] = {"TrustAnchorRootCRL", "inhibitAnyPolicy1CACRL",
+                              "inhibitAnyPolicy1subCA2CRL"};
+  ASSERT_TRUE(this->Verify(certs, crls, {}));
+}
+
+// 4.12.10 Invalid Self-Issued inhibitAnyPolicy Test10
+WRAPPED_TYPED_TEST_P(PkitsTest12InhibitAnyPolicy,
+                     Section12InvalidSelfIssuedinhibitAnyPolicyTest10) {
+  const char* const certs[] = {
+      "TrustAnchorRootCertificate", "inhibitAnyPolicy1CACert",
+      "inhibitAnyPolicy1SelfIssuedCACert", "inhibitAnyPolicy1subCA2Cert",
+      "InvalidSelfIssuedinhibitAnyPolicyTest10EE"};
+  const char* const crls[] = {"TrustAnchorRootCRL", "inhibitAnyPolicy1CACRL",
+                              "inhibitAnyPolicy1subCA2CRL"};
+  ASSERT_FALSE(this->Verify(certs, crls, {}));
+}
+
+WRAPPED_REGISTER_TYPED_TEST_CASE_P(
+    PkitsTest12InhibitAnyPolicy,
+    Section12InvalidinhibitAnyPolicyTest1,
+    Section12ValidinhibitAnyPolicyTest2,
+    Section12inhibitAnyPolicyTest3Subpart1,
+    Section12inhibitAnyPolicyTest3Subpart2,
+    Section12InvalidinhibitAnyPolicyTest4,
+    Section12InvalidinhibitAnyPolicyTest5,
+    Section12InvalidinhibitAnyPolicyTest6,
+    Section12ValidSelfIssuedinhibitAnyPolicyTest7,
+    Section12InvalidSelfIssuedinhibitAnyPolicyTest8,
+    Section12ValidSelfIssuedinhibitAnyPolicyTest9,
+    Section12InvalidSelfIssuedinhibitAnyPolicyTest10);
 
 template <typename PkitsTestDelegate>
 class PkitsTest13NameConstraints : public PkitsTest<PkitsTestDelegate> {};
@@ -1051,7 +2264,7 @@
                                "nameConstraintsDN1CACert",
                                "ValidDNnameConstraintsTest1EE"};
   const char* const crls[] = {"TrustAnchorRootCRL", "nameConstraintsDN1CACRL"};
-  ASSERT_TRUE(this->Verify(certs, crls));
+  ASSERT_TRUE(this->Verify(certs, crls, {}));
 }
 
 // 4.13.2 Invalid DN nameConstraints Test2
@@ -1061,7 +2274,7 @@
                                "nameConstraintsDN1CACert",
                                "InvalidDNnameConstraintsTest2EE"};
   const char* const crls[] = {"TrustAnchorRootCRL", "nameConstraintsDN1CACRL"};
-  ASSERT_FALSE(this->Verify(certs, crls));
+  ASSERT_FALSE(this->Verify(certs, crls, {}));
 }
 
 // 4.13.3 Invalid DN nameConstraints Test3
@@ -1071,7 +2284,7 @@
                                "nameConstraintsDN1CACert",
                                "InvalidDNnameConstraintsTest3EE"};
   const char* const crls[] = {"TrustAnchorRootCRL", "nameConstraintsDN1CACRL"};
-  ASSERT_FALSE(this->Verify(certs, crls));
+  ASSERT_FALSE(this->Verify(certs, crls, {}));
 }
 
 // 4.13.4 Valid DN nameConstraints Test4
@@ -1081,7 +2294,7 @@
                                "nameConstraintsDN1CACert",
                                "ValidDNnameConstraintsTest4EE"};
   const char* const crls[] = {"TrustAnchorRootCRL", "nameConstraintsDN1CACRL"};
-  ASSERT_TRUE(this->Verify(certs, crls));
+  ASSERT_TRUE(this->Verify(certs, crls, {}));
 }
 
 // 4.13.5 Valid DN nameConstraints Test5
@@ -1091,7 +2304,7 @@
                                "nameConstraintsDN2CACert",
                                "ValidDNnameConstraintsTest5EE"};
   const char* const crls[] = {"TrustAnchorRootCRL", "nameConstraintsDN2CACRL"};
-  ASSERT_TRUE(this->Verify(certs, crls));
+  ASSERT_TRUE(this->Verify(certs, crls, {}));
 }
 
 // 4.13.6 Valid DN nameConstraints Test6
@@ -1101,7 +2314,7 @@
                                "nameConstraintsDN3CACert",
                                "ValidDNnameConstraintsTest6EE"};
   const char* const crls[] = {"TrustAnchorRootCRL", "nameConstraintsDN3CACRL"};
-  ASSERT_TRUE(this->Verify(certs, crls));
+  ASSERT_TRUE(this->Verify(certs, crls, {}));
 }
 
 // 4.13.7 Invalid DN nameConstraints Test7
@@ -1111,7 +2324,7 @@
                                "nameConstraintsDN3CACert",
                                "InvalidDNnameConstraintsTest7EE"};
   const char* const crls[] = {"TrustAnchorRootCRL", "nameConstraintsDN3CACRL"};
-  ASSERT_FALSE(this->Verify(certs, crls));
+  ASSERT_FALSE(this->Verify(certs, crls, {}));
 }
 
 // 4.13.8 Invalid DN nameConstraints Test8
@@ -1121,7 +2334,7 @@
                                "nameConstraintsDN4CACert",
                                "InvalidDNnameConstraintsTest8EE"};
   const char* const crls[] = {"TrustAnchorRootCRL", "nameConstraintsDN4CACRL"};
-  ASSERT_FALSE(this->Verify(certs, crls));
+  ASSERT_FALSE(this->Verify(certs, crls, {}));
 }
 
 // 4.13.9 Invalid DN nameConstraints Test9
@@ -1131,7 +2344,7 @@
                                "nameConstraintsDN4CACert",
                                "InvalidDNnameConstraintsTest9EE"};
   const char* const crls[] = {"TrustAnchorRootCRL", "nameConstraintsDN4CACRL"};
-  ASSERT_FALSE(this->Verify(certs, crls));
+  ASSERT_FALSE(this->Verify(certs, crls, {}));
 }
 
 // 4.13.10 Invalid DN nameConstraints Test10
@@ -1141,7 +2354,7 @@
                                "nameConstraintsDN5CACert",
                                "InvalidDNnameConstraintsTest10EE"};
   const char* const crls[] = {"TrustAnchorRootCRL", "nameConstraintsDN5CACRL"};
-  ASSERT_FALSE(this->Verify(certs, crls));
+  ASSERT_FALSE(this->Verify(certs, crls, {}));
 }
 
 // 4.13.11 Valid DN nameConstraints Test11
@@ -1151,7 +2364,7 @@
                                "nameConstraintsDN5CACert",
                                "ValidDNnameConstraintsTest11EE"};
   const char* const crls[] = {"TrustAnchorRootCRL", "nameConstraintsDN5CACRL"};
-  ASSERT_TRUE(this->Verify(certs, crls));
+  ASSERT_TRUE(this->Verify(certs, crls, {}));
 }
 
 // 4.13.12 Invalid DN nameConstraints Test12
@@ -1162,7 +2375,7 @@
       "nameConstraintsDN1subCA1Cert", "InvalidDNnameConstraintsTest12EE"};
   const char* const crls[] = {"TrustAnchorRootCRL", "nameConstraintsDN1CACRL",
                               "nameConstraintsDN1subCA1CRL"};
-  ASSERT_FALSE(this->Verify(certs, crls));
+  ASSERT_FALSE(this->Verify(certs, crls, {}));
 }
 
 // 4.13.13 Invalid DN nameConstraints Test13
@@ -1173,7 +2386,7 @@
       "nameConstraintsDN1subCA2Cert", "InvalidDNnameConstraintsTest13EE"};
   const char* const crls[] = {"TrustAnchorRootCRL", "nameConstraintsDN1CACRL",
                               "nameConstraintsDN1subCA2CRL"};
-  ASSERT_FALSE(this->Verify(certs, crls));
+  ASSERT_FALSE(this->Verify(certs, crls, {}));
 }
 
 // 4.13.14 Valid DN nameConstraints Test14
@@ -1184,7 +2397,7 @@
       "nameConstraintsDN1subCA2Cert", "ValidDNnameConstraintsTest14EE"};
   const char* const crls[] = {"TrustAnchorRootCRL", "nameConstraintsDN1CACRL",
                               "nameConstraintsDN1subCA2CRL"};
-  ASSERT_TRUE(this->Verify(certs, crls));
+  ASSERT_TRUE(this->Verify(certs, crls, {}));
 }
 
 // 4.13.15 Invalid DN nameConstraints Test15
@@ -1195,7 +2408,7 @@
       "nameConstraintsDN3subCA1Cert", "InvalidDNnameConstraintsTest15EE"};
   const char* const crls[] = {"TrustAnchorRootCRL", "nameConstraintsDN3CACRL",
                               "nameConstraintsDN3subCA1CRL"};
-  ASSERT_FALSE(this->Verify(certs, crls));
+  ASSERT_FALSE(this->Verify(certs, crls, {}));
 }
 
 // 4.13.16 Invalid DN nameConstraints Test16
@@ -1206,7 +2419,7 @@
       "nameConstraintsDN3subCA1Cert", "InvalidDNnameConstraintsTest16EE"};
   const char* const crls[] = {"TrustAnchorRootCRL", "nameConstraintsDN3CACRL",
                               "nameConstraintsDN3subCA1CRL"};
-  ASSERT_FALSE(this->Verify(certs, crls));
+  ASSERT_FALSE(this->Verify(certs, crls, {}));
 }
 
 // 4.13.17 Invalid DN nameConstraints Test17
@@ -1217,7 +2430,7 @@
       "nameConstraintsDN3subCA2Cert", "InvalidDNnameConstraintsTest17EE"};
   const char* const crls[] = {"TrustAnchorRootCRL", "nameConstraintsDN3CACRL",
                               "nameConstraintsDN3subCA2CRL"};
-  ASSERT_FALSE(this->Verify(certs, crls));
+  ASSERT_FALSE(this->Verify(certs, crls, {}));
 }
 
 // 4.13.18 Valid DN nameConstraints Test18
@@ -1228,7 +2441,7 @@
       "nameConstraintsDN3subCA2Cert", "ValidDNnameConstraintsTest18EE"};
   const char* const crls[] = {"TrustAnchorRootCRL", "nameConstraintsDN3CACRL",
                               "nameConstraintsDN3subCA2CRL"};
-  ASSERT_TRUE(this->Verify(certs, crls));
+  ASSERT_TRUE(this->Verify(certs, crls, {}));
 }
 
 // 4.13.19 Valid Self-Issued DN nameConstraints Test19
@@ -1238,7 +2451,7 @@
       "TrustAnchorRootCertificate", "nameConstraintsDN1CACert",
       "nameConstraintsDN1SelfIssuedCACert", "ValidDNnameConstraintsTest19EE"};
   const char* const crls[] = {"TrustAnchorRootCRL", "nameConstraintsDN1CACRL"};
-  ASSERT_TRUE(this->Verify(certs, crls));
+  ASSERT_TRUE(this->Verify(certs, crls, {}));
 }
 
 // 4.13.20 Invalid Self-Issued DN nameConstraints Test20
@@ -1248,7 +2461,7 @@
                                "nameConstraintsDN1CACert",
                                "InvalidDNnameConstraintsTest20EE"};
   const char* const crls[] = {"TrustAnchorRootCRL", "nameConstraintsDN1CACRL"};
-  ASSERT_FALSE(this->Verify(certs, crls));
+  ASSERT_FALSE(this->Verify(certs, crls, {}));
 }
 
 // 4.13.21 Valid RFC822 nameConstraints Test21
@@ -1259,7 +2472,7 @@
                                "ValidRFC822nameConstraintsTest21EE"};
   const char* const crls[] = {"TrustAnchorRootCRL",
                               "nameConstraintsRFC822CA1CRL"};
-  ASSERT_TRUE(this->Verify(certs, crls));
+  ASSERT_TRUE(this->Verify(certs, crls, {}));
 }
 
 // 4.13.22 Invalid RFC822 nameConstraints Test22
@@ -1270,7 +2483,7 @@
                                "InvalidRFC822nameConstraintsTest22EE"};
   const char* const crls[] = {"TrustAnchorRootCRL",
                               "nameConstraintsRFC822CA1CRL"};
-  ASSERT_FALSE(this->Verify(certs, crls));
+  ASSERT_FALSE(this->Verify(certs, crls, {}));
 }
 
 // 4.13.23 Valid RFC822 nameConstraints Test23
@@ -1281,7 +2494,7 @@
                                "ValidRFC822nameConstraintsTest23EE"};
   const char* const crls[] = {"TrustAnchorRootCRL",
                               "nameConstraintsRFC822CA2CRL"};
-  ASSERT_TRUE(this->Verify(certs, crls));
+  ASSERT_TRUE(this->Verify(certs, crls, {}));
 }
 
 // 4.13.24 Invalid RFC822 nameConstraints Test24
@@ -1292,7 +2505,7 @@
                                "InvalidRFC822nameConstraintsTest24EE"};
   const char* const crls[] = {"TrustAnchorRootCRL",
                               "nameConstraintsRFC822CA2CRL"};
-  ASSERT_FALSE(this->Verify(certs, crls));
+  ASSERT_FALSE(this->Verify(certs, crls, {}));
 }
 
 // 4.13.25 Valid RFC822 nameConstraints Test25
@@ -1303,7 +2516,7 @@
                                "ValidRFC822nameConstraintsTest25EE"};
   const char* const crls[] = {"TrustAnchorRootCRL",
                               "nameConstraintsRFC822CA3CRL"};
-  ASSERT_TRUE(this->Verify(certs, crls));
+  ASSERT_TRUE(this->Verify(certs, crls, {}));
 }
 
 // 4.13.26 Invalid RFC822 nameConstraints Test26
@@ -1314,7 +2527,7 @@
                                "InvalidRFC822nameConstraintsTest26EE"};
   const char* const crls[] = {"TrustAnchorRootCRL",
                               "nameConstraintsRFC822CA3CRL"};
-  ASSERT_FALSE(this->Verify(certs, crls));
+  ASSERT_FALSE(this->Verify(certs, crls, {}));
 }
 
 // 4.13.27 Valid DN and RFC822 nameConstraints Test27
@@ -1326,7 +2539,7 @@
                                "ValidDNandRFC822nameConstraintsTest27EE"};
   const char* const crls[] = {"TrustAnchorRootCRL", "nameConstraintsDN1CACRL",
                               "nameConstraintsDN1subCA3CRL"};
-  ASSERT_TRUE(this->Verify(certs, crls));
+  ASSERT_TRUE(this->Verify(certs, crls, {}));
 }
 
 // 4.13.28 Invalid DN and RFC822 nameConstraints Test28
@@ -1338,7 +2551,7 @@
                                "InvalidDNandRFC822nameConstraintsTest28EE"};
   const char* const crls[] = {"TrustAnchorRootCRL", "nameConstraintsDN1CACRL",
                               "nameConstraintsDN1subCA3CRL"};
-  ASSERT_FALSE(this->Verify(certs, crls));
+  ASSERT_FALSE(this->Verify(certs, crls, {}));
 }
 
 // 4.13.29 Invalid DN and RFC822 nameConstraints Test29
@@ -1350,7 +2563,7 @@
                                "InvalidDNandRFC822nameConstraintsTest29EE"};
   const char* const crls[] = {"TrustAnchorRootCRL", "nameConstraintsDN1CACRL",
                               "nameConstraintsDN1subCA3CRL"};
-  ASSERT_FALSE(this->Verify(certs, crls));
+  ASSERT_FALSE(this->Verify(certs, crls, {}));
 }
 
 // 4.13.30 Valid DNS nameConstraints Test30
@@ -1360,7 +2573,7 @@
                                "nameConstraintsDNS1CACert",
                                "ValidDNSnameConstraintsTest30EE"};
   const char* const crls[] = {"TrustAnchorRootCRL", "nameConstraintsDNS1CACRL"};
-  ASSERT_TRUE(this->Verify(certs, crls));
+  ASSERT_TRUE(this->Verify(certs, crls, {}));
 }
 
 // 4.13.31 Invalid DNS nameConstraints Test31
@@ -1370,7 +2583,7 @@
                                "nameConstraintsDNS1CACert",
                                "InvalidDNSnameConstraintsTest31EE"};
   const char* const crls[] = {"TrustAnchorRootCRL", "nameConstraintsDNS1CACRL"};
-  ASSERT_FALSE(this->Verify(certs, crls));
+  ASSERT_FALSE(this->Verify(certs, crls, {}));
 }
 
 // 4.13.32 Valid DNS nameConstraints Test32
@@ -1380,7 +2593,7 @@
                                "nameConstraintsDNS2CACert",
                                "ValidDNSnameConstraintsTest32EE"};
   const char* const crls[] = {"TrustAnchorRootCRL", "nameConstraintsDNS2CACRL"};
-  ASSERT_TRUE(this->Verify(certs, crls));
+  ASSERT_TRUE(this->Verify(certs, crls, {}));
 }
 
 // 4.13.33 Invalid DNS nameConstraints Test33
@@ -1390,7 +2603,7 @@
                                "nameConstraintsDNS2CACert",
                                "InvalidDNSnameConstraintsTest33EE"};
   const char* const crls[] = {"TrustAnchorRootCRL", "nameConstraintsDNS2CACRL"};
-  ASSERT_FALSE(this->Verify(certs, crls));
+  ASSERT_FALSE(this->Verify(certs, crls, {}));
 }
 
 // 4.13.34 Valid URI nameConstraints Test34
@@ -1400,7 +2613,7 @@
                                "nameConstraintsURI1CACert",
                                "ValidURInameConstraintsTest34EE"};
   const char* const crls[] = {"TrustAnchorRootCRL", "nameConstraintsURI1CACRL"};
-  ASSERT_TRUE(this->Verify(certs, crls));
+  ASSERT_TRUE(this->Verify(certs, crls, {}));
 }
 
 // 4.13.35 Invalid URI nameConstraints Test35
@@ -1410,7 +2623,7 @@
                                "nameConstraintsURI1CACert",
                                "InvalidURInameConstraintsTest35EE"};
   const char* const crls[] = {"TrustAnchorRootCRL", "nameConstraintsURI1CACRL"};
-  ASSERT_FALSE(this->Verify(certs, crls));
+  ASSERT_FALSE(this->Verify(certs, crls, {}));
 }
 
 // 4.13.36 Valid URI nameConstraints Test36
@@ -1420,7 +2633,7 @@
                                "nameConstraintsURI2CACert",
                                "ValidURInameConstraintsTest36EE"};
   const char* const crls[] = {"TrustAnchorRootCRL", "nameConstraintsURI2CACRL"};
-  ASSERT_TRUE(this->Verify(certs, crls));
+  ASSERT_TRUE(this->Verify(certs, crls, {}));
 }
 
 // 4.13.37 Invalid URI nameConstraints Test37
@@ -1430,7 +2643,7 @@
                                "nameConstraintsURI2CACert",
                                "InvalidURInameConstraintsTest37EE"};
   const char* const crls[] = {"TrustAnchorRootCRL", "nameConstraintsURI2CACRL"};
-  ASSERT_FALSE(this->Verify(certs, crls));
+  ASSERT_FALSE(this->Verify(certs, crls, {}));
 }
 
 // 4.13.38 Invalid DNS nameConstraints Test38
@@ -1440,7 +2653,7 @@
                                "nameConstraintsDNS1CACert",
                                "InvalidDNSnameConstraintsTest38EE"};
   const char* const crls[] = {"TrustAnchorRootCRL", "nameConstraintsDNS1CACRL"};
-  ASSERT_FALSE(this->Verify(certs, crls));
+  ASSERT_FALSE(this->Verify(certs, crls, {}));
 }
 
 WRAPPED_REGISTER_TYPED_TEST_CASE_P(
@@ -1495,7 +2708,7 @@
                                "distributionPoint1CACert",
                                "ValiddistributionPointTest1EE"};
   const char* const crls[] = {"TrustAnchorRootCRL", "distributionPoint1CACRL"};
-  ASSERT_TRUE(this->Verify(certs, crls));
+  ASSERT_TRUE(this->Verify(certs, crls, {}));
 }
 
 // 4.14.2 Invalid distributionPoint Test2
@@ -1505,7 +2718,7 @@
                                "distributionPoint1CACert",
                                "InvaliddistributionPointTest2EE"};
   const char* const crls[] = {"TrustAnchorRootCRL", "distributionPoint1CACRL"};
-  ASSERT_FALSE(this->Verify(certs, crls));
+  ASSERT_FALSE(this->Verify(certs, crls, {}));
 }
 
 // 4.14.3 Invalid distributionPoint Test3
@@ -1515,7 +2728,7 @@
                                "distributionPoint1CACert",
                                "InvaliddistributionPointTest3EE"};
   const char* const crls[] = {"TrustAnchorRootCRL", "distributionPoint1CACRL"};
-  ASSERT_FALSE(this->Verify(certs, crls));
+  ASSERT_FALSE(this->Verify(certs, crls, {}));
 }
 
 // 4.14.4 Valid distributionPoint Test4
@@ -1525,7 +2738,7 @@
                                "distributionPoint1CACert",
                                "ValiddistributionPointTest4EE"};
   const char* const crls[] = {"TrustAnchorRootCRL", "distributionPoint1CACRL"};
-  ASSERT_TRUE(this->Verify(certs, crls));
+  ASSERT_TRUE(this->Verify(certs, crls, {}));
 }
 
 // 4.14.5 Valid distributionPoint Test5
@@ -1535,7 +2748,7 @@
                                "distributionPoint2CACert",
                                "ValiddistributionPointTest5EE"};
   const char* const crls[] = {"TrustAnchorRootCRL", "distributionPoint2CACRL"};
-  ASSERT_TRUE(this->Verify(certs, crls));
+  ASSERT_TRUE(this->Verify(certs, crls, {}));
 }
 
 // 4.14.6 Invalid distributionPoint Test6
@@ -1545,7 +2758,7 @@
                                "distributionPoint2CACert",
                                "InvaliddistributionPointTest6EE"};
   const char* const crls[] = {"TrustAnchorRootCRL", "distributionPoint2CACRL"};
-  ASSERT_FALSE(this->Verify(certs, crls));
+  ASSERT_FALSE(this->Verify(certs, crls, {}));
 }
 
 // 4.14.7 Valid distributionPoint Test7
@@ -1555,7 +2768,7 @@
                                "distributionPoint2CACert",
                                "ValiddistributionPointTest7EE"};
   const char* const crls[] = {"TrustAnchorRootCRL", "distributionPoint2CACRL"};
-  ASSERT_TRUE(this->Verify(certs, crls));
+  ASSERT_TRUE(this->Verify(certs, crls, {}));
 }
 
 // 4.14.8 Invalid distributionPoint Test8
@@ -1565,7 +2778,7 @@
                                "distributionPoint2CACert",
                                "InvaliddistributionPointTest8EE"};
   const char* const crls[] = {"TrustAnchorRootCRL", "distributionPoint2CACRL"};
-  ASSERT_FALSE(this->Verify(certs, crls));
+  ASSERT_FALSE(this->Verify(certs, crls, {}));
 }
 
 // 4.14.9 Invalid distributionPoint Test9
@@ -1575,7 +2788,7 @@
                                "distributionPoint2CACert",
                                "InvaliddistributionPointTest9EE"};
   const char* const crls[] = {"TrustAnchorRootCRL", "distributionPoint2CACRL"};
-  ASSERT_FALSE(this->Verify(certs, crls));
+  ASSERT_FALSE(this->Verify(certs, crls, {}));
 }
 
 // 4.14.10 Valid No issuingDistributionPoint Test10
@@ -1586,7 +2799,7 @@
                                "ValidNoissuingDistributionPointTest10EE"};
   const char* const crls[] = {"TrustAnchorRootCRL",
                               "NoissuingDistributionPointCACRL"};
-  ASSERT_TRUE(this->Verify(certs, crls));
+  ASSERT_TRUE(this->Verify(certs, crls, {}));
 }
 
 // 4.14.11 Invalid onlyContainsUserCerts CRL Test11
@@ -1597,7 +2810,7 @@
                                "InvalidonlyContainsUserCertsTest11EE"};
   const char* const crls[] = {"TrustAnchorRootCRL",
                               "onlyContainsUserCertsCACRL"};
-  ASSERT_FALSE(this->Verify(certs, crls));
+  ASSERT_FALSE(this->Verify(certs, crls, {}));
 }
 
 // 4.14.12 Invalid onlyContainsCACerts CRL Test12
@@ -1607,7 +2820,7 @@
                                "onlyContainsCACertsCACert",
                                "InvalidonlyContainsCACertsTest12EE"};
   const char* const crls[] = {"TrustAnchorRootCRL", "onlyContainsCACertsCACRL"};
-  ASSERT_FALSE(this->Verify(certs, crls));
+  ASSERT_FALSE(this->Verify(certs, crls, {}));
 }
 
 // 4.14.13 Valid onlyContainsCACerts CRL Test13
@@ -1617,7 +2830,7 @@
                                "onlyContainsCACertsCACert",
                                "ValidonlyContainsCACertsTest13EE"};
   const char* const crls[] = {"TrustAnchorRootCRL", "onlyContainsCACertsCACRL"};
-  ASSERT_TRUE(this->Verify(certs, crls));
+  ASSERT_TRUE(this->Verify(certs, crls, {}));
 }
 
 // 4.14.14 Invalid onlyContainsAttributeCerts Test14
@@ -1628,7 +2841,7 @@
                                "InvalidonlyContainsAttributeCertsTest14EE"};
   const char* const crls[] = {"TrustAnchorRootCRL",
                               "onlyContainsAttributeCertsCACRL"};
-  ASSERT_FALSE(this->Verify(certs, crls));
+  ASSERT_FALSE(this->Verify(certs, crls, {}));
 }
 
 // 4.14.15 Invalid onlySomeReasons Test15
@@ -1640,7 +2853,7 @@
   const char* const crls[] = {"TrustAnchorRootCRL",
                               "onlySomeReasonsCA1compromiseCRL",
                               "onlySomeReasonsCA1otherreasonsCRL"};
-  ASSERT_FALSE(this->Verify(certs, crls));
+  ASSERT_FALSE(this->Verify(certs, crls, {}));
 }
 
 // 4.14.16 Invalid onlySomeReasons Test16
@@ -1652,7 +2865,7 @@
   const char* const crls[] = {"TrustAnchorRootCRL",
                               "onlySomeReasonsCA1compromiseCRL",
                               "onlySomeReasonsCA1otherreasonsCRL"};
-  ASSERT_FALSE(this->Verify(certs, crls));
+  ASSERT_FALSE(this->Verify(certs, crls, {}));
 }
 
 // 4.14.17 Invalid onlySomeReasons Test17
@@ -1663,7 +2876,7 @@
                                "InvalidonlySomeReasonsTest17EE"};
   const char* const crls[] = {"TrustAnchorRootCRL", "onlySomeReasonsCA2CRL1",
                               "onlySomeReasonsCA2CRL2"};
-  ASSERT_FALSE(this->Verify(certs, crls));
+  ASSERT_FALSE(this->Verify(certs, crls, {}));
 }
 
 // 4.14.18 Valid onlySomeReasons Test18
@@ -1675,7 +2888,7 @@
   const char* const crls[] = {"TrustAnchorRootCRL",
                               "onlySomeReasonsCA3compromiseCRL",
                               "onlySomeReasonsCA3otherreasonsCRL"};
-  ASSERT_TRUE(this->Verify(certs, crls));
+  ASSERT_TRUE(this->Verify(certs, crls, {}));
 }
 
 // 4.14.19 Valid onlySomeReasons Test19
@@ -1687,7 +2900,7 @@
   const char* const crls[] = {"TrustAnchorRootCRL",
                               "onlySomeReasonsCA4compromiseCRL",
                               "onlySomeReasonsCA4otherreasonsCRL"};
-  ASSERT_TRUE(this->Verify(certs, crls));
+  ASSERT_TRUE(this->Verify(certs, crls, {}));
 }
 
 // 4.14.20 Invalid onlySomeReasons Test20
@@ -1699,7 +2912,7 @@
   const char* const crls[] = {"TrustAnchorRootCRL",
                               "onlySomeReasonsCA4compromiseCRL",
                               "onlySomeReasonsCA4otherreasonsCRL"};
-  ASSERT_FALSE(this->Verify(certs, crls));
+  ASSERT_FALSE(this->Verify(certs, crls, {}));
 }
 
 // 4.14.21 Invalid onlySomeReasons Test21
@@ -1711,7 +2924,7 @@
   const char* const crls[] = {"TrustAnchorRootCRL",
                               "onlySomeReasonsCA4compromiseCRL",
                               "onlySomeReasonsCA4otherreasonsCRL"};
-  ASSERT_FALSE(this->Verify(certs, crls));
+  ASSERT_FALSE(this->Verify(certs, crls, {}));
 }
 
 // 4.14.22 Valid IDP with indirectCRL Test22
@@ -1721,7 +2934,7 @@
                                "indirectCRLCA1Cert",
                                "ValidIDPwithindirectCRLTest22EE"};
   const char* const crls[] = {"TrustAnchorRootCRL", "indirectCRLCA1CRL"};
-  ASSERT_TRUE(this->Verify(certs, crls));
+  ASSERT_TRUE(this->Verify(certs, crls, {}));
 }
 
 // 4.14.23 Invalid IDP with indirectCRL Test23
@@ -1731,7 +2944,7 @@
                                "indirectCRLCA1Cert",
                                "InvalidIDPwithindirectCRLTest23EE"};
   const char* const crls[] = {"TrustAnchorRootCRL", "indirectCRLCA1CRL"};
-  ASSERT_FALSE(this->Verify(certs, crls));
+  ASSERT_FALSE(this->Verify(certs, crls, {}));
 }
 
 // 4.14.24 Valid IDP with indirectCRL Test24
@@ -1741,7 +2954,7 @@
                                "indirectCRLCA2Cert", "indirectCRLCA1Cert",
                                "ValidIDPwithindirectCRLTest24EE"};
   const char* const crls[] = {"TrustAnchorRootCRL", "indirectCRLCA1CRL"};
-  ASSERT_TRUE(this->Verify(certs, crls));
+  ASSERT_TRUE(this->Verify(certs, crls, {}));
 }
 
 // 4.14.25 Valid IDP with indirectCRL Test25
@@ -1751,7 +2964,7 @@
                                "indirectCRLCA2Cert", "indirectCRLCA1Cert",
                                "ValidIDPwithindirectCRLTest25EE"};
   const char* const crls[] = {"TrustAnchorRootCRL", "indirectCRLCA1CRL"};
-  ASSERT_TRUE(this->Verify(certs, crls));
+  ASSERT_TRUE(this->Verify(certs, crls, {}));
 }
 
 // 4.14.26 Invalid IDP with indirectCRL Test26
@@ -1761,7 +2974,7 @@
                                "indirectCRLCA2Cert", "indirectCRLCA1Cert",
                                "InvalidIDPwithindirectCRLTest26EE"};
   const char* const crls[] = {"TrustAnchorRootCRL", "indirectCRLCA1CRL"};
-  ASSERT_FALSE(this->Verify(certs, crls));
+  ASSERT_FALSE(this->Verify(certs, crls, {}));
 }
 
 // 4.14.27 Invalid cRLIssuer Test27
@@ -1771,7 +2984,7 @@
                                "indirectCRLCA2Cert", "GoodCACert",
                                "InvalidcRLIssuerTest27EE"};
   const char* const crls[] = {"TrustAnchorRootCRL", "GoodCACRL"};
-  ASSERT_FALSE(this->Verify(certs, crls));
+  ASSERT_FALSE(this->Verify(certs, crls, {}));
 }
 
 // 4.14.28 Valid cRLIssuer Test28
@@ -1782,7 +2995,7 @@
       "indirectCRLCA3cRLIssuerCert", "ValidcRLIssuerTest28EE"};
   const char* const crls[] = {"TrustAnchorRootCRL", "indirectCRLCA3CRL",
                               "indirectCRLCA3cRLIssuerCRL"};
-  ASSERT_TRUE(this->Verify(certs, crls));
+  ASSERT_TRUE(this->Verify(certs, crls, {}));
 }
 
 // 4.14.29 Valid cRLIssuer Test29
@@ -1793,7 +3006,7 @@
       "indirectCRLCA3cRLIssuerCert", "ValidcRLIssuerTest29EE"};
   const char* const crls[] = {"TrustAnchorRootCRL", "indirectCRLCA3CRL",
                               "indirectCRLCA3cRLIssuerCRL"};
-  ASSERT_TRUE(this->Verify(certs, crls));
+  ASSERT_TRUE(this->Verify(certs, crls, {}));
 }
 
 // 4.14.30 Valid cRLIssuer Test30
@@ -1804,7 +3017,7 @@
       "indirectCRLCA4cRLIssuerCert", "ValidcRLIssuerTest30EE"};
   const char* const crls[] = {"TrustAnchorRootCRL",
                               "indirectCRLCA4cRLIssuerCRL"};
-  ASSERT_TRUE(this->Verify(certs, crls));
+  ASSERT_TRUE(this->Verify(certs, crls, {}));
 }
 
 // 4.14.31 Invalid cRLIssuer Test31
@@ -1814,7 +3027,7 @@
                                "indirectCRLCA5Cert", "indirectCRLCA6Cert",
                                "InvalidcRLIssuerTest31EE"};
   const char* const crls[] = {"TrustAnchorRootCRL", "indirectCRLCA5CRL"};
-  ASSERT_FALSE(this->Verify(certs, crls));
+  ASSERT_FALSE(this->Verify(certs, crls, {}));
 }
 
 // 4.14.32 Invalid cRLIssuer Test32
@@ -1824,7 +3037,7 @@
                                "indirectCRLCA5Cert", "indirectCRLCA6Cert",
                                "InvalidcRLIssuerTest32EE"};
   const char* const crls[] = {"TrustAnchorRootCRL", "indirectCRLCA5CRL"};
-  ASSERT_FALSE(this->Verify(certs, crls));
+  ASSERT_FALSE(this->Verify(certs, crls, {}));
 }
 
 // 4.14.33 Valid cRLIssuer Test33
@@ -1834,7 +3047,7 @@
                                "indirectCRLCA5Cert", "indirectCRLCA6Cert",
                                "ValidcRLIssuerTest33EE"};
   const char* const crls[] = {"TrustAnchorRootCRL", "indirectCRLCA5CRL"};
-  ASSERT_TRUE(this->Verify(certs, crls));
+  ASSERT_TRUE(this->Verify(certs, crls, {}));
 }
 
 // 4.14.34 Invalid cRLIssuer Test34
@@ -1844,7 +3057,7 @@
                                "indirectCRLCA5Cert",
                                "InvalidcRLIssuerTest34EE"};
   const char* const crls[] = {"TrustAnchorRootCRL", "indirectCRLCA5CRL"};
-  ASSERT_FALSE(this->Verify(certs, crls));
+  ASSERT_FALSE(this->Verify(certs, crls, {}));
 }
 
 // 4.14.35 Invalid cRLIssuer Test35
@@ -1854,7 +3067,7 @@
                                "indirectCRLCA5Cert",
                                "InvalidcRLIssuerTest35EE"};
   const char* const crls[] = {"TrustAnchorRootCRL", "indirectCRLCA5CRL"};
-  ASSERT_FALSE(this->Verify(certs, crls));
+  ASSERT_FALSE(this->Verify(certs, crls, {}));
 }
 
 WRAPPED_REGISTER_TYPED_TEST_CASE_P(
@@ -1907,7 +3120,7 @@
                                "InvaliddeltaCRLIndicatorNoBaseTest1EE"};
   const char* const crls[] = {"TrustAnchorRootCRL",
                               "deltaCRLIndicatorNoBaseCACRL"};
-  ASSERT_FALSE(this->Verify(certs, crls));
+  ASSERT_FALSE(this->Verify(certs, crls, {}));
 }
 
 // 4.15.2 Valid delta-CRL Test2
@@ -1916,7 +3129,7 @@
                                "ValiddeltaCRLTest2EE"};
   const char* const crls[] = {"TrustAnchorRootCRL", "deltaCRLCA1CRL",
                               "deltaCRLCA1deltaCRL"};
-  ASSERT_TRUE(this->Verify(certs, crls));
+  ASSERT_TRUE(this->Verify(certs, crls, {}));
 }
 
 // 4.15.3 Invalid delta-CRL Test3
@@ -1925,7 +3138,7 @@
                                "InvaliddeltaCRLTest3EE"};
   const char* const crls[] = {"TrustAnchorRootCRL", "deltaCRLCA1CRL",
                               "deltaCRLCA1deltaCRL"};
-  ASSERT_FALSE(this->Verify(certs, crls));
+  ASSERT_FALSE(this->Verify(certs, crls, {}));
 }
 
 // 4.15.4 Invalid delta-CRL Test4
@@ -1934,7 +3147,7 @@
                                "InvaliddeltaCRLTest4EE"};
   const char* const crls[] = {"TrustAnchorRootCRL", "deltaCRLCA1CRL",
                               "deltaCRLCA1deltaCRL"};
-  ASSERT_FALSE(this->Verify(certs, crls));
+  ASSERT_FALSE(this->Verify(certs, crls, {}));
 }
 
 // 4.15.5 Valid delta-CRL Test5
@@ -1943,7 +3156,7 @@
                                "ValiddeltaCRLTest5EE"};
   const char* const crls[] = {"TrustAnchorRootCRL", "deltaCRLCA1CRL",
                               "deltaCRLCA1deltaCRL"};
-  ASSERT_TRUE(this->Verify(certs, crls));
+  ASSERT_TRUE(this->Verify(certs, crls, {}));
 }
 
 // 4.15.6 Invalid delta-CRL Test6
@@ -1952,7 +3165,7 @@
                                "InvaliddeltaCRLTest6EE"};
   const char* const crls[] = {"TrustAnchorRootCRL", "deltaCRLCA1CRL",
                               "deltaCRLCA1deltaCRL"};
-  ASSERT_FALSE(this->Verify(certs, crls));
+  ASSERT_FALSE(this->Verify(certs, crls, {}));
 }
 
 // 4.15.7 Valid delta-CRL Test7
@@ -1961,7 +3174,7 @@
                                "ValiddeltaCRLTest7EE"};
   const char* const crls[] = {"TrustAnchorRootCRL", "deltaCRLCA1CRL",
                               "deltaCRLCA1deltaCRL"};
-  ASSERT_TRUE(this->Verify(certs, crls));
+  ASSERT_TRUE(this->Verify(certs, crls, {}));
 }
 
 // 4.15.8 Valid delta-CRL Test8
@@ -1970,7 +3183,7 @@
                                "ValiddeltaCRLTest8EE"};
   const char* const crls[] = {"TrustAnchorRootCRL", "deltaCRLCA2CRL",
                               "deltaCRLCA2deltaCRL"};
-  ASSERT_TRUE(this->Verify(certs, crls));
+  ASSERT_TRUE(this->Verify(certs, crls, {}));
 }
 
 // 4.15.9 Invalid delta-CRL Test9
@@ -1979,7 +3192,7 @@
                                "InvaliddeltaCRLTest9EE"};
   const char* const crls[] = {"TrustAnchorRootCRL", "deltaCRLCA2CRL",
                               "deltaCRLCA2deltaCRL"};
-  ASSERT_FALSE(this->Verify(certs, crls));
+  ASSERT_FALSE(this->Verify(certs, crls, {}));
 }
 
 // 4.15.10 Invalid delta-CRL Test10
@@ -1988,7 +3201,7 @@
                                "InvaliddeltaCRLTest10EE"};
   const char* const crls[] = {"TrustAnchorRootCRL", "deltaCRLCA3CRL",
                               "deltaCRLCA3deltaCRL"};
-  ASSERT_FALSE(this->Verify(certs, crls));
+  ASSERT_FALSE(this->Verify(certs, crls, {}));
 }
 
 WRAPPED_REGISTER_TYPED_TEST_CASE_P(PkitsTest15DeltaCRLs,
@@ -2016,7 +3229,7 @@
       "TrustAnchorRootCertificate",
       "ValidUnknownNotCriticalCertificateExtensionTest1EE"};
   const char* const crls[] = {"TrustAnchorRootCRL"};
-  ASSERT_TRUE(this->Verify(certs, crls));
+  ASSERT_TRUE(this->Verify(certs, crls, {}));
 }
 
 // 4.16.2 Invalid Unknown Critical Certificate Extension Test2
@@ -2026,7 +3239,7 @@
       "TrustAnchorRootCertificate",
       "InvalidUnknownCriticalCertificateExtensionTest2EE"};
   const char* const crls[] = {"TrustAnchorRootCRL"};
-  ASSERT_FALSE(this->Verify(certs, crls));
+  ASSERT_FALSE(this->Verify(certs, crls, {}));
 }
 
 WRAPPED_REGISTER_TYPED_TEST_CASE_P(
diff --git a/net/url_request/url_request_context_builder.cc b/net/url_request/url_request_context_builder.cc
index 3309974b..605ff7e 100644
--- a/net/url_request/url_request_context_builder.cc
+++ b/net/url_request/url_request_context_builder.cc
@@ -192,8 +192,7 @@
 URLRequestContextBuilder::HttpCacheParams::~HttpCacheParams() {}
 
 URLRequestContextBuilder::HttpNetworkSessionParams::HttpNetworkSessionParams()
-    : host_mapping_rules(nullptr),
-      ignore_certificate_errors(false),
+    : ignore_certificate_errors(false),
       testing_fixed_http_port(0),
       testing_fixed_https_port(0),
       enable_http2(true),
diff --git a/net/url_request/url_request_context_builder.h b/net/url_request/url_request_context_builder.h
index a622625..45526b0a 100644
--- a/net/url_request/url_request_context_builder.h
+++ b/net/url_request/url_request_context_builder.h
@@ -94,7 +94,7 @@
     void ConfigureSessionParams(HttpNetworkSession::Params* params) const;
 
     // These fields mirror those in HttpNetworkSession::Params;
-    HostMappingRules* host_mapping_rules;
+    HostMappingRules host_mapping_rules;
     bool ignore_certificate_errors;
     uint16_t testing_fixed_http_port;
     uint16_t testing_fixed_https_port;
diff --git a/remoting/client/gesture_interpreter.cc b/remoting/client/gesture_interpreter.cc
index c5ae592..7cf7e72 100644
--- a/remoting/client/gesture_interpreter.cc
+++ b/remoting/client/gesture_interpreter.cc
@@ -8,7 +8,7 @@
 #include "base/time/time.h"
 #include "remoting/client/chromoting_session.h"
 #include "remoting/client/display/renderer_proxy.h"
-#include "remoting/client/input/direct_input_strategy.h"
+#include "remoting/client/input/direct_touch_input_strategy.h"
 #include "remoting/client/input/trackpad_input_strategy.h"
 
 namespace {
@@ -41,7 +41,7 @@
 void GestureInterpreter::SetInputMode(InputMode mode) {
   switch (mode) {
     case DIRECT_INPUT_MODE:
-      input_strategy_.reset(new DirectInputStrategy());
+      input_strategy_.reset(new DirectTouchInputStrategy());
       break;
     case TRACKPAD_INPUT_MODE:
       input_strategy_.reset(new TrackpadInputStrategy(viewport_));
@@ -64,7 +64,7 @@
                               float scale,
                               GestureState state) {
   AbortAnimations();
-  SetGestureInProgress(InputStrategy::ZOOM, state != GESTURE_ENDED);
+  SetGestureInProgress(TouchInputStrategy::ZOOM, state != GESTURE_ENDED);
   input_strategy_->HandleZoom({pivot_x, pivot_y}, scale, &viewport_);
 }
 
@@ -101,11 +101,11 @@
 
   if (state == GESTURE_BEGAN) {
     StartInputFeedback(cursor_position.x, cursor_position.y,
-                       InputStrategy::DRAG_FEEDBACK);
+                       TouchInputStrategy::DRAG_FEEDBACK);
   }
 
   bool is_dragging_mode = state != GESTURE_ENDED;
-  SetGestureInProgress(InputStrategy::DRAG, is_dragging_mode);
+  SetGestureInProgress(TouchInputStrategy::DRAG, is_dragging_mode);
   input_stub_->SendMouseEvent(cursor_position.x, cursor_position.y,
                               protocol::MouseEvent_MouseButton_BUTTON_LEFT,
                               is_dragging_mode);
@@ -162,7 +162,7 @@
                                  gesture_in_progress_, &viewport_)) {
     // Cursor position changed.
     ViewMatrix::Point cursor_position = input_strategy_->GetCursorPosition();
-    if (gesture_in_progress_ != InputStrategy::DRAG) {
+    if (gesture_in_progress_ != TouchInputStrategy::DRAG) {
       // Drag() will inject the position so don't need to do that in that case.
       InjectCursorPosition(cursor_position.x, cursor_position.y);
     }
@@ -195,7 +195,7 @@
   }
   ViewMatrix::Point cursor_position = input_strategy_->GetCursorPosition();
   StartInputFeedback(cursor_position.x, cursor_position.y,
-                     InputStrategy::TAP_FEEDBACK);
+                     TouchInputStrategy::TAP_FEEDBACK);
 
   input_stub_->SendMouseEvent(cursor_position.x, cursor_position.y, button,
                               true);
@@ -203,10 +203,11 @@
                               false);
 }
 
-void GestureInterpreter::SetGestureInProgress(InputStrategy::Gesture gesture,
-                                              bool is_in_progress) {
+void GestureInterpreter::SetGestureInProgress(
+    TouchInputStrategy::Gesture gesture,
+    bool is_in_progress) {
   if (!is_in_progress && gesture_in_progress_ == gesture) {
-    gesture_in_progress_ = InputStrategy::NONE;
+    gesture_in_progress_ = TouchInputStrategy::NONE;
     return;
   }
   gesture_in_progress_ = gesture;
@@ -215,7 +216,7 @@
 void GestureInterpreter::StartInputFeedback(
     float cursor_x,
     float cursor_y,
-    InputStrategy::InputFeedbackType feedback_type) {
+    TouchInputStrategy::TouchFeedbackType feedback_type) {
   // This radius is on the view's coordinates. Need to be converted to desktop
   // coordinate.
   float feedback_radius = input_strategy_->GetFeedbackRadius(feedback_type);
diff --git a/remoting/client/gesture_interpreter.h b/remoting/client/gesture_interpreter.h
index 66642e5..abe6871 100644
--- a/remoting/client/gesture_interpreter.h
+++ b/remoting/client/gesture_interpreter.h
@@ -7,7 +7,7 @@
 
 #include <memory>
 
-#include "remoting/client/input/input_strategy.h"
+#include "remoting/client/input/touch_input_strategy.h"
 #include "remoting/client/ui/desktop_viewport.h"
 #include "remoting/client/ui/fling_animation.h"
 #include "remoting/proto/event.pb.h"
@@ -91,21 +91,21 @@
 
   void InjectCursorPosition(float x, float y);
 
-  void SetGestureInProgress(InputStrategy::Gesture gesture,
+  void SetGestureInProgress(TouchInputStrategy::Gesture gesture,
                             bool is_in_progress);
 
   // Starts the given feedback at (cursor_x, cursor_y) if the feedback radius
   // is non-zero.
   void StartInputFeedback(float cursor_x,
                           float cursor_y,
-                          InputStrategy::InputFeedbackType feedback_type);
+                          TouchInputStrategy::TouchFeedbackType feedback_type);
 
   InputMode input_mode_ = UNDEFINED_INPUT_MODE;
-  std::unique_ptr<InputStrategy> input_strategy_;
+  std::unique_ptr<TouchInputStrategy> input_strategy_;
   DesktopViewport viewport_;
   RendererProxy* renderer_;
   ChromotingSession* input_stub_;
-  InputStrategy::Gesture gesture_in_progress_;
+  TouchInputStrategy::Gesture gesture_in_progress_;
 
   FlingAnimation pan_animation_;
   FlingAnimation scroll_animation_;
diff --git a/remoting/client/input/BUILD.gn b/remoting/client/input/BUILD.gn
index c6e3279..5dc460e2 100644
--- a/remoting/client/input/BUILD.gn
+++ b/remoting/client/input/BUILD.gn
@@ -5,9 +5,8 @@
 source_set("input") {
   sources = [
     "client_input_injector.h",
-    "direct_input_strategy.cc",
-    "direct_input_strategy.h",
-    "input_strategy.h",
+    "direct_touch_input_strategy.cc",
+    "direct_touch_input_strategy.h",
     "key_event_mapper.cc",
     "key_event_mapper.h",
     "keyboard_input_strategy.h",
@@ -21,6 +20,7 @@
     "text_keyboard_input_strategy.h",
     "touch_input_scaler.cc",
     "touch_input_scaler.h",
+    "touch_input_strategy.h",
     "trackpad_input_strategy.cc",
     "trackpad_input_strategy.h",
   ]
diff --git a/remoting/client/input/direct_input_strategy.cc b/remoting/client/input/direct_input_strategy.cc
deleted file mode 100644
index b8a35b7..0000000
--- a/remoting/client/input/direct_input_strategy.cc
+++ /dev/null
@@ -1,81 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "remoting/client/input/direct_input_strategy.h"
-
-#include "remoting/client/ui/desktop_viewport.h"
-
-namespace remoting {
-
-namespace {
-
-const float kTapFeedbackRadius = 25.f;
-const float kDragFeedbackRadius = 55.f;
-
-}  // namespace
-
-DirectInputStrategy::DirectInputStrategy() {}
-
-DirectInputStrategy::~DirectInputStrategy() {}
-
-void DirectInputStrategy::HandleZoom(const ViewMatrix::Point& pivot,
-                                     float scale,
-                                     DesktopViewport* viewport) {
-  viewport->ScaleDesktop(pivot.x, pivot.y, scale);
-}
-
-bool DirectInputStrategy::HandlePan(const ViewMatrix::Vector2D& translation,
-                                    Gesture simultaneous_gesture,
-                                    DesktopViewport* viewport) {
-  if (simultaneous_gesture == DRAG) {
-    // If the user is dragging something, we should synchronize the movement
-    // with the object that the user is trying to move on the desktop, rather
-    // than moving the desktop around.
-    ViewMatrix::Vector2D viewport_movement =
-        viewport->GetTransformation().Invert().MapVector(translation);
-    viewport->MoveViewport(viewport_movement.x, viewport_movement.y);
-    return false;
-  }
-
-  viewport->MoveDesktop(translation.x, translation.y);
-  return false;
-}
-
-bool DirectInputStrategy::TrackTouchInput(const ViewMatrix::Point& touch_point,
-                                          const DesktopViewport& viewport) {
-  ViewMatrix::Point new_position =
-      viewport.GetTransformation().Invert().MapPoint(touch_point);
-  if (!viewport.IsPointWithinDesktopBounds(new_position)) {
-    return false;
-  }
-  cursor_position_ = new_position;
-  return true;
-}
-
-ViewMatrix::Point DirectInputStrategy::GetCursorPosition() const {
-  return cursor_position_;
-}
-
-ViewMatrix::Vector2D DirectInputStrategy::MapScreenVectorToDesktop(
-    const ViewMatrix::Vector2D& delta,
-    const DesktopViewport& viewport) const {
-  return viewport.GetTransformation().Invert().MapVector(delta);
-}
-
-float DirectInputStrategy::GetFeedbackRadius(InputFeedbackType type) const {
-  switch (type) {
-    case InputFeedbackType::TAP_FEEDBACK:
-      return kTapFeedbackRadius;
-    case InputFeedbackType::DRAG_FEEDBACK:
-      return kDragFeedbackRadius;
-  }
-  NOTREACHED();
-  return 0.f;
-}
-
-bool DirectInputStrategy::IsCursorVisible() const {
-  return false;
-}
-
-}  // namespace remoting
diff --git a/remoting/client/input/direct_touch_input_strategy.cc b/remoting/client/input/direct_touch_input_strategy.cc
new file mode 100644
index 0000000..5b4647a
--- /dev/null
+++ b/remoting/client/input/direct_touch_input_strategy.cc
@@ -0,0 +1,84 @@
+// 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/client/input/direct_touch_input_strategy.h"
+
+#include "remoting/client/ui/desktop_viewport.h"
+
+namespace remoting {
+
+namespace {
+
+const float kTapFeedbackRadius = 25.f;
+const float kDragFeedbackRadius = 55.f;
+
+}  // namespace
+
+DirectTouchInputStrategy::DirectTouchInputStrategy() {}
+
+DirectTouchInputStrategy::~DirectTouchInputStrategy() {}
+
+void DirectTouchInputStrategy::HandleZoom(const ViewMatrix::Point& pivot,
+                                          float scale,
+                                          DesktopViewport* viewport) {
+  viewport->ScaleDesktop(pivot.x, pivot.y, scale);
+}
+
+bool DirectTouchInputStrategy::HandlePan(
+    const ViewMatrix::Vector2D& translation,
+    Gesture simultaneous_gesture,
+    DesktopViewport* viewport) {
+  if (simultaneous_gesture == DRAG) {
+    // If the user is dragging something, we should synchronize the movement
+    // with the object that the user is trying to move on the desktop, rather
+    // than moving the desktop around.
+    ViewMatrix::Vector2D viewport_movement =
+        viewport->GetTransformation().Invert().MapVector(translation);
+    viewport->MoveViewport(viewport_movement.x, viewport_movement.y);
+    return false;
+  }
+
+  viewport->MoveDesktop(translation.x, translation.y);
+  return false;
+}
+
+bool DirectTouchInputStrategy::TrackTouchInput(
+    const ViewMatrix::Point& touch_point,
+    const DesktopViewport& viewport) {
+  ViewMatrix::Point new_position =
+      viewport.GetTransformation().Invert().MapPoint(touch_point);
+  if (!viewport.IsPointWithinDesktopBounds(new_position)) {
+    return false;
+  }
+  cursor_position_ = new_position;
+  return true;
+}
+
+ViewMatrix::Point DirectTouchInputStrategy::GetCursorPosition() const {
+  return cursor_position_;
+}
+
+ViewMatrix::Vector2D DirectTouchInputStrategy::MapScreenVectorToDesktop(
+    const ViewMatrix::Vector2D& delta,
+    const DesktopViewport& viewport) const {
+  return viewport.GetTransformation().Invert().MapVector(delta);
+}
+
+float DirectTouchInputStrategy::GetFeedbackRadius(
+    TouchFeedbackType type) const {
+  switch (type) {
+    case TouchFeedbackType::TAP_FEEDBACK:
+      return kTapFeedbackRadius;
+    case TouchFeedbackType::DRAG_FEEDBACK:
+      return kDragFeedbackRadius;
+  }
+  NOTREACHED();
+  return 0.f;
+}
+
+bool DirectTouchInputStrategy::IsCursorVisible() const {
+  return false;
+}
+
+}  // namespace remoting
diff --git a/remoting/client/input/direct_input_strategy.h b/remoting/client/input/direct_touch_input_strategy.h
similarity index 66%
rename from remoting/client/input/direct_input_strategy.h
rename to remoting/client/input/direct_touch_input_strategy.h
index ef83851..08a63cc61 100644
--- a/remoting/client/input/direct_input_strategy.h
+++ b/remoting/client/input/direct_touch_input_strategy.h
@@ -2,22 +2,22 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef REMOTING_CLIENT_INPUT_DIRECT_INPUT_STRATEGY_H_
-#define REMOTING_CLIENT_INPUT_DIRECT_INPUT_STRATEGY_H_
+#ifndef REMOTING_CLIENT_INPUT_DIRECT_TOUCH_INPUT_STRATEGY_H_
+#define REMOTING_CLIENT_INPUT_DIRECT_TOUCH_INPUT_STRATEGY_H_
 
-#include "remoting/client/input/input_strategy.h"
+#include "remoting/client/input/touch_input_strategy.h"
 
 namespace remoting {
 
 // This strategy directly translates all operations on the OpenGL view into
 // corresponding operations on the desktop. It doesn't maintain the cursor
 // positions separately -- the positions come from the location of the touch.
-class DirectInputStrategy : public InputStrategy {
+class DirectTouchInputStrategy : public TouchInputStrategy {
  public:
-  DirectInputStrategy();
-  ~DirectInputStrategy() override;
+  DirectTouchInputStrategy();
+  ~DirectTouchInputStrategy() override;
 
-  // InputStrategy overrides.
+  // TouchInputStrategy overrides.
 
   void HandleZoom(const ViewMatrix::Point& pivot,
                   float scale,
@@ -36,7 +36,7 @@
       const ViewMatrix::Vector2D& delta,
       const DesktopViewport& viewport) const override;
 
-  float GetFeedbackRadius(InputFeedbackType type) const override;
+  float GetFeedbackRadius(TouchFeedbackType type) const override;
 
   bool IsCursorVisible() const override;
 
@@ -44,9 +44,9 @@
   ViewMatrix::Point cursor_position_{0.f, 0.f};
 
   // TouchInputStrategy is neither copyable nor movable.
-  DirectInputStrategy(const DirectInputStrategy&) = delete;
-  DirectInputStrategy& operator=(const DirectInputStrategy&) = delete;
+  DirectTouchInputStrategy(const DirectTouchInputStrategy&) = delete;
+  DirectTouchInputStrategy& operator=(const DirectTouchInputStrategy&) = delete;
 };
 
 }  // namespace remoting
-#endif  // REMOTING_CLIENT_INPUT_DIRECT_INPUT_STRATEGY_H_
+#endif  // REMOTING_CLIENT_INPUT_DIRECT_TOUCH_INPUT_STRATEGY_H_
diff --git a/remoting/client/input/input_strategy.h b/remoting/client/input/touch_input_strategy.h
similarity index 89%
rename from remoting/client/input/input_strategy.h
rename to remoting/client/input/touch_input_strategy.h
index 72f70d5..60eec61f 100644
--- a/remoting/client/input/input_strategy.h
+++ b/remoting/client/input/touch_input_strategy.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef REMOTING_CLIENT_INPUT_INPUT_STRATEGY_H_
-#define REMOTING_CLIENT_INPUT_INPUT_STRATEGY_H_
+#ifndef REMOTING_CLIENT_TOUCH_INPUT_INPUT_STRATEGY_H_
+#define REMOTING_CLIENT_TOUCH_INPUT_INPUT_STRATEGY_H_
 
 #include "remoting/client/ui/view_matrix.h"
 
@@ -13,9 +13,9 @@
 
 // This is an interface used by GestureInterpreter to customize the way gestures
 // are handled.
-class InputStrategy {
+class TouchInputStrategy {
  public:
-  enum InputFeedbackType {
+  enum TouchFeedbackType {
     TAP_FEEDBACK,
     DRAG_FEEDBACK,
   };
@@ -26,7 +26,7 @@
     DRAG,
   };
 
-  virtual ~InputStrategy() {}
+  virtual ~TouchInputStrategy() {}
 
   // Called when the GestureInterpreter receives a zoom gesture. The
   // implementation is responsible for modifying the viewport and observing the
@@ -68,7 +68,7 @@
   // coordinate for the given input type. The feedback will then be shown on the
   // cursor positions returned by GetCursorPosition(). Return 0 if no feedback
   // should be shown.
-  virtual float GetFeedbackRadius(InputFeedbackType type) const = 0;
+  virtual float GetFeedbackRadius(TouchFeedbackType type) const = 0;
 
   // Returns true if the input strategy maintains a visible cursor on the
   // desktop.
@@ -76,4 +76,4 @@
 };
 
 }  // namespace remoting
-#endif  // REMOTING_CLIENT_INPUT_INPUT_STRATEGY_H_
+#endif  // REMOTING_CLIENT_TOUCH_INPUT_INPUT_STRATEGY_H_
diff --git a/remoting/client/input/trackpad_input_strategy.cc b/remoting/client/input/trackpad_input_strategy.cc
index b669871..b38c625 100644
--- a/remoting/client/input/trackpad_input_strategy.cc
+++ b/remoting/client/input/trackpad_input_strategy.cc
@@ -73,11 +73,11 @@
   return delta;
 }
 
-float TrackpadInputStrategy::GetFeedbackRadius(InputFeedbackType type) const {
+float TrackpadInputStrategy::GetFeedbackRadius(TouchFeedbackType type) const {
   switch (type) {
-    case InputFeedbackType::TAP_FEEDBACK:
+    case TouchFeedbackType::TAP_FEEDBACK:
       return kTapFeedbackRadius;
-    case InputFeedbackType::DRAG_FEEDBACK:
+    case TouchFeedbackType::DRAG_FEEDBACK:
       return kDragFeedbackRadius;
   }
   NOTREACHED();
diff --git a/remoting/client/input/trackpad_input_strategy.h b/remoting/client/input/trackpad_input_strategy.h
index abee68f..edf325f 100644
--- a/remoting/client/input/trackpad_input_strategy.h
+++ b/remoting/client/input/trackpad_input_strategy.h
@@ -5,18 +5,18 @@
 #ifndef REMOTING_CLIENT_INPUT_TRACKPAD_INPUT_STRATEGY_H_
 #define REMOTING_CLIENT_INPUT_TRACKPAD_INPUT_STRATEGY_H_
 
-#include "remoting/client/input/input_strategy.h"
+#include "remoting/client/input/touch_input_strategy.h"
 
 namespace remoting {
 
 // This strategy simulate the trackpad's behavior. It keeps a visible cursor
 // with positions independent of the location of the touch events.
-class TrackpadInputStrategy : public InputStrategy {
+class TrackpadInputStrategy : public TouchInputStrategy {
  public:
   TrackpadInputStrategy(const DesktopViewport& viewport);
   ~TrackpadInputStrategy() override;
 
-  // InputStrategy overrides.
+  // TouchInputStrategy overrides.
   void HandleZoom(const ViewMatrix::Point& pivot,
                   float scale,
                   DesktopViewport* viewport) override;
@@ -34,7 +34,7 @@
       const ViewMatrix::Vector2D& delta,
       const DesktopViewport& viewport) const override;
 
-  float GetFeedbackRadius(InputFeedbackType type) const override;
+  float GetFeedbackRadius(TouchFeedbackType type) const override;
 
   bool IsCursorVisible() const override;
 
diff --git a/services/resource_coordinator/BUILD.gn b/services/resource_coordinator/BUILD.gn
index b31809c..b1a8500a 100644
--- a/services/resource_coordinator/BUILD.gn
+++ b/services/resource_coordinator/BUILD.gn
@@ -64,6 +64,7 @@
     "memory_instrumentation/process_map_unittest.cc",
     "public/cpp/memory_instrumentation/client_process_impl_unittest.cc",
     "public/cpp/tracing/chrome_trace_event_agent_unittest.cc",
+    "tracing/agent_registry_unittest.cc",
     "tracing/recorder_unittest.cc",
   ]
 
diff --git a/services/resource_coordinator/tracing/BUILD.gn b/services/resource_coordinator/tracing/BUILD.gn
index 866a179..e578dfcd 100644
--- a/services/resource_coordinator/tracing/BUILD.gn
+++ b/services/resource_coordinator/tracing/BUILD.gn
@@ -4,6 +4,8 @@
 
 source_set("lib") {
   sources = [
+    "agent_registry.cc",
+    "agent_registry.h",
     "recorder.cc",
     "recorder.h",
   ]
diff --git a/services/resource_coordinator/tracing/agent_registry.cc b/services/resource_coordinator/tracing/agent_registry.cc
new file mode 100644
index 0000000..d87a055
--- /dev/null
+++ b/services/resource_coordinator/tracing/agent_registry.cc
@@ -0,0 +1,116 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "services/resource_coordinator/tracing/agent_registry.h"
+
+#include <string>
+#include <utility>
+
+#include "base/callback_forward.h"
+#include "base/logging.h"
+#include "base/threading/thread_checker.h"
+#include "services/service_manager/public/cpp/bind_source_info.h"
+
+namespace {
+tracing::AgentRegistry* g_agent_registry;
+}
+
+namespace tracing {
+
+AgentRegistry::AgentEntry::AgentEntry(size_t id,
+                                      AgentRegistry* agent_registry,
+                                      mojom::AgentPtr agent,
+                                      const std::string& label,
+                                      mojom::TraceDataType type,
+                                      bool supports_explicit_clock_sync)
+    : id_(id),
+      agent_registry_(agent_registry),
+      agent_(std::move(agent)),
+      label_(label),
+      type_(type),
+      supports_explicit_clock_sync_(supports_explicit_clock_sync) {
+  DCHECK(!label.empty());
+  agent_.set_connection_error_handler(base::BindRepeating(
+      &AgentRegistry::AgentEntry::OnConnectionError, base::Unretained(this)));
+}
+
+AgentRegistry::AgentEntry::~AgentEntry() = default;
+
+void AgentRegistry::AgentEntry::SetDisconnectClosure(
+    base::OnceClosure closure) {
+  DCHECK(closure_.is_null());
+  closure_ = std::move(closure);
+}
+
+bool AgentRegistry::AgentEntry::RemoveDisconnectClosure() {
+  bool closure_was_set = !closure_.is_null();
+  closure_.Reset();
+  return closure_was_set;
+}
+
+void AgentRegistry::AgentEntry::OnConnectionError() {
+  // Run the disconnect closure if it is set. We should mark |closure_| as
+  // movable so that the version of |Run| that takes an rvalue reference is
+  // selected not the version that takes a const reference. The former is for
+  // once callbacks and the latter is for repeating callbacks.
+  if (!closure_.is_null())
+    std::move(closure_).Run();
+  agent_registry_->UnregisterAgent(id_);
+}
+
+// static
+AgentRegistry* AgentRegistry::GetInstance() {
+  return g_agent_registry;
+}
+
+AgentRegistry::AgentRegistry() {
+  DCHECK(!g_agent_registry);
+  g_agent_registry = this;
+}
+
+AgentRegistry::~AgentRegistry() {
+  // For testing only.
+  g_agent_registry = nullptr;
+}
+
+void AgentRegistry::BindAgentRegistryRequest(
+    const service_manager::BindSourceInfo& source_info,
+    mojom::AgentRegistryRequest request) {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+  bindings_.AddBinding(this, std::move(request));
+}
+
+void AgentRegistry::SetAgentInitializationCallback(
+    const AgentInitializationCallback& callback) {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+  agent_initialization_callback_ = callback;
+  ForAllAgents([this](AgentEntry* agent_entry) {
+    agent_initialization_callback_.Run(agent_entry);
+  });
+}
+
+void AgentRegistry::RemoveAgentInitializationCallback() {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+  agent_initialization_callback_.Reset();
+}
+
+void AgentRegistry::RegisterAgent(mojom::AgentPtr agent,
+                                  const std::string& label,
+                                  mojom::TraceDataType type,
+                                  bool supports_explicit_clock_sync) {
+  auto id = next_agent_id_++;
+  auto entry = base::MakeUnique<AgentEntry>(id, this, std::move(agent), label,
+                                            type, supports_explicit_clock_sync);
+  if (!agent_initialization_callback_.is_null())
+    agent_initialization_callback_.Run(entry.get());
+  auto result = agents_.insert(std::make_pair(id, std::move(entry)));
+  DCHECK(result.second);
+}
+
+void AgentRegistry::UnregisterAgent(size_t agent_id) {
+  size_t num_deleted = agents_.erase(agent_id);
+  DCHECK_EQ(1u, num_deleted);
+}
+
+}  // namespace tracing
diff --git a/services/resource_coordinator/tracing/agent_registry.h b/services/resource_coordinator/tracing/agent_registry.h
new file mode 100644
index 0000000..6841978c
--- /dev/null
+++ b/services/resource_coordinator/tracing/agent_registry.h
@@ -0,0 +1,109 @@
+// 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_RESOURCE_COORDINATOR_TRACING_AGENT_REGISTRY_H_
+#define SERVICES_RESOURCE_COORDINATOR_TRACING_AGENT_REGISTRY_H_
+
+#include <map>
+#include <memory>
+#include <string>
+
+#include "base/callback_forward.h"
+#include "base/macros.h"
+#include "base/threading/thread_checker.h"
+#include "mojo/public/cpp/bindings/binding_set.h"
+#include "services/resource_coordinator/public/interfaces/tracing/tracing.mojom.h"
+#include "services/service_manager/public/cpp/bind_source_info.h"
+
+namespace tracing {
+
+class AgentRegistry : public mojom::AgentRegistry {
+ public:
+  class AgentEntry {
+   public:
+    AgentEntry(size_t id,
+               AgentRegistry* agent_registry,
+               mojom::AgentPtr agent,
+               const std::string& label,
+               mojom::TraceDataType type,
+               bool supports_explicit_clock_sync);
+    ~AgentEntry();
+
+    // Currently, at most one callback when the tracing agent is disconnected is
+    // enough. We can generalize this later if several parts of the service need
+    // to get notified when an agent disconnects.
+    void SetDisconnectClosure(base::OnceClosure closure);
+    bool RemoveDisconnectClosure();
+
+    mojom::Agent* agent() const { return agent_.get(); }
+    const std::string& label() const { return label_; }
+    mojom::TraceDataType type() const { return type_; }
+    bool supports_explicit_clock_sync() const {
+      return supports_explicit_clock_sync_;
+    }
+
+   private:
+    void OnConnectionError();
+
+    const size_t id_;
+    AgentRegistry* agent_registry_;
+    mojom::AgentPtr agent_;
+    const std::string label_;
+    const mojom::TraceDataType type_;
+    const bool supports_explicit_clock_sync_;
+    base::OnceClosure closure_;
+
+    DISALLOW_COPY_AND_ASSIGN(AgentEntry);
+  };
+
+  // A function to be run for every agent that registers itself.
+  using AgentInitializationCallback =
+      base::RepeatingCallback<void(AgentEntry*)>;
+
+  static AgentRegistry* GetInstance();
+
+  AgentRegistry();
+
+  void BindAgentRegistryRequest(
+      const service_manager::BindSourceInfo& source_info,
+      mojom::AgentRegistryRequest request);
+  void SetAgentInitializationCallback(
+      const AgentInitializationCallback& callback);
+  void RemoveAgentInitializationCallback();
+
+  template <typename FunctionType>
+  void ForAllAgents(FunctionType function) {
+    DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+    for (const auto& key_value : agents_) {
+      function(key_value.second.get());
+    }
+  }
+
+ private:
+  friend std::default_delete<AgentRegistry>;
+  friend class AgentRegistryTest;  // For testing.
+
+  ~AgentRegistry() override;
+
+  // mojom::AgentRegistry
+  void RegisterAgent(mojom::AgentPtr agent,
+                     const std::string& label,
+                     mojom::TraceDataType type,
+                     bool supports_explicit_clock_sync) override;
+
+  void UnregisterAgent(size_t agent_id);
+
+  mojo::BindingSet<mojom::AgentRegistry> bindings_;
+  size_t next_agent_id_ = 0;
+  std::map<size_t, std::unique_ptr<AgentEntry>> agents_;
+  AgentInitializationCallback agent_initialization_callback_;
+
+  THREAD_CHECKER(thread_checker_);
+
+  DISALLOW_COPY_AND_ASSIGN(AgentRegistry);
+};
+
+}  // namespace tracing
+
+#endif  // SERVICES_RESOURCE_COORDINATOR_TRACING_AGENT_REGISTRY_H_
diff --git a/services/resource_coordinator/tracing/agent_registry_unittest.cc b/services/resource_coordinator/tracing/agent_registry_unittest.cc
new file mode 100644
index 0000000..858e61e
--- /dev/null
+++ b/services/resource_coordinator/tracing/agent_registry_unittest.cc
@@ -0,0 +1,148 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "services/resource_coordinator/tracing/agent_registry.h"
+
+#include <memory>
+#include <string>
+#include <utility>
+
+#include "base/message_loop/message_loop.h"
+#include "base/run_loop.h"
+#include "mojo/public/cpp/bindings/binding.h"
+#include "services/resource_coordinator/public/interfaces/tracing/tracing.mojom.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace tracing {
+
+class MockAgent : public mojom::Agent {
+ public:
+  MockAgent() : binding_(this) {}
+
+  mojom::AgentPtr CreateAgentPtr() {
+    return binding_.CreateInterfacePtrAndBind();
+  }
+
+ private:
+  // mojom::Agent
+  void StartTracing(const std::string& config,
+                    mojom::RecorderPtr recorder,
+                    const StartTracingCallback& callback) override {}
+  void StopAndFlush() override {}
+  void RequestClockSyncMarker(
+      const std::string& sync_id,
+      const RequestClockSyncMarkerCallback& callback) override {}
+  void RequestBufferStatus(
+      const RequestBufferStatusCallback& callback) override {}
+  void GetCategories(const GetCategoriesCallback& callback) override {}
+
+  mojo::Binding<mojom::Agent> binding_;
+};
+
+class AgentRegistryTest : public testing::Test {
+ public:
+  void SetUp() override {
+    message_loop_.reset(new base::MessageLoop());
+    registry_.reset(new AgentRegistry());
+  }
+
+  void TearDown() override {
+    registry_.reset();
+    message_loop_.reset();
+  }
+
+  void RegisterAgent(mojom::AgentPtr agent,
+                     const std::string& label,
+                     mojom::TraceDataType type,
+                     bool supports_explicit_clock_sync) {
+    registry_->RegisterAgent(std::move(agent), label, type,
+                             supports_explicit_clock_sync);
+  }
+
+  void RegisterAgent(mojom::AgentPtr agent) {
+    registry_->RegisterAgent(std::move(agent), "label",
+                             mojom::TraceDataType::ARRAY, false);
+  }
+
+  std::unique_ptr<AgentRegistry> registry_;
+
+ private:
+  std::unique_ptr<base::MessageLoop> message_loop_;
+};
+
+TEST_F(AgentRegistryTest, RegisterAgent) {
+  MockAgent agent1;
+  RegisterAgent(agent1.CreateAgentPtr(), "TraceEvent",
+                mojom::TraceDataType::ARRAY, false);
+  size_t num_agents = 0;
+  registry_->ForAllAgents([&num_agents](AgentRegistry::AgentEntry* entry) {
+    num_agents++;
+    EXPECT_EQ("TraceEvent", entry->label());
+    EXPECT_EQ(mojom::TraceDataType::ARRAY, entry->type());
+    EXPECT_FALSE(entry->supports_explicit_clock_sync());
+  });
+  EXPECT_EQ(1u, num_agents);
+
+  MockAgent agent2;
+  RegisterAgent(agent2.CreateAgentPtr(), "Power", mojom::TraceDataType::STRING,
+                true);
+  num_agents = 0;
+  registry_->ForAllAgents([&num_agents](AgentRegistry::AgentEntry* entry) {
+    num_agents++;
+    // Properties of |agent1| is already verified.
+    if (entry->label() == "TraceEvent")
+      return;
+    EXPECT_EQ("Power", entry->label());
+    EXPECT_EQ(mojom::TraceDataType::STRING, entry->type());
+    EXPECT_TRUE(entry->supports_explicit_clock_sync());
+  });
+  EXPECT_EQ(2u, num_agents);
+}
+
+TEST_F(AgentRegistryTest, UnregisterAgent) {
+  base::RunLoop run_loop;
+  MockAgent agent1;
+  RegisterAgent(agent1.CreateAgentPtr());
+  {
+    MockAgent agent2;
+    RegisterAgent(agent2.CreateAgentPtr());
+    size_t num_agents = 0;
+    registry_->ForAllAgents(
+        [&num_agents](AgentRegistry::AgentEntry* entry) { num_agents++; });
+    EXPECT_EQ(2u, num_agents);
+  }
+  run_loop.RunUntilIdle();
+
+  // |agent2| is not alive anymore.
+  size_t num_agents = 0;
+  registry_->ForAllAgents(
+      [&num_agents](AgentRegistry::AgentEntry* entry) { num_agents++; });
+  EXPECT_EQ(1u, num_agents);
+}
+
+TEST_F(AgentRegistryTest, AgentInitialization) {
+  size_t num_calls = 0;
+  MockAgent agent1;
+  RegisterAgent(agent1.CreateAgentPtr());
+  registry_->SetAgentInitializationCallback(base::BindRepeating(
+      [](size_t* num_calls, tracing::AgentRegistry::AgentEntry* entry) {
+        (*num_calls)++;
+      },
+      base::Unretained(&num_calls)));
+  // Since an agent was already registered, the callback should be run once.
+  EXPECT_EQ(1u, num_calls);
+
+  // The callback should be run on future agents, too.
+  MockAgent agent2;
+  RegisterAgent(agent2.CreateAgentPtr());
+  EXPECT_EQ(2u, num_calls);
+
+  // The callback should not be run on future agents if it is removed.
+  registry_->RemoveAgentInitializationCallback();
+  MockAgent agent3;
+  RegisterAgent(agent3.CreateAgentPtr());
+  EXPECT_EQ(2u, num_calls);
+}
+
+}  // namespace tracing
diff --git a/services/ui/public/interfaces/window_manager.mojom b/services/ui/public/interfaces/window_manager.mojom
index 0f659e62..563697d 100644
--- a/services/ui/public/interfaces/window_manager.mojom
+++ b/services/ui/public/interfaces/window_manager.mojom
@@ -280,6 +280,16 @@
                  uint32 window_id) =>
       (cc.mojom.LocalSurfaceId? local_surface_id);
 
+  // Configures the metrics for the displays and notifies observers. This is
+  // only applicable when WindowTree was created with a value of false for
+  // |automatically_create_display_roots|. This *must* be called after any
+  // display related changes to inform observers, for example, after calling
+  // SetDisplayRoot() this must be called to inform observers of the display
+  // changes.
+  SetDisplayConfiguration(array<display.mojom.Display> displays,
+                          array<WmViewportMetrics> viewport_metrics,
+                          int64 primary_display_id) => (bool success);
+
   // The window manager has completed a request with the specific change id.
   WmResponse(uint32 change_id, bool response);
 
diff --git a/services/ui/service.cc b/services/ui/service.cc
index 59b670f..5b2c9ca 100644
--- a/services/ui/service.cc
+++ b/services/ui/service.cc
@@ -29,6 +29,7 @@
 #include "services/ui/ime/ime_server_impl.h"
 #include "services/ui/ws/accessibility_manager.h"
 #include "services/ui/ws/display_binding.h"
+#include "services/ui/ws/display_creation_config.h"
 #include "services/ui/ws/display_manager.h"
 #include "services/ui/ws/gpu_host.h"
 #include "services/ui/ws/user_activity_monitor.h"
@@ -288,15 +289,19 @@
 
 void Service::OnWillCreateTreeForWindowManager(
     bool automatically_create_display_roots) {
-  if (screen_manager_config_ != ScreenManagerConfig::UNKNOWN)
+  if (window_server_->display_creation_config() !=
+      ws::DisplayCreationConfig::UNKNOWN) {
     return;
+  }
 
   DVLOG(3) << "OnWillCreateTreeForWindowManager "
            << automatically_create_display_roots;
-  screen_manager_config_ = automatically_create_display_roots
-                               ? ScreenManagerConfig::INTERNAL
-                               : ScreenManagerConfig::FORWARDING;
-  if (screen_manager_config_ == ScreenManagerConfig::FORWARDING) {
+  ws::DisplayCreationConfig config = automatically_create_display_roots
+                                         ? ws::DisplayCreationConfig::AUTOMATIC
+                                         : ws::DisplayCreationConfig::MANUAL;
+  window_server_->SetDisplayCreationConfig(config);
+  if (window_server_->display_creation_config() ==
+      ws::DisplayCreationConfig::MANUAL) {
 #if defined(USE_OZONE) && defined(OS_CHROMEOS)
     screen_manager_ = base::MakeUnique<display::ScreenManagerForwarding>();
 #else
@@ -334,8 +339,9 @@
 void Service::BindDisplayManagerRequest(
     const service_manager::BindSourceInfo& source_info,
     mojom::DisplayManagerRequest request) {
-  // DisplayManagerObservers generally expect there to be at least one display.
-  if (!window_server_->display_manager()->has_displays()) {
+  // Wait for the DisplayManager to be configured before binding display
+  // requests. Otherwise the client sees no displays.
+  if (!window_server_->display_manager()->IsReady()) {
     std::unique_ptr<PendingRequest> pending_request(new PendingRequest);
     pending_request->source_info = source_info;
     pending_request->dm_request.reset(
@@ -392,7 +398,7 @@
     const service_manager::BindSourceInfo& source_info,
     mojom::WindowTreeFactoryRequest request) {
   AddUserIfNecessary(source_info.identity);
-  if (!window_server_->display_manager()->has_displays()) {
+  if (!window_server_->display_manager()->IsReady()) {
     std::unique_ptr<PendingRequest> pending_request(new PendingRequest);
     pending_request->source_info = source_info;
     pending_request->wtf_request.reset(
diff --git a/services/ui/service.h b/services/ui/service.h
index 6bf2463..6dd7d2f3 100644
--- a/services/ui/service.h
+++ b/services/ui/service.h
@@ -66,20 +66,6 @@
   ~Service() override;
 
  private:
-  // How the ScreenManager is configured.
-  enum ScreenManagerConfig {
-    // Initial state.
-    UNKNOWN,
-
-    // ScreenManager runs locally.
-    INTERNAL,
-
-    // Used when the window manager supplies a value of false for
-    // |automatically_create_display_roots|. In this config the ScreenManager
-    // is configured to forward calls.
-    FORWARDING,
-  };
-
   // Holds InterfaceRequests received before the first WindowTreeHost Display
   // has been established.
   struct PendingRequest;
@@ -192,7 +178,6 @@
 
   // Set to true in StartDisplayInit().
   bool is_gpu_ready_ = false;
-  ScreenManagerConfig screen_manager_config_ = ScreenManagerConfig::UNKNOWN;
 
   DISALLOW_COPY_AND_ASSIGN(Service);
 };
diff --git a/services/ui/ws/BUILD.gn b/services/ui/ws/BUILD.gn
index e24c44c..73659a52 100644
--- a/services/ui/ws/BUILD.gn
+++ b/services/ui/ws/BUILD.gn
@@ -33,6 +33,7 @@
     "display.h",
     "display_binding.cc",
     "display_binding.h",
+    "display_creation_config.h",
     "display_manager.cc",
     "display_manager.h",
     "drag_controller.cc",
diff --git a/services/ui/ws/cursor_location_manager.cc b/services/ui/ws/cursor_location_manager.cc
index c26e15a3..a958716 100644
--- a/services/ui/ws/cursor_location_manager.cc
+++ b/services/ui/ws/cursor_location_manager.cc
@@ -14,9 +14,9 @@
 CursorLocationManager::~CursorLocationManager() {}
 
 void CursorLocationManager::OnMouseCursorLocationChanged(
-    const gfx::Point& point) {
+    const gfx::Point& point_in_dip) {
   current_cursor_location_ = static_cast<base::subtle::Atomic32>(
-      (point.x() & 0xFFFF) << 16 | (point.y() & 0xFFFF));
+      (point_in_dip.x() & 0xFFFF) << 16 | (point_in_dip.y() & 0xFFFF));
   if (cursor_location_memory()) {
     base::subtle::NoBarrier_Store(cursor_location_memory(),
                                   current_cursor_location_);
diff --git a/services/ui/ws/cursor_location_manager.h b/services/ui/ws/cursor_location_manager.h
index 6096cdc..85d5a5e 100644
--- a/services/ui/ws/cursor_location_manager.h
+++ b/services/ui/ws/cursor_location_manager.h
@@ -23,8 +23,8 @@
   ~CursorLocationManager();
 
   // Sets the current cursor location to |point|. Atomically writes the location
-  // to shared memory.
-  void OnMouseCursorLocationChanged(const gfx::Point& point);
+  // to shared memory. |point| should be in screen-coord and DIP.
+  void OnMouseCursorLocationChanged(const gfx::Point& point_in_dip);
 
   // Returns a read-only handle to the shared memory which contains the global
   // mouse cursor position. Each call returns a new handle.
diff --git a/services/ui/ws/display.cc b/services/ui/ws/display.cc
index 4625f3ec..a355f19 100644
--- a/services/ui/ws/display.cc
+++ b/services/ui/ws/display.cc
@@ -190,6 +190,8 @@
        it != window_manager_display_root_map_.end(); ++it) {
     if (it->second == display_root) {
       window_manager_display_root_map_.erase(it);
+      if (window_manager_display_root_map_.empty())
+        display_manager()->DestroyDisplay(this);
       return;
     }
   }
@@ -299,10 +301,14 @@
     const display::ViewportMetrics& metrics) {
   platform_display_->UpdateViewportMetrics(metrics);
 
-  if (root_->bounds().size() == metrics.bounds_in_pixels.size())
+  SetBoundsInPixels(metrics.bounds_in_pixels);
+}
+
+void Display::SetBoundsInPixels(const gfx::Rect& bounds_in_pixels) {
+  if (root_->bounds().size() == bounds_in_pixels.size())
     return;
 
-  gfx::Rect new_bounds(metrics.bounds_in_pixels.size());
+  gfx::Rect new_bounds(bounds_in_pixels.size());
   root_->SetBounds(new_bounds, allocator_.GenerateId());
   for (auto& pair : window_manager_display_root_map_)
     pair.second->root()->SetBounds(new_bounds, allocator_.GenerateId());
diff --git a/services/ui/ws/display.h b/services/ui/ws/display.h
index 28cee3f..27e212c 100644
--- a/services/ui/ws/display.h
+++ b/services/ui/ws/display.h
@@ -159,6 +159,8 @@
   // Updates the size of display root ServerWindow and WM root ServerWindow(s).
   void OnViewportMetricsChanged(const display::ViewportMetrics& metrics);
 
+  void SetBoundsInPixels(const gfx::Rect& bounds_in_pixels);
+
   // Returns the root window of the active user.
   ServerWindow* GetActiveRootWindow();
 
diff --git a/services/ui/ws/display_creation_config.h b/services/ui/ws/display_creation_config.h
new file mode 100644
index 0000000..65b1f52
--- /dev/null
+++ b/services/ui/ws/display_creation_config.h
@@ -0,0 +1,25 @@
+// 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_WS_DISPLAY_CREATION_CONFIG_H_
+#define SERVICES_UI_WS_DISPLAY_CREATION_CONFIG_H_
+
+namespace ui {
+namespace ws {
+
+enum class DisplayCreationConfig {
+  // Initial state.
+  UNKNOWN,
+
+  // Displays are created automatically based on the system.
+  AUTOMATIC,
+
+  // Used when the window manager manually creates displays.
+  MANUAL,
+};
+
+}  // namespace ws
+}  // namespace ui
+
+#endif  // SERVICES_UI_WS_DISPLAY_CREATION_CONFIG_H_
diff --git a/services/ui/ws/display_manager.cc b/services/ui/ws/display_manager.cc
index 6575884c..e64964b 100644
--- a/services/ui/ws/display_manager.cc
+++ b/services/ui/ws/display_manager.cc
@@ -7,11 +7,14 @@
 #include <vector>
 
 #include "base/memory/ptr_util.h"
+#include "base/stl_util.h"
 #include "base/trace_event/trace_event.h"
+#include "services/ui/display/screen_manager.h"
 #include "services/ui/display/viewport_metrics.h"
 #include "services/ui/ws/cursor_location_manager.h"
 #include "services/ui/ws/display.h"
 #include "services/ui/ws/display_binding.h"
+#include "services/ui/ws/display_creation_config.h"
 #include "services/ui/ws/event_dispatcher.h"
 #include "services/ui/ws/frame_generator.h"
 #include "services/ui/ws/server_window.h"
@@ -22,6 +25,8 @@
 #include "services/ui/ws/window_manager_window_tree_factory.h"
 #include "services/ui/ws/window_server_delegate.h"
 #include "services/ui/ws/window_tree.h"
+#include "ui/display/display_list.h"
+#include "ui/display/screen_base.h"
 #include "ui/events/event_rewriter.h"
 
 #if defined(OS_CHROMEOS)
@@ -54,11 +59,101 @@
   DestroyAllDisplays();
 }
 
+void DisplayManager::OnDisplayCreationConfigSet() {
+  if (window_server_->display_creation_config() ==
+      DisplayCreationConfig::MANUAL) {
+    for (const auto& pair : user_display_managers_)
+      pair.second->DisableAutomaticNotification();
+  } else {
+    // In AUTOMATIC mode SetDisplayConfiguration() is never called.
+    got_initial_config_from_window_manager_ = true;
+  }
+}
+
+bool DisplayManager::SetDisplayConfiguration(
+    const std::vector<display::Display>& displays,
+    std::vector<ui::mojom::WmViewportMetricsPtr> viewport_metrics,
+    int64_t primary_display_id) {
+  if (window_server_->display_creation_config() !=
+      DisplayCreationConfig::MANUAL) {
+    DVLOG(1) << "SetDisplayConfiguration is only valid when roots manually "
+                "created";
+    return false;
+  }
+  if (displays.size() != viewport_metrics.size()) {
+    DVLOG(1) << "SetDisplayConfiguration called with mismatch in sizes";
+    return false;
+  }
+  size_t primary_display_index = displays.size();
+  std::set<int64_t> display_ids;
+  for (size_t i = 0; i < displays.size(); ++i) {
+    const display::Display& display = displays[i];
+    if (display.id() == display::kInvalidDisplayId) {
+      DVLOG(1) << "SetDisplayConfiguration passed invalid display id";
+      return false;
+    }
+    if (!display_ids.insert(display.id()).second) {
+      DVLOG(1) << "SetDisplayConfiguration passed duplicate display id";
+      return false;
+    }
+    if (display.id() == primary_display_id)
+      primary_display_index = i;
+    Display* ws_display = GetDisplayById(display.id());
+    if (!ws_display) {
+      DVLOG(1) << "SetDisplayConfiguration passed unknown display id "
+               << display.id();
+      return false;
+    }
+  }
+  if (primary_display_index == displays.size()) {
+    DVLOG(1) << "SetDisplayConfiguration primary id not in displays";
+    return false;
+  }
+
+  display::DisplayList& display_list =
+      display::ScreenManager::GetInstance()->GetScreen()->display_list();
+  display_list.AddOrUpdateDisplay(displays[primary_display_index],
+                                  display::DisplayList::Type::PRIMARY);
+  for (size_t i = 0; i < displays.size(); ++i) {
+    Display* ws_display = GetDisplayById(displays[i].id());
+    DCHECK(ws_display);
+    ws_display->SetDisplay(displays[i]);
+    ws_display->SetBoundsInPixels(viewport_metrics[i]->bounds_in_pixels);
+    if (i != primary_display_index) {
+      display_list.AddOrUpdateDisplay(displays[i],
+                                      display::DisplayList::Type::NOT_PRIMARY);
+    }
+  }
+
+  std::set<int64_t> existing_display_ids;
+  for (const display::Display& display : display_list.displays())
+    existing_display_ids.insert(display.id());
+  std::set<int64_t> removed_display_ids =
+      base::STLSetDifference<std::set<int64_t>>(existing_display_ids,
+                                                display_ids);
+  for (int64_t display_id : removed_display_ids)
+    display_list.RemoveDisplay(display_id);
+
+  for (auto& pair : user_display_managers_)
+    pair.second->CallOnDisplaysChanged();
+
+  if (!got_initial_config_from_window_manager_) {
+    got_initial_config_from_window_manager_ = true;
+    window_server_->delegate()->OnFirstDisplayReady();
+  }
+
+  return true;
+}
+
 UserDisplayManager* DisplayManager::GetUserDisplayManager(
     const UserId& user_id) {
   if (!user_display_managers_.count(user_id)) {
     user_display_managers_[user_id] =
         base::MakeUnique<UserDisplayManager>(window_server_, user_id);
+    if (window_server_->display_creation_config() ==
+        DisplayCreationConfig::MANUAL) {
+      user_display_managers_[user_id]->DisableAutomaticNotification();
+    }
   }
   return user_display_managers_[user_id].get();
 }
@@ -77,10 +172,19 @@
   pending_displays_.insert(display);
 }
 
-void DisplayManager::AddDisplayForWindowManager(
+Display* DisplayManager::AddDisplayForWindowManager(
+    bool is_primary_display,
     const display::Display& display,
     const display::ViewportMetrics& metrics) {
+  DCHECK_EQ(DisplayCreationConfig::MANUAL,
+            window_server_->display_creation_config());
+  const display::DisplayList::Type display_type =
+      is_primary_display ? display::DisplayList::Type::PRIMARY
+                         : display::DisplayList::Type::NOT_PRIMARY;
+  display::ScreenManager::GetInstance()->GetScreen()->display_list().AddDisplay(
+      display, display_type);
   OnDisplayAdded(display, metrics);
+  return GetDisplayById(display.id());
 }
 
 void DisplayManager::DestroyDisplay(Display* display) {
@@ -180,7 +284,8 @@
 void DisplayManager::OnDisplayAcceleratedWidgetAvailable(Display* display) {
   DCHECK_NE(0u, pending_displays_.count(display));
   DCHECK_EQ(0u, displays_.count(display));
-  const bool is_first_display = displays_.empty();
+  const bool is_first_display =
+      displays_.empty() && got_initial_config_from_window_manager_;
   displays_.insert(display);
   pending_displays_.erase(display);
   if (event_rewriter_)
@@ -199,26 +304,35 @@
                                            const UserId& active_id) {
   WindowManagerState* previous_window_manager_state =
       window_server_->GetWindowManagerStateForUser(previously_active_id);
-  gfx::Point mouse_location_on_screen;
+  gfx::Point mouse_location_on_display;
+  int64_t mouse_display_id = 0;
   if (previous_window_manager_state) {
-    mouse_location_on_screen = previous_window_manager_state->event_dispatcher()
-                                   ->mouse_pointer_last_location();
+    mouse_location_on_display =
+        previous_window_manager_state->event_dispatcher()
+            ->mouse_pointer_last_location();
+    mouse_display_id = previous_window_manager_state->event_dispatcher()
+                           ->mouse_pointer_display_id();
     previous_window_manager_state->Deactivate();
   }
 
   WindowManagerState* current_window_manager_state =
       window_server_->GetWindowManagerStateForUser(active_id);
   if (current_window_manager_state)
-    current_window_manager_state->Activate(mouse_location_on_screen);
+    current_window_manager_state->Activate(mouse_location_on_display,
+                                           mouse_display_id);
+}
+
+void DisplayManager::CreateDisplay(const display::Display& display,
+                                   const display::ViewportMetrics& metrics) {
+  ws::Display* ws_display = new ws::Display(window_server_);
+  ws_display->SetDisplay(display);
+  ws_display->Init(metrics, nullptr);
 }
 
 void DisplayManager::OnDisplayAdded(const display::Display& display,
                                     const display::ViewportMetrics& metrics) {
   DVLOG(3) << "OnDisplayAdded: " << display.ToString();
-
-  ws::Display* ws_display = new ws::Display(window_server_);
-  ws_display->SetDisplay(display);
-  ws_display->Init(metrics, nullptr);
+  CreateDisplay(display, metrics);
 }
 
 void DisplayManager::OnDisplayRemoved(int64_t display_id) {
diff --git a/services/ui/ws/display_manager.h b/services/ui/ws/display_manager.h
index 99bf820..2fc3a80 100644
--- a/services/ui/ws/display_manager.h
+++ b/services/ui/ws/display_manager.h
@@ -11,6 +11,7 @@
 
 #include "base/macros.h"
 #include "services/ui/display/screen_manager_delegate.h"
+#include "services/ui/public/interfaces/window_manager_constants.mojom.h"
 #include "services/ui/ws/ids.h"
 #include "services/ui/ws/user_id.h"
 #include "services/ui/ws/user_id_tracker_observer.h"
@@ -37,6 +38,23 @@
   DisplayManager(WindowServer* window_server, UserIdTracker* user_id_tracker);
   ~DisplayManager() override;
 
+  // Called once WindowServer::display_creation_config() has been determined.
+  void OnDisplayCreationConfigSet();
+
+  // Indicates the display configuration is valid. Set to true when the
+  // display_creation_config() has been determined and the config is
+  // AUTOMATIC, or MANUAL and the SetDisplayConfiguration() has been called.
+  bool got_initial_config_from_window_manager() const {
+    return got_initial_config_from_window_manager_;
+  }
+
+  // Sets the display configuration from the window manager. Returns true
+  // on success, false if the arguments aren't valid.
+  bool SetDisplayConfiguration(
+      const std::vector<display::Display>& displays,
+      std::vector<ui::mojom::WmViewportMetricsPtr> viewport_metrics,
+      int64_t primary_display_id);
+
   // Returns the UserDisplayManager for |user_id|. DisplayManager owns the
   // return value.
   UserDisplayManager* GetUserDisplayManager(const UserId& user_id);
@@ -48,8 +66,9 @@
   // TODO(sky): make add take a scoped_ptr.
   void AddDisplay(Display* display);
   // Called when the window manager explicitly adds a new display.
-  void AddDisplayForWindowManager(const display::Display& display,
-                                  const display::ViewportMetrics& metrics);
+  Display* AddDisplayForWindowManager(bool is_primary_display,
+                                      const display::Display& display,
+                                      const display::ViewportMetrics& metrics);
   void DestroyDisplay(Display* display);
   void DestroyAllDisplays();
   const std::set<Display*>& displays() { return displays_; }
@@ -73,7 +92,9 @@
   WindowManagerDisplayRoot* GetWindowManagerDisplayRoot(
       const ServerWindow* window);
 
-  bool has_displays() const { return !displays_.empty(); }
+  bool IsReady() const {
+    return !displays_.empty() && got_initial_config_from_window_manager_;
+  }
   bool has_active_or_pending_displays() const {
     return !displays_.empty() || !pending_displays_.empty();
   }
@@ -93,6 +114,11 @@
   void OnActiveUserIdChanged(const UserId& previously_active_id,
                              const UserId& active_id) override;
 
+  void CreateDisplay(const display::Display& display,
+                     const display::ViewportMetrics& metrics);
+
+  // NOTE: these functions are *not* called when the WindowManager manually
+  // creates roots.
   // display::ScreenManagerDelegate:
   void OnDisplayAdded(const display::Display& display,
                       const display::ViewportMetrics& metrics) override;
@@ -120,6 +146,8 @@
   // ID to use for next root node.
   ClientSpecificId next_root_id_;
 
+  bool got_initial_config_from_window_manager_ = false;
+
   DISALLOW_COPY_AND_ASSIGN(DisplayManager);
 };
 
diff --git a/services/ui/ws/event_dispatcher.cc b/services/ui/ws/event_dispatcher.cc
index 83e8b37..bcea3cf9 100644
--- a/services/ui/ws/event_dispatcher.cc
+++ b/services/ui/ws/event_dispatcher.cc
@@ -76,15 +76,17 @@
   mouse_button_down_ = false;
 }
 
-void EventDispatcher::SetMousePointerScreenLocation(
-    const gfx::Point& screen_location) {
+void EventDispatcher::SetMousePointerDisplayLocation(
+    const gfx::Point& display_location,
+    const int64_t display_id) {
   DCHECK(pointer_targets_.empty());
-  mouse_pointer_last_location_ = screen_location;
+  mouse_pointer_last_location_ = display_location;
+  mouse_pointer_display_id_ = display_id;
   UpdateCursorProviderByLastKnownLocation();
   // Write our initial location back to our shared screen coordinate. This
   // shouldn't cause problems because we already read the cursor before we
   // process any events in views during window construction.
-  delegate_->OnMouseCursorLocationChanged(screen_location);
+  delegate_->OnMouseCursorLocationChanged(display_location, display_id);
 }
 
 ui::CursorData EventDispatcher::GetCurrentMouseCursor() const {
@@ -219,8 +221,8 @@
 
 void EventDispatcher::UpdateNonClientAreaForCurrentWindow() {
   if (mouse_cursor_source_window_) {
-    DeepestWindow deepest_window =
-        FindDeepestVisibleWindowForEvents(mouse_pointer_last_location_);
+    DeepestWindow deepest_window = FindDeepestVisibleWindowForEvents(
+        &mouse_pointer_last_location_, &mouse_pointer_display_id_);
     if (deepest_window.window == mouse_cursor_source_window_) {
       mouse_cursor_in_non_client_area_ = mouse_cursor_source_window_
                                              ? deepest_window.in_non_client_area
@@ -231,14 +233,14 @@
 
 void EventDispatcher::UpdateCursorProviderByLastKnownLocation() {
   if (!mouse_button_down_) {
-    DeepestWindow deepest_window =
-        FindDeepestVisibleWindowForEvents(mouse_pointer_last_location_);
+    DeepestWindow deepest_window = FindDeepestVisibleWindowForEvents(
+        &mouse_pointer_last_location_, &mouse_pointer_display_id_);
     SetMouseCursorSourceWindow(deepest_window.window);
     if (mouse_cursor_source_window_) {
       mouse_cursor_in_non_client_area_ = deepest_window.in_non_client_area;
     } else {
-      gfx::Point location = mouse_pointer_last_location_;
-      SetMouseCursorSourceWindow(delegate_->GetRootWindowContaining(&location));
+      SetMouseCursorSourceWindow(delegate_->GetRootWindowContaining(
+          &mouse_pointer_last_location_, &mouse_pointer_display_id_));
       mouse_cursor_in_non_client_area_ = true;
     }
   }
@@ -273,6 +275,7 @@
 }
 
 void EventDispatcher::ProcessEvent(const ui::Event& event,
+                                   const int64_t display_id,
                                    AcceleratorMatchPhase match_phase) {
 #if !defined(NDEBUG)
   if (match_phase == AcceleratorMatchPhase::POST_ONLY) {
@@ -287,6 +290,7 @@
   previous_event_ = Event::Clone(event);
   previous_accelerator_match_phase_ = match_phase;
 #endif
+  event_display_id_ = display_id;
   if (event.IsKeyEvent()) {
     const ui::KeyEvent* key_event = event.AsKeyEvent();
     if (!key_event->is_char() && match_phase == AcceleratorMatchPhase::ANY) {
@@ -294,7 +298,7 @@
           FindAccelerator(*key_event, ui::mojom::AcceleratorPhase::PRE_TARGET);
       if (pre_target) {
         delegate_->OnAccelerator(
-            pre_target->id(), event,
+            pre_target->id(), event_display_id_, event,
             EventDispatcherDelegate::AcceleratorPhase::PRE);
         return;
       }
@@ -329,19 +333,19 @@
     return;
   }
   ServerWindow* focused_window =
-      delegate_->GetFocusedWindowForEventDispatcher();
+      delegate_->GetFocusedWindowForEventDispatcher(event_display_id_);
   if (focused_window) {
     // Assume key events are for the client area.
     const bool in_nonclient_area = false;
     const ClientSpecificId client_id =
         delegate_->GetEventTargetClientId(focused_window, in_nonclient_area);
-    delegate_->DispatchInputEventToWindow(focused_window, client_id, event,
-                                          post_target);
+    delegate_->DispatchInputEventToWindow(
+        focused_window, client_id, event_display_id_, event, post_target);
     return;
   }
-  delegate_->OnEventTargetNotFound(event);
+  delegate_->OnEventTargetNotFound(event, event_display_id_);
   if (post_target)
-    delegate_->OnAccelerator(post_target->id(), event,
+    delegate_->OnAccelerator(post_target->id(), event_display_id_, event,
                              EventDispatcherDelegate::AcceleratorPhase::POST);
 }
 
@@ -351,7 +355,9 @@
 
   if (is_mouse_event) {
     mouse_pointer_last_location_ = event.root_location();
-    delegate_->OnMouseCursorLocationChanged(event.root_location());
+    mouse_pointer_display_id_ = event_display_id_;
+    delegate_->OnMouseCursorLocationChanged(event.root_location(),
+                                            event_display_id_);
   }
 
   // Release capture on pointer up. For mouse we only release if there are
@@ -399,7 +405,9 @@
         ServerWindow* capture_window = pointer_target.window;
         if (!capture_window) {
           gfx::Point event_location = event.root_location();
-          capture_window = delegate_->GetRootWindowContaining(&event_location);
+          int64_t event_display_id = event_display_id_;
+          capture_window = delegate_->GetRootWindowContaining(
+              &event_location, &event_display_id);
         }
         delegate_->SetNativeCapture(capture_window);
       }
@@ -481,8 +489,9 @@
 EventDispatcher::PointerTarget EventDispatcher::PointerTargetForEvent(
     const ui::LocatedEvent& event) {
   PointerTarget pointer_target;
-  DeepestWindow deepest_window =
-      FindDeepestVisibleWindowForEvents(event.root_location());
+  gfx::Point event_root_location(event.root_location());
+  DeepestWindow deepest_window = FindDeepestVisibleWindowForEvents(
+      &event_root_location, &event_display_id_);
   pointer_target.window =
       modal_window_controller_.GetTargetForWindow(deepest_window.window);
   pointer_target.is_mouse_event = event.IsMousePointerEvent();
@@ -504,7 +513,7 @@
 void EventDispatcher::DispatchToPointerTarget(const PointerTarget& target,
                                               const ui::LocatedEvent& event) {
   if (!target.window) {
-    delegate_->OnEventTargetNotFound(event);
+    delegate_->OnEventTargetNotFound(event, event_display_id_);
     return;
   }
 
@@ -526,7 +535,8 @@
   clone->AsLocatedEvent()->set_location(location);
   // TODO(jonross): add post-target accelerator support once accelerators
   // support pointer events.
-  delegate_->DispatchInputEventToWindow(window, client_id, *clone, nullptr);
+  delegate_->DispatchInputEventToWindow(window, client_id, event_display_id_,
+                                        *clone, nullptr);
 }
 
 void EventDispatcher::CancelPointerEventsToTarget(ServerWindow* window) {
@@ -581,12 +591,10 @@
 }
 
 DeepestWindow EventDispatcher::FindDeepestVisibleWindowForEvents(
-    const gfx::Point& location) {
-  gfx::Point relative_location(location);
-  // For the case of no root.
-  ServerWindow* root = delegate_->GetRootWindowContaining(&relative_location);
-  return root ? ui::ws::FindDeepestVisibleWindowForEvents(root,
-                                                          relative_location)
+    gfx::Point* location,
+    int64_t* display_id) {
+  ServerWindow* root = delegate_->GetRootWindowContaining(location, display_id);
+  return root ? ui::ws::FindDeepestVisibleWindowForEvents(root, *location)
               : DeepestWindow();
 }
 
diff --git a/services/ui/ws/event_dispatcher.h b/services/ui/ws/event_dispatcher.h
index 8b084d7..a99846e8 100644
--- a/services/ui/ws/event_dispatcher.h
+++ b/services/ui/ws/event_dispatcher.h
@@ -59,10 +59,12 @@
   // any events to the delegate.
   void Reset();
 
-  void SetMousePointerScreenLocation(const gfx::Point& screen_location);
+  void SetMousePointerDisplayLocation(const gfx::Point& display_location,
+                                      const int64_t display_id);
   const gfx::Point& mouse_pointer_last_location() const {
     return mouse_pointer_last_location_;
   }
+  int64_t mouse_pointer_display_id() const { return mouse_pointer_display_id_; }
 
   // Returns the cursor for the current target, or POINTER if the mouse is not
   // over a valid target.
@@ -142,7 +144,9 @@
   // ANY and there is a matching accelerator with PRE_TARGET found, than only
   // OnAccelerator() is called. The expectation is after the PRE_TARGET has been
   // handled this is again called with an AcceleratorMatchPhase of POST_ONLY.
-  void ProcessEvent(const ui::Event& event, AcceleratorMatchPhase match_phase);
+  void ProcessEvent(const ui::Event& event,
+                    const int64_t display_id,
+                    AcceleratorMatchPhase match_phase);
 
  private:
   friend class test::EventDispatcherTestApi;
@@ -234,7 +238,8 @@
   Accelerator* FindAccelerator(const ui::KeyEvent& event,
                                const ui::mojom::AcceleratorPhase phase);
 
-  DeepestWindow FindDeepestVisibleWindowForEvents(const gfx::Point& location);
+  DeepestWindow FindDeepestVisibleWindowForEvents(gfx::Point* location,
+                                                  int64_t* display_id);
 
   // Clears the implicit captures in |pointer_targets_|, with the exception of
   // |window|. |window| may be null. |client_id| is the target client of
@@ -265,9 +270,15 @@
   ServerWindow* mouse_cursor_source_window_;
   bool mouse_cursor_in_non_client_area_;
 
-  // The on screen location of the mouse pointer. This can be outside the
-  // bounds of |mouse_cursor_source_window_|, which can capture the cursor.
+  // The location of the mouse pointer in display coordinates. This can be
+  // outside the bounds of |mouse_cursor_source_window_|, which can capture the
+  // cursor.
   gfx::Point mouse_pointer_last_location_;
+  // Id of the display |mouse_pointer_last_location_| is on.
+  int64_t mouse_pointer_display_id_ = display::kInvalidDisplayId;
+
+  // Id of the display the most recent event is on.
+  int64_t event_display_id_ = display::kInvalidDisplayId;
 
   std::map<uint32_t, std::unique_ptr<Accelerator>> accelerators_;
 
diff --git a/services/ui/ws/event_dispatcher_delegate.h b/services/ui/ws/event_dispatcher_delegate.h
index 69b7a1b..28a2a0a1 100644
--- a/services/ui/ws/event_dispatcher_delegate.h
+++ b/services/ui/ws/event_dispatcher_delegate.h
@@ -31,11 +31,13 @@
   };
 
   virtual void OnAccelerator(uint32_t accelerator,
+                             const int64_t display_id,
                              const ui::Event& event,
                              AcceleratorPhase phase) = 0;
 
   virtual void SetFocusedWindowFromEventDispatcher(ServerWindow* window) = 0;
-  virtual ServerWindow* GetFocusedWindowForEventDispatcher() = 0;
+  virtual ServerWindow* GetFocusedWindowForEventDispatcher(
+      const int64_t display_id) = 0;
 
   // Called when capture should be set on the native display. |window| is the
   // window capture is being set on.
@@ -55,11 +57,13 @@
   virtual void OnCaptureChanged(ServerWindow* new_capture,
                                 ServerWindow* old_capture) = 0;
 
-  virtual void OnMouseCursorLocationChanged(const gfx::Point& point) = 0;
+  virtual void OnMouseCursorLocationChanged(const gfx::Point& point,
+                                            const int64_t display_id) = 0;
 
   // Dispatches an event to the specific client.
   virtual void DispatchInputEventToWindow(ServerWindow* target,
                                           ClientSpecificId client_id,
+                                          const int64_t display_id,
                                           const ui::Event& event,
                                           Accelerator* accelerator) = 0;
 
@@ -69,14 +73,20 @@
                                                   bool in_nonclient_area) = 0;
 
   // Returns the window to start searching from at the specified location, or
-  // null if there is a no window containing |location|. |location| should be in
-  // screen coordinates and if a window is returned then |location| will be
-  // updated to be relative to the origin of the window.
-  virtual ServerWindow* GetRootWindowContaining(gfx::Point* location) = 0;
+  // null if there is a no window containing |location_in_display|.
+  // |location_in_display| is in display coordinates and in pixels.
+  // |location_in_display| and |display_id| are updated if the window we
+  // found is on a different display than the originated display.
+  // TODO(riajiang): No need to update |location_in_display| and |display_id|
+  // after ozone drm can tell us the right display the cursor is on for
+  // drag-n-drop events. crbug.com/726470
+  virtual ServerWindow* GetRootWindowContaining(gfx::Point* location_in_display,
+                                                int64_t* display_id) = 0;
 
   // Called when event dispatch could not find a target. OnAccelerator may still
   // be called.
-  virtual void OnEventTargetNotFound(const ui::Event& event) = 0;
+  virtual void OnEventTargetNotFound(const ui::Event& event,
+                                     const int64_t display_id) = 0;
 
  protected:
   virtual ~EventDispatcherDelegate() {}
diff --git a/services/ui/ws/event_dispatcher_unittest.cc b/services/ui/ws/event_dispatcher_unittest.cc
index e6d241b..be3217a 100644
--- a/services/ui/ws/event_dispatcher_unittest.cc
+++ b/services/ui/ws/event_dispatcher_unittest.cc
@@ -106,13 +106,15 @@
  private:
   // EventDispatcherDelegate:
   void OnAccelerator(uint32_t accelerator,
+                     const int64_t display_id,
                      const ui::Event& event,
                      AcceleratorPhase phase) override {
     EXPECT_EQ(0u, last_accelerator_);
     last_accelerator_ = accelerator;
     last_accelerator_phase_ = phase;
   }
-  ServerWindow* GetFocusedWindowForEventDispatcher() override {
+  ServerWindow* GetFocusedWindowForEventDispatcher(
+      const int64_t display_id) override {
     return focused_window_;
   }
   void SetNativeCapture(ServerWindow* window) override {}
@@ -125,9 +127,11 @@
                         ServerWindow* old_capture_window) override {
     lost_capture_window_ = old_capture_window;
   }
-  void OnMouseCursorLocationChanged(const gfx::Point& point) override {}
+  void OnMouseCursorLocationChanged(const gfx::Point& point,
+                                    const int64_t display_id) override {}
   void DispatchInputEventToWindow(ServerWindow* target,
                                   ClientSpecificId client_id,
+                                  const int64_t display_id,
                                   const ui::Event& event,
                                   Accelerator* accelerator) override {
     std::unique_ptr<DispatchedEventDetails> details(new DispatchedEventDetails);
@@ -141,10 +145,12 @@
                                           bool in_nonclient_area) override {
     return in_nonclient_area ? kNonclientAreaId : kClientAreaId;
   }
-  ServerWindow* GetRootWindowContaining(gfx::Point* location) override {
+  ServerWindow* GetRootWindowContaining(gfx::Point* location_in_display,
+                                        int64_t* display_id) override {
     return root_;
   }
-  void OnEventTargetNotFound(const ui::Event& event) override {
+  void OnEventTargetNotFound(const ui::Event& event,
+                             const int64_t display_id) override {
     last_event_target_not_found_ = ui::Event::Clone(event);
   }
 
@@ -200,7 +206,7 @@
     const MouseEventTest& test = tests[i];
     ASSERT_FALSE(dispatcher_delegate->has_queued_events())
         << " unexpected queued events before running " << i;
-    dispatcher->ProcessEvent(ui::PointerEvent(test.input_event),
+    dispatcher->ProcessEvent(ui::PointerEvent(test.input_event), 0,
                              EventDispatcher::AcceleratorMatchPhase::ANY);
 
     std::unique_ptr<DispatchedEventDetails> details =
@@ -341,7 +347,7 @@
   const ui::PointerEvent ui_event(ui::MouseEvent(
       ui::ET_MOUSE_PRESSED, gfx::Point(20, 25), gfx::Point(20, 25),
       base::TimeTicks(), ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON));
-  event_dispatcher()->ProcessEvent(ui_event,
+  event_dispatcher()->ProcessEvent(ui_event, 0,
                                    EventDispatcher::AcceleratorMatchPhase::ANY);
 
   std::unique_ptr<DispatchedEventDetails> details =
@@ -360,7 +366,7 @@
 TEST_F(EventDispatcherTest, ProcessEventNoTarget) {
   // Send event without a target.
   ui::KeyEvent key(ui::ET_KEY_PRESSED, ui::VKEY_A, ui::EF_NONE);
-  event_dispatcher()->ProcessEvent(key,
+  event_dispatcher()->ProcessEvent(key, 0,
                                    EventDispatcher::AcceleratorMatchPhase::ANY);
 
   // Event wasn't dispatched to a target.
@@ -425,7 +431,7 @@
   dispatcher->AddAccelerator(accelerator_1, std::move(matcher));
 
   ui::KeyEvent key(ui::ET_KEY_PRESSED, ui::VKEY_W, ui::EF_CONTROL_DOWN);
-  dispatcher->ProcessEvent(key, EventDispatcher::AcceleratorMatchPhase::ANY);
+  dispatcher->ProcessEvent(key, 0, EventDispatcher::AcceleratorMatchPhase::ANY);
   EXPECT_EQ(accelerator_1,
             event_dispatcher_delegate->GetAndClearLastAccelerator());
 
@@ -433,24 +439,24 @@
   // ignoring.
   key = ui::KeyEvent(ui::ET_KEY_PRESSED, ui::VKEY_W,
                      ui::EF_CONTROL_DOWN | ui::EF_NUM_LOCK_ON);
-  dispatcher->ProcessEvent(key, EventDispatcher::AcceleratorMatchPhase::ANY);
+  dispatcher->ProcessEvent(key, 0, EventDispatcher::AcceleratorMatchPhase::ANY);
   EXPECT_EQ(accelerator_1,
             event_dispatcher_delegate->GetAndClearLastAccelerator());
 
   key = ui::KeyEvent(ui::ET_KEY_PRESSED, ui::VKEY_W, ui::EF_NONE);
-  dispatcher->ProcessEvent(key, EventDispatcher::AcceleratorMatchPhase::ANY);
+  dispatcher->ProcessEvent(key, 0, EventDispatcher::AcceleratorMatchPhase::ANY);
   EXPECT_EQ(0u, event_dispatcher_delegate->GetAndClearLastAccelerator());
 
   uint32_t accelerator_2 = 2;
   matcher = ui::CreateKeyMatcher(ui::mojom::KeyboardCode::W,
                                  ui::mojom::kEventFlagNone);
   dispatcher->AddAccelerator(accelerator_2, std::move(matcher));
-  dispatcher->ProcessEvent(key, EventDispatcher::AcceleratorMatchPhase::ANY);
+  dispatcher->ProcessEvent(key, 0, EventDispatcher::AcceleratorMatchPhase::ANY);
   EXPECT_EQ(accelerator_2,
             event_dispatcher_delegate->GetAndClearLastAccelerator());
 
   dispatcher->RemoveAccelerator(accelerator_2);
-  dispatcher->ProcessEvent(key, EventDispatcher::AcceleratorMatchPhase::ANY);
+  dispatcher->ProcessEvent(key, 0, EventDispatcher::AcceleratorMatchPhase::ANY);
   EXPECT_EQ(0u, event_dispatcher_delegate->GetAndClearLastAccelerator());
 }
 
@@ -468,7 +474,7 @@
 
   ui::KeyEvent key(ui::ET_KEY_PRESSED, ui::VKEY_W, ui::EF_CONTROL_DOWN);
   // The post-target accelerator should be fired if there is no focused window.
-  dispatcher->ProcessEvent(key, EventDispatcher::AcceleratorMatchPhase::ANY);
+  dispatcher->ProcessEvent(key, 0, EventDispatcher::AcceleratorMatchPhase::ANY);
   EXPECT_EQ(accelerator_1,
             event_dispatcher_delegate->GetAndClearLastAccelerator());
   std::unique_ptr<DispatchedEventDetails> details =
@@ -480,7 +486,7 @@
   event_dispatcher_delegate->SetFocusedWindowFromEventDispatcher(child.get());
 
   // With a focused window the event should be dispatched.
-  dispatcher->ProcessEvent(key, EventDispatcher::AcceleratorMatchPhase::ANY);
+  dispatcher->ProcessEvent(key, 0, EventDispatcher::AcceleratorMatchPhase::ANY);
   EXPECT_EQ(0u, event_dispatcher_delegate->GetAndClearLastAccelerator());
   details = event_dispatcher_delegate->GetAndAdvanceDispatchedEventDetails();
   EXPECT_TRUE(details);
@@ -492,7 +498,7 @@
   EXPECT_FALSE(accelerator_weak_ptr);
 
   // Post deletion there should be no accelerator
-  dispatcher->ProcessEvent(key, EventDispatcher::AcceleratorMatchPhase::ANY);
+  dispatcher->ProcessEvent(key, 0, EventDispatcher::AcceleratorMatchPhase::ANY);
   EXPECT_EQ(0u, event_dispatcher_delegate->GetAndClearLastAccelerator());
   details = event_dispatcher_delegate->GetAndAdvanceDispatchedEventDetails();
   EXPECT_TRUE(details);
@@ -527,14 +533,14 @@
   // Dispatch for ANY, which should trigger PRE and not call
   // DispatchInputEventToWindow().
   ui::KeyEvent key(ui::ET_KEY_PRESSED, ui::VKEY_W, ui::EF_CONTROL_DOWN);
-  dispatcher->ProcessEvent(key, EventDispatcher::AcceleratorMatchPhase::ANY);
+  dispatcher->ProcessEvent(key, 0, EventDispatcher::AcceleratorMatchPhase::ANY);
   EXPECT_EQ(EventDispatcherDelegate::AcceleratorPhase::PRE,
             event_dispatcher_delegate->last_accelerator_phase());
   EXPECT_EQ(pre_id, event_dispatcher_delegate->GetAndClearLastAccelerator());
   EXPECT_FALSE(event_dispatcher_delegate->has_queued_events());
 
   // Dispatch for POST, which should trigger POST.
-  dispatcher->ProcessEvent(key,
+  dispatcher->ProcessEvent(key, 0,
                            EventDispatcher::AcceleratorMatchPhase::POST_ONLY);
   std::unique_ptr<DispatchedEventDetails> details =
       event_dispatcher_delegate->GetAndAdvanceDispatchedEventDetails();
@@ -645,7 +651,7 @@
   const ui::PointerEvent press_event(ui::MouseEvent(
       ui::ET_MOUSE_PRESSED, gfx::Point(12, 12), gfx::Point(12, 12),
       base::TimeTicks(), ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON));
-  dispatcher->ProcessEvent(press_event,
+  dispatcher->ProcessEvent(press_event, 0,
                            EventDispatcher::AcceleratorMatchPhase::ANY);
 
   // Events should target child and be in the non-client area.
@@ -660,7 +666,7 @@
   const ui::PointerEvent move_event(
       ui::MouseEvent(ui::ET_MOUSE_MOVED, gfx::Point(17, 18), gfx::Point(17, 18),
                      base::TimeTicks(), ui::EF_LEFT_MOUSE_BUTTON, 0));
-  dispatcher->ProcessEvent(move_event,
+  dispatcher->ProcessEvent(move_event, 0,
                            EventDispatcher::AcceleratorMatchPhase::ANY);
 
   // Still same target.
@@ -673,7 +679,7 @@
   const ui::PointerEvent release_event(ui::MouseEvent(
       ui::ET_MOUSE_RELEASED, gfx::Point(17, 18), gfx::Point(17, 18),
       base::TimeTicks(), ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON));
-  dispatcher->ProcessEvent(release_event,
+  dispatcher->ProcessEvent(release_event, 0,
                            EventDispatcher::AcceleratorMatchPhase::ANY);
 
   // The event should not have been dispatched to the delegate.
@@ -687,7 +693,7 @@
   const ui::PointerEvent press_event2(ui::MouseEvent(
       ui::ET_MOUSE_PRESSED, gfx::Point(21, 22), gfx::Point(21, 22),
       base::TimeTicks(), ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON));
-  dispatcher->ProcessEvent(press_event2,
+  dispatcher->ProcessEvent(press_event2, 0,
                            EventDispatcher::AcceleratorMatchPhase::ANY);
   details = event_dispatcher_delegate->GetAndAdvanceDispatchedEventDetails();
   EXPECT_TRUE(event_dispatcher_delegate->has_queued_events());
@@ -718,7 +724,7 @@
   const ui::PointerEvent press_event(ui::MouseEvent(
       ui::ET_MOUSE_PRESSED, gfx::Point(28, 11), gfx::Point(28, 11),
       base::TimeTicks(), ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON));
-  event_dispatcher()->ProcessEvent(press_event,
+  event_dispatcher()->ProcessEvent(press_event, 0,
                                    EventDispatcher::AcceleratorMatchPhase::ANY);
 
   // Events should target child and be in the client area.
@@ -740,7 +746,7 @@
   const ui::PointerEvent move1(ui::MouseEvent(
       ui::ET_MOUSE_MOVED, gfx::Point(11, 11), gfx::Point(11, 11),
       base::TimeTicks(), ui::EF_LEFT_MOUSE_BUTTON, 0));
-  event_dispatcher()->ProcessEvent(move1,
+  event_dispatcher()->ProcessEvent(move1, 0,
                                    EventDispatcher::AcceleratorMatchPhase::ANY);
 
   // Event went through the child window and hit the root.
@@ -755,7 +761,7 @@
   const ui::PointerEvent move2(ui::MouseEvent(
       ui::ET_MOUSE_MOVED, gfx::Point(11, 12), gfx::Point(11, 12),
       base::TimeTicks(), ui::EF_LEFT_MOUSE_BUTTON, 0));
-  event_dispatcher()->ProcessEvent(move2,
+  event_dispatcher()->ProcessEvent(move2, 0,
                                    EventDispatcher::AcceleratorMatchPhase::ANY);
 
   // Mouse exits the root.
@@ -786,7 +792,7 @@
   const ui::PointerEvent press_event(ui::MouseEvent(
       ui::ET_MOUSE_PRESSED, gfx::Point(12, 12), gfx::Point(12, 12),
       base::TimeTicks(), ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON));
-  dispatcher->ProcessEvent(press_event,
+  dispatcher->ProcessEvent(press_event, 0,
                            EventDispatcher::AcceleratorMatchPhase::ANY);
   std::unique_ptr<DispatchedEventDetails> details =
       event_dispatcher_delegate->GetAndAdvanceDispatchedEventDetails();
@@ -800,7 +806,7 @@
   const ui::PointerEvent touch_event(ui::TouchEvent(
       ui::ET_TOUCH_PRESSED, gfx::Point(53, 54), base::TimeTicks(),
       ui::PointerDetails(ui::EventPointerType::POINTER_TYPE_TOUCH, 2)));
-  dispatcher->ProcessEvent(touch_event,
+  dispatcher->ProcessEvent(touch_event, 0,
                            EventDispatcher::AcceleratorMatchPhase::ANY);
   details = event_dispatcher_delegate->GetAndAdvanceDispatchedEventDetails();
   EXPECT_FALSE(event_dispatcher_delegate->has_queued_events());
@@ -824,7 +830,7 @@
   const ui::PointerEvent touch_event1(ui::TouchEvent(
       ui::ET_TOUCH_PRESSED, gfx::Point(12, 13), base::TimeTicks(),
       ui::PointerDetails(ui::EventPointerType::POINTER_TYPE_TOUCH, 1)));
-  dispatcher->ProcessEvent(touch_event1,
+  dispatcher->ProcessEvent(touch_event1, 0,
                            EventDispatcher::AcceleratorMatchPhase::ANY);
   std::unique_ptr<DispatchedEventDetails> details =
       event_dispatcher_delegate->GetAndAdvanceDispatchedEventDetails();
@@ -834,7 +840,7 @@
   const ui::PointerEvent drag_event1(ui::TouchEvent(
       ui::ET_TOUCH_MOVED, gfx::Point(53, 54), base::TimeTicks(),
       ui::PointerDetails(ui::EventPointerType::POINTER_TYPE_TOUCH, 1)));
-  dispatcher->ProcessEvent(drag_event1,
+  dispatcher->ProcessEvent(drag_event1, 0,
                            EventDispatcher::AcceleratorMatchPhase::ANY);
   details = event_dispatcher_delegate->GetAndAdvanceDispatchedEventDetails();
   EXPECT_EQ(child1.get(), details->window);
@@ -843,7 +849,7 @@
   const ui::PointerEvent touch_event2(ui::TouchEvent(
       ui::ET_TOUCH_PRESSED, gfx::Point(54, 55), base::TimeTicks(),
       ui::PointerDetails(ui::EventPointerType::POINTER_TYPE_TOUCH, 2)));
-  dispatcher->ProcessEvent(touch_event2,
+  dispatcher->ProcessEvent(touch_event2, 0,
                            EventDispatcher::AcceleratorMatchPhase::ANY);
   details = event_dispatcher_delegate->GetAndAdvanceDispatchedEventDetails();
   EXPECT_EQ(child2.get(), details->window);
@@ -852,13 +858,13 @@
   const ui::PointerEvent drag_event2(ui::TouchEvent(
       ui::ET_TOUCH_MOVED, gfx::Point(13, 14), base::TimeTicks(),
       ui::PointerDetails(ui::EventPointerType::POINTER_TYPE_TOUCH, 2)));
-  dispatcher->ProcessEvent(drag_event2,
+  dispatcher->ProcessEvent(drag_event2, 0,
                            EventDispatcher::AcceleratorMatchPhase::ANY);
   details = event_dispatcher_delegate->GetAndAdvanceDispatchedEventDetails();
   EXPECT_EQ(child2.get(), details->window);
 
   // Drag again with id 1, child1 should continue to get it.
-  dispatcher->ProcessEvent(drag_event1,
+  dispatcher->ProcessEvent(drag_event1, 0,
                            EventDispatcher::AcceleratorMatchPhase::ANY);
   details = event_dispatcher_delegate->GetAndAdvanceDispatchedEventDetails();
   EXPECT_EQ(child1.get(), details->window);
@@ -867,14 +873,14 @@
   const ui::PointerEvent touch_release(ui::TouchEvent(
       ui::ET_TOUCH_RELEASED, gfx::Point(54, 55), base::TimeTicks(),
       ui::PointerDetails(ui::EventPointerType::POINTER_TYPE_TOUCH, 1)));
-  dispatcher->ProcessEvent(touch_release,
+  dispatcher->ProcessEvent(touch_release, 0,
                            EventDispatcher::AcceleratorMatchPhase::ANY);
   details = event_dispatcher_delegate->GetAndAdvanceDispatchedEventDetails();
   EXPECT_EQ(child1.get(), details->window);
   const ui::PointerEvent touch_event3(ui::TouchEvent(
       ui::ET_TOUCH_PRESSED, gfx::Point(54, 55), base::TimeTicks(),
       ui::PointerDetails(ui::EventPointerType::POINTER_TYPE_TOUCH, 2)));
-  dispatcher->ProcessEvent(touch_event3,
+  dispatcher->ProcessEvent(touch_event3, 0,
                            EventDispatcher::AcceleratorMatchPhase::ANY);
   details = event_dispatcher_delegate->GetAndAdvanceDispatchedEventDetails();
   EXPECT_EQ(child2.get(), details->window);
@@ -894,7 +900,7 @@
   const ui::PointerEvent touch_event1(ui::TouchEvent(
       ui::ET_TOUCH_PRESSED, gfx::Point(12, 13), base::TimeTicks(),
       ui::PointerDetails(ui::EventPointerType::POINTER_TYPE_TOUCH, 1)));
-  dispatcher->ProcessEvent(touch_event1,
+  dispatcher->ProcessEvent(touch_event1, 0,
                            EventDispatcher::AcceleratorMatchPhase::ANY);
   std::unique_ptr<DispatchedEventDetails> details =
       event_dispatcher_delegate->GetAndAdvanceDispatchedEventDetails();
@@ -907,7 +913,7 @@
   const ui::PointerEvent drag_event1(ui::TouchEvent(
       ui::ET_TOUCH_MOVED, gfx::Point(53, 54), base::TimeTicks(),
       ui::PointerDetails(ui::EventPointerType::POINTER_TYPE_TOUCH, 1)));
-  dispatcher->ProcessEvent(drag_event1,
+  dispatcher->ProcessEvent(drag_event1, 0,
                            EventDispatcher::AcceleratorMatchPhase::ANY);
   details = event_dispatcher_delegate->GetAndAdvanceDispatchedEventDetails();
   EXPECT_EQ(nullptr, details.get());
@@ -928,7 +934,7 @@
   const ui::PointerEvent ui_event(ui::MouseEvent(
       ui::ET_MOUSE_PRESSED, gfx::Point(8, 9), gfx::Point(8, 9),
       base::TimeTicks(), ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON));
-  dispatcher->ProcessEvent(ui_event,
+  dispatcher->ProcessEvent(ui_event, 0,
                            EventDispatcher::AcceleratorMatchPhase::ANY);
   std::unique_ptr<DispatchedEventDetails> details =
       event_dispatcher_delegate->GetAndAdvanceDispatchedEventDetails();
@@ -938,7 +944,7 @@
   const ui::PointerEvent release_event(ui::MouseEvent(
       ui::ET_MOUSE_RELEASED, gfx::Point(8, 9), gfx::Point(8, 9),
       base::TimeTicks(), ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON));
-  dispatcher->ProcessEvent(release_event,
+  dispatcher->ProcessEvent(release_event, 0,
                            EventDispatcher::AcceleratorMatchPhase::ANY);
   details = event_dispatcher_delegate->GetAndAdvanceDispatchedEventDetails();
   EXPECT_FALSE(event_dispatcher_delegate->has_queued_events());
@@ -948,7 +954,7 @@
   // Change the extended hit test region and send event in extended hit test
   // region. Should result in exit for root, followed by press for child.
   child->set_extended_hit_test_region(gfx::Insets(5, 5, 5, 5));
-  dispatcher->ProcessEvent(ui_event,
+  dispatcher->ProcessEvent(ui_event, 0,
                            EventDispatcher::AcceleratorMatchPhase::ANY);
   details = event_dispatcher_delegate->GetAndAdvanceDispatchedEventDetails();
   EXPECT_EQ(root, details->window);
@@ -1014,7 +1020,7 @@
     const ui::PointerEvent left_press_event(ui::MouseEvent(
         ui::ET_MOUSE_PRESSED, gfx::Point(5, 5), gfx::Point(5, 5),
         base::TimeTicks(), ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON));
-    dispatcher->ProcessEvent(left_press_event,
+    dispatcher->ProcessEvent(left_press_event, 0,
                              EventDispatcher::AcceleratorMatchPhase::ANY);
 
     // Events should target child.
@@ -1031,7 +1037,7 @@
         ui::ET_MOUSE_PRESSED, gfx::Point(5, 5), gfx::Point(5, 5),
         base::TimeTicks(), ui::EF_LEFT_MOUSE_BUTTON | ui::EF_RIGHT_MOUSE_BUTTON,
         ui::EF_RIGHT_MOUSE_BUTTON));
-    dispatcher->ProcessEvent(right_press_event,
+    dispatcher->ProcessEvent(right_press_event, 0,
                              EventDispatcher::AcceleratorMatchPhase::ANY);
     details = event_dispatcher_delegate->GetAndAdvanceDispatchedEventDetails();
     EXPECT_TRUE(IsMouseButtonDown());
@@ -1041,7 +1047,7 @@
         ui::ET_MOUSE_RELEASED, gfx::Point(5, 5), gfx::Point(5, 5),
         base::TimeTicks(), ui::EF_LEFT_MOUSE_BUTTON | ui::EF_RIGHT_MOUSE_BUTTON,
         ui::EF_LEFT_MOUSE_BUTTON));
-    dispatcher->ProcessEvent(left_release_event,
+    dispatcher->ProcessEvent(left_release_event, 0,
                              EventDispatcher::AcceleratorMatchPhase::ANY);
     details = event_dispatcher_delegate->GetAndAdvanceDispatchedEventDetails();
     EXPECT_TRUE(IsMouseButtonDown());
@@ -1050,7 +1056,7 @@
     const ui::PointerEvent touch_event(ui::TouchEvent(
         ui::ET_TOUCH_PRESSED, gfx::Point(15, 15), base::TimeTicks(),
         ui::PointerDetails(ui::EventPointerType::POINTER_TYPE_TOUCH, 2)));
-    dispatcher->ProcessEvent(touch_event,
+    dispatcher->ProcessEvent(touch_event, 0,
                              EventDispatcher::AcceleratorMatchPhase::ANY);
     details = event_dispatcher_delegate->GetAndAdvanceDispatchedEventDetails();
     EXPECT_TRUE(IsMouseButtonDown());
@@ -1060,7 +1066,7 @@
         ui::MouseEvent(ui::ET_MOUSE_MOVED, gfx::Point(15, 5), gfx::Point(15, 5),
                        base::TimeTicks(), ui::EF_RIGHT_MOUSE_BUTTON,
                        ui::EF_RIGHT_MOUSE_BUTTON));
-    dispatcher->ProcessEvent(move_event,
+    dispatcher->ProcessEvent(move_event, 0,
                              EventDispatcher::AcceleratorMatchPhase::ANY);
     details = event_dispatcher_delegate->GetAndAdvanceDispatchedEventDetails();
     EXPECT_TRUE(IsMouseButtonDown());
@@ -1070,7 +1076,7 @@
         ui::MouseEvent(ui::ET_MOUSE_RELEASED, gfx::Point(5, 5),
                        gfx::Point(5, 5), base::TimeTicks(),
                        ui::EF_RIGHT_MOUSE_BUTTON, ui::EF_RIGHT_MOUSE_BUTTON));
-    dispatcher->ProcessEvent(right_release_event,
+    dispatcher->ProcessEvent(right_release_event, 0,
                              EventDispatcher::AcceleratorMatchPhase::ANY);
     details = event_dispatcher_delegate->GetAndAdvanceDispatchedEventDetails();
     EXPECT_FALSE(IsMouseButtonDown());
@@ -1082,7 +1088,7 @@
     const ui::PointerEvent press_event(ui::MouseEvent(
         ui::ET_MOUSE_PRESSED, gfx::Point(5, 5), gfx::Point(5, 5),
         base::TimeTicks(), ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON));
-    dispatcher->ProcessEvent(press_event,
+    dispatcher->ProcessEvent(press_event, 0,
                              EventDispatcher::AcceleratorMatchPhase::ANY);
 
     // Events should target the root.
@@ -1142,7 +1148,7 @@
     const ui::PointerEvent touch_event(ui::TouchEvent(
         ui::ET_TOUCH_PRESSED, gfx::Point(12, 13), base::TimeTicks(),
         ui::PointerDetails(ui::EventPointerType::POINTER_TYPE_TOUCH, 1)));
-    dispatcher->ProcessEvent(touch_event,
+    dispatcher->ProcessEvent(touch_event, 0,
                              EventDispatcher::AcceleratorMatchPhase::ANY);
   }
 
@@ -1175,7 +1181,7 @@
   const ui::PointerEvent press_event(ui::MouseEvent(
       ui::ET_MOUSE_PRESSED, gfx::Point(15, 15), gfx::Point(15, 15),
       base::TimeTicks(), ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON));
-  dispatcher->ProcessEvent(press_event,
+  dispatcher->ProcessEvent(press_event, 0,
                            EventDispatcher::AcceleratorMatchPhase::ANY);
 
   // Events should target the root.
@@ -1196,7 +1202,7 @@
     const ui::PointerEvent press_event(ui::MouseEvent(
         ui::ET_MOUSE_PRESSED, gfx::Point(5, 5), gfx::Point(5, 5),
         base::TimeTicks(), ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON));
-    dispatcher->ProcessEvent(press_event,
+    dispatcher->ProcessEvent(press_event, 0,
                              EventDispatcher::AcceleratorMatchPhase::ANY);
 
     std::unique_ptr<DispatchedEventDetails> details =
@@ -1208,7 +1214,7 @@
     const ui::PointerEvent touch_event(ui::TouchEvent(
         ui::ET_TOUCH_PRESSED, gfx::Point(12, 13), base::TimeTicks(),
         ui::PointerDetails(ui::EventPointerType::POINTER_TYPE_TOUCH, 1)));
-    dispatcher->ProcessEvent(touch_event,
+    dispatcher->ProcessEvent(touch_event, 0,
                              EventDispatcher::AcceleratorMatchPhase::ANY);
   }
 
@@ -1279,7 +1285,7 @@
   const ui::PointerEvent press_event(ui::MouseEvent(
       ui::ET_MOUSE_PRESSED, gfx::Point(6, 6), gfx::Point(6, 6),
       base::TimeTicks(), ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON));
-  event_dispatcher()->ProcessEvent(press_event,
+  event_dispatcher()->ProcessEvent(press_event, 0,
                                    EventDispatcher::AcceleratorMatchPhase::ANY);
 
   // Events should target child and be in the client area.
@@ -1301,7 +1307,7 @@
         ui::ET_MOUSE_PRESSED, gfx::Point(20, 25), gfx::Point(20, 25),
         base::TimeTicks(), ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON));
     event_dispatcher()->ProcessEvent(
-        pointer_event, EventDispatcher::AcceleratorMatchPhase::ANY);
+        pointer_event, 0, EventDispatcher::AcceleratorMatchPhase::ANY);
 
     std::unique_ptr<DispatchedEventDetails> details =
         test_event_dispatcher_delegate()->GetAndAdvanceDispatchedEventDetails();
@@ -1323,7 +1329,7 @@
         ui::PointerDetails(ui::EventPointerType::POINTER_TYPE_TOUCH,
                            touch_id)));
     event_dispatcher()->ProcessEvent(
-        pointer_event, EventDispatcher::AcceleratorMatchPhase::ANY);
+        pointer_event, 0, EventDispatcher::AcceleratorMatchPhase::ANY);
 
     std::unique_ptr<DispatchedEventDetails> details =
         test_event_dispatcher_delegate()->GetAndAdvanceDispatchedEventDetails();
@@ -1350,7 +1356,7 @@
   const ui::PointerEvent ui_event(ui::MouseEvent(
       ui::ET_MOUSE_PRESSED, gfx::Point(20, 25), gfx::Point(20, 25),
       base::TimeTicks(), ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON));
-  event_dispatcher()->ProcessEvent(ui_event,
+  event_dispatcher()->ProcessEvent(ui_event, 0,
                                    EventDispatcher::AcceleratorMatchPhase::ANY);
 
   std::unique_ptr<DispatchedEventDetails> details =
@@ -1394,7 +1400,7 @@
   const ui::PointerEvent mouse_pressed(ui::MouseEvent(
       ui::ET_MOUSE_PRESSED, gfx::Point(15, 15), gfx::Point(15, 15),
       base::TimeTicks(), ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON));
-  event_dispatcher()->ProcessEvent(mouse_pressed,
+  event_dispatcher()->ProcessEvent(mouse_pressed, 0,
                                    EventDispatcher::AcceleratorMatchPhase::ANY);
 
   std::unique_ptr<DispatchedEventDetails> details =
@@ -1427,7 +1433,7 @@
   const ui::PointerEvent mouse_pressed(ui::MouseEvent(
       ui::ET_MOUSE_PRESSED, gfx::Point(55, 15), gfx::Point(55, 15),
       base::TimeTicks(), ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON));
-  event_dispatcher()->ProcessEvent(mouse_pressed,
+  event_dispatcher()->ProcessEvent(mouse_pressed, 0,
                                    EventDispatcher::AcceleratorMatchPhase::ANY);
 
   std::unique_ptr<DispatchedEventDetails> details =
@@ -1463,7 +1469,7 @@
   const ui::PointerEvent mouse_pressed(ui::MouseEvent(
       ui::ET_MOUSE_PRESSED, gfx::Point(75, 15), gfx::Point(75, 15),
       base::TimeTicks(), ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON));
-  event_dispatcher()->ProcessEvent(mouse_pressed,
+  event_dispatcher()->ProcessEvent(mouse_pressed, 0,
                                    EventDispatcher::AcceleratorMatchPhase::ANY);
 
   std::unique_ptr<DispatchedEventDetails> details =
@@ -1500,7 +1506,7 @@
   const ui::PointerEvent mouse_pressed(ui::MouseEvent(
       ui::ET_MOUSE_PRESSED, gfx::Point(25, 25), gfx::Point(25, 25),
       base::TimeTicks(), ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON));
-  event_dispatcher()->ProcessEvent(mouse_pressed,
+  event_dispatcher()->ProcessEvent(mouse_pressed, 0,
                                    EventDispatcher::AcceleratorMatchPhase::ANY);
 
   std::unique_ptr<DispatchedEventDetails> details =
@@ -1529,7 +1535,7 @@
   const ui::PointerEvent mouse_pressed(ui::MouseEvent(
       ui::ET_MOUSE_PRESSED, gfx::Point(15, 15), gfx::Point(15, 15),
       base::TimeTicks(), ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON));
-  event_dispatcher()->ProcessEvent(mouse_pressed,
+  event_dispatcher()->ProcessEvent(mouse_pressed, 0,
                                    EventDispatcher::AcceleratorMatchPhase::ANY);
 
   std::unique_ptr<DispatchedEventDetails> details =
@@ -1559,7 +1565,7 @@
   const ui::PointerEvent mouse_pressed(ui::MouseEvent(
       ui::ET_MOUSE_PRESSED, gfx::Point(45, 15), gfx::Point(45, 15),
       base::TimeTicks(), ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON));
-  event_dispatcher()->ProcessEvent(mouse_pressed,
+  event_dispatcher()->ProcessEvent(mouse_pressed, 0,
                                    EventDispatcher::AcceleratorMatchPhase::ANY);
 
   std::unique_ptr<DispatchedEventDetails> details =
@@ -1609,7 +1615,7 @@
         ui::PointerEvent(ui::TouchEvent(
             ui::ET_TOUCH_PRESSED, kTouchData[i].location, base::TimeTicks(),
             ui::PointerDetails(ui::EventPointerType::POINTER_TYPE_TOUCH, 0))),
-        EventDispatcher::AcceleratorMatchPhase::ANY);
+        0, EventDispatcher::AcceleratorMatchPhase::ANY);
     std::unique_ptr<DispatchedEventDetails> details =
         test_event_dispatcher_delegate()->GetAndAdvanceDispatchedEventDetails();
     ASSERT_TRUE(details) << " details is nullptr " << i;
@@ -1620,7 +1626,7 @@
         ui::PointerEvent(ui::TouchEvent(
             ui::ET_TOUCH_RELEASED, kTouchData[i].location, base::TimeTicks(),
             ui::PointerDetails(ui::EventPointerType::POINTER_TYPE_TOUCH, 0))),
-        EventDispatcher::AcceleratorMatchPhase::ANY);
+        0, EventDispatcher::AcceleratorMatchPhase::ANY);
     test_event_dispatcher_delegate()->GetAndAdvanceDispatchedEventDetails();
   }
 }
@@ -1737,7 +1743,7 @@
   const ui::PointerEvent mouse_pressed(ui::MouseEvent(
       ui::ET_MOUSE_PRESSED, gfx::Point(15, 15), gfx::Point(15, 15),
       base::TimeTicks(), ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON));
-  event_dispatcher()->ProcessEvent(mouse_pressed,
+  event_dispatcher()->ProcessEvent(mouse_pressed, 0,
                                    EventDispatcher::AcceleratorMatchPhase::ANY);
   event_dispatcher()->SetCaptureWindow(w11.get(), kClientAreaId);
 
@@ -1870,7 +1876,7 @@
   root_window()->SetBounds(gfx::Rect(0, 0, 100, 100));
   c1->SetBounds(gfx::Rect(10, 10, 20, 20));
 
-  event_dispatcher()->SetMousePointerScreenLocation(gfx::Point(15, 15));
+  event_dispatcher()->SetMousePointerDisplayLocation(gfx::Point(15, 15), 0);
   EXPECT_EQ(c1.get(), event_dispatcher()->mouse_cursor_source_window());
   c1.reset();
   EXPECT_EQ(nullptr, event_dispatcher()->mouse_cursor_source_window());
diff --git a/services/ui/ws/platform_display_default.cc b/services/ui/ws/platform_display_default.cc
index d576a8d..b474dcf 100644
--- a/services/ui/ws/platform_display_default.cc
+++ b/services/ui/ws/platform_display_default.cc
@@ -178,15 +178,6 @@
   return widget_;
 }
 
-void PlatformDisplayDefault::UpdateEventRootLocation(ui::LocatedEvent* event) {
-  // TODO(riajiang): This is broken for HDPI because it mixes PPs and DIPs. See
-  // http://crbug.com/701036 for details.
-  const display::Display& display = delegate_->GetDisplay();
-  gfx::Point location = event->location();
-  location.Offset(display.bounds().x(), display.bounds().y());
-  event->set_root_location(location);
-}
-
 void PlatformDisplayDefault::OnBoundsChanged(const gfx::Rect& new_bounds) {
   // We only care if the window size has changed.
   if (new_bounds.size() == metrics_.bounds_in_pixels.size())
@@ -202,9 +193,8 @@
 }
 
 void PlatformDisplayDefault::DispatchEvent(ui::Event* event) {
-  if (event->IsLocatedEvent())
-    UpdateEventRootLocation(event->AsLocatedEvent());
-
+  // Event location and event root location are the same, and both in pixels
+  // and display coordinates.
   if (event->IsScrollEvent()) {
     // TODO(moshayedi): crbug.com/602859. Dispatch scroll events as
     // they are once we have proper support for scroll events.
@@ -261,6 +251,7 @@
       std::move(compositor_frame_sink_client),
       mojo::MakeRequest(&display_private));
 
+  display_private->SetDisplayVisible(true);
   frame_generator_ = base::MakeUnique<FrameGenerator>();
   auto frame_sink_client_binding =
       base::MakeUnique<CompositorFrameSinkClientBinding>(
diff --git a/services/ui/ws/platform_display_default.h b/services/ui/ws/platform_display_default.h
index 5ca1ba6..3e28a60f 100644
--- a/services/ui/ws/platform_display_default.h
+++ b/services/ui/ws/platform_display_default.h
@@ -19,7 +19,6 @@
 
 class EventSink;
 class ImageCursors;
-class LocatedEvent;
 class PlatformWindow;
 
 namespace ws {
@@ -52,13 +51,6 @@
   FrameGenerator* GetFrameGenerator() override;
 
  private:
-  // Update the root_location of located events to be relative to the origin
-  // of this display. For example, if the origin of this display is (1800, 0)
-  // and the location of the event is (100, 200) then the root_location will be
-  // updated to be (1900, 200).
-  // TODO(riajiang): This is totally broken with HDPI.
-  void UpdateEventRootLocation(ui::LocatedEvent* event);
-
   // ui::PlatformWindowDelegate:
   void OnBoundsChanged(const gfx::Rect& new_bounds) override;
   void OnDamageRect(const gfx::Rect& damaged_region) override;
diff --git a/services/ui/ws/test_utils.cc b/services/ui/ws/test_utils.cc
index f9735fe..9ee891f 100644
--- a/services/ui/ws/test_utils.cc
+++ b/services/ui/ws/test_utils.cc
@@ -7,11 +7,13 @@
 #include <utility>
 
 #include "base/memory/ptr_util.h"
+#include "base/strings/string_number_conversions.h"
 #include "cc/output/copy_output_request.h"
 #include "gpu/ipc/client/gpu_channel_host.h"
 #include "services/service_manager/public/interfaces/connector.mojom.h"
 #include "services/ui/public/interfaces/cursor/cursor.mojom.h"
 #include "services/ui/ws/display_binding.h"
+#include "services/ui/ws/display_creation_config.h"
 #include "services/ui/ws/display_manager.h"
 #include "services/ui/ws/window_manager_access_policy.h"
 #include "services/ui/ws/window_manager_window_tree_factory.h"
@@ -524,6 +526,17 @@
   return true;
 }
 
+void TestWindowServerDelegate::OnWillCreateTreeForWindowManager(
+    bool automatically_create_display_roots) {
+  if (window_server_->display_creation_config() !=
+      DisplayCreationConfig::UNKNOWN) {
+    return;
+  }
+  window_server_->SetDisplayCreationConfig(
+      automatically_create_display_roots ? DisplayCreationConfig::AUTOMATIC
+                                         : DisplayCreationConfig::MANUAL);
+}
+
 // WindowServerTestHelper  ---------------------------------------------------
 
 WindowServerTestHelper::WindowServerTestHelper()
@@ -621,6 +634,42 @@
 
 // ----------------------------------------------------------------------------
 
+TestDisplayManagerObserver::TestDisplayManagerObserver() : binding_(this) {}
+
+TestDisplayManagerObserver::~TestDisplayManagerObserver() = default;
+
+mojom::DisplayManagerObserverPtr TestDisplayManagerObserver::GetPtr() {
+  return binding_.CreateInterfacePtrAndBind();
+}
+
+std::string TestDisplayManagerObserver::GetAndClearObserverCalls() {
+  std::string result;
+  std::swap(observer_calls_, result);
+  return result;
+}
+
+std::string TestDisplayManagerObserver::DisplayIdsToString(
+    const std::vector<mojom::WsDisplayPtr>& wm_displays) {
+  std::string display_ids;
+  for (const auto& wm_display : wm_displays) {
+    if (!display_ids.empty())
+      display_ids += " ";
+    display_ids += base::Int64ToString(wm_display->display.id());
+  }
+  return display_ids;
+}
+
+void TestDisplayManagerObserver::OnDisplaysChanged(
+    std::vector<mojom::WsDisplayPtr> displays,
+    int64_t primary_display_id,
+    int64_t internal_display_id) {
+  if (!observer_calls_.empty())
+    observer_calls_ += "\n";
+  observer_calls_ += "OnDisplaysChanged " + DisplayIdsToString(displays);
+}
+
+// -----------------------------------------------------------------------------
+
 void AddWindowManager(WindowServer* window_server,
                       const UserId& user_id,
                       bool automatically_create_display_roots) {
diff --git a/services/ui/ws/test_utils.h b/services/ui/ws/test_utils.h
index b5a9886..5cadb9d 100644
--- a/services/ui/ws/test_utils.h
+++ b/services/ui/ws/test_utils.h
@@ -207,9 +207,11 @@
 
   void DispatchInputEventToWindow(ServerWindow* target,
                                   ClientSpecificId client_id,
+                                  const int64_t display_id,
                                   const ui::Event& event,
                                   Accelerator* accelerator) {
-    wms_->DispatchInputEventToWindow(target, client_id, event, accelerator);
+    wms_->DispatchInputEventToWindow(target, client_id, display_id, event,
+                                     accelerator);
   }
 
   ClientSpecificId GetEventTargetClientId(ServerWindow* window,
@@ -597,6 +599,8 @@
       mojom::WindowTreeRequest* tree_request,
       mojom::WindowTreeClientPtr* client) override;
   bool IsTestConfig() const override;
+  void OnWillCreateTreeForWindowManager(
+      bool automatically_create_display_roots) override;
 
  private:
   WindowServer* window_server_ = nullptr;
@@ -690,6 +694,32 @@
 
 // -----------------------------------------------------------------------------
 
+class TestDisplayManagerObserver : public mojom::DisplayManagerObserver {
+ public:
+  TestDisplayManagerObserver();
+  ~TestDisplayManagerObserver() override;
+
+  mojom::DisplayManagerObserverPtr GetPtr();
+
+  std::string GetAndClearObserverCalls();
+
+ private:
+  std::string DisplayIdsToString(
+      const std::vector<mojom::WsDisplayPtr>& wm_displays);
+
+  // mojom::DisplayManagerObserver:
+  void OnDisplaysChanged(std::vector<mojom::WsDisplayPtr> displays,
+                         int64_t primary_display_id,
+                         int64_t internal_display_id) override;
+
+  mojo::Binding<mojom::DisplayManagerObserver> binding_;
+  std::string observer_calls_;
+
+  DISALLOW_COPY_AND_ASSIGN(TestDisplayManagerObserver);
+};
+
+// -----------------------------------------------------------------------------
+
 // Adds a new WM to |window_server| for |user_id|. Creates
 // WindowManagerWindowTreeFactory and associated WindowTree for the WM.
 void AddWindowManager(WindowServer* window_server,
diff --git a/services/ui/ws/user_display_manager.cc b/services/ui/ws/user_display_manager.cc
index b32c531..f52663f 100644
--- a/services/ui/ws/user_display_manager.cc
+++ b/services/ui/ws/user_display_manager.cc
@@ -33,6 +33,18 @@
 
 UserDisplayManager::~UserDisplayManager() {}
 
+void UserDisplayManager::DisableAutomaticNotification() {
+  DCHECK(notify_automatically_);
+  notify_automatically_ = false;
+}
+
+void UserDisplayManager::CallOnDisplaysChanged() {
+  display_manager_observers_.ForAllPtrs(
+      [this](mojom::DisplayManagerObserver* observer) {
+        CallOnDisplaysChanged(observer);
+      });
+}
+
 void UserDisplayManager::OnFrameDecorationValuesChanged() {
   got_valid_frame_decorations_ = true;
   CallOnDisplaysChangedIfNecessary();
@@ -100,13 +112,10 @@
 }
 
 void UserDisplayManager::CallOnDisplaysChangedIfNecessary() {
-  if (!ShouldCallOnDisplaysChanged())
+  if (!notify_automatically_ || !ShouldCallOnDisplaysChanged())
     return;
 
-  display_manager_observers_.ForAllPtrs(
-      [this](mojom::DisplayManagerObserver* observer) {
-        CallOnDisplaysChanged(observer);
-      });
+  CallOnDisplaysChanged();
 }
 
 void UserDisplayManager::CallOnDisplaysChanged(
diff --git a/services/ui/ws/user_display_manager.h b/services/ui/ws/user_display_manager.h
index 755bb70..8b889cda 100644
--- a/services/ui/ws/user_display_manager.h
+++ b/services/ui/ws/user_display_manager.h
@@ -29,6 +29,11 @@
                      const UserId& user_id);
   ~UserDisplayManager() override;
 
+  void DisableAutomaticNotification();
+
+  // Unconditionally calls OnDisplayChanged() on observers.
+  void CallOnDisplaysChanged();
+
   // Called when the frame decorations for this user change.
   void OnFrameDecorationValuesChanged();
 
@@ -78,6 +83,12 @@
   mojo::InterfacePtrSet<mojom::DisplayManagerObserver>
       display_manager_observers_;
 
+  // If true DisplayManagerObservers are notified any time there is a display
+  // change. If false, observers are only notified when CallOnDisplaysChanged()
+  // is explicitly called. This value is true in automatic display creation and
+  // false when in manual mode.
+  bool notify_automatically_ = true;
+
   DISALLOW_COPY_AND_ASSIGN(UserDisplayManager);
 };
 
diff --git a/services/ui/ws/user_display_manager_unittest.cc b/services/ui/ws/user_display_manager_unittest.cc
index f2b3417..f6bbcf7c 100644
--- a/services/ui/ws/user_display_manager_unittest.cc
+++ b/services/ui/ws/user_display_manager_unittest.cc
@@ -36,52 +36,6 @@
 
 const char kUserId1[] = "123";
 
-class TestDisplayManagerObserver : public mojom::DisplayManagerObserver {
- public:
-  TestDisplayManagerObserver() : binding_(this) {}
-  ~TestDisplayManagerObserver() override {}
-
-  mojom::DisplayManagerObserverPtr GetPtr() {
-    return binding_.CreateInterfacePtrAndBind();
-  }
-
-  std::string GetAndClearObserverCalls() {
-    std::string result;
-    std::swap(observer_calls_, result);
-    return result;
-  }
-
- private:
-  void AddCall(const std::string& call) {
-    if (!observer_calls_.empty())
-      observer_calls_ += "\n";
-    observer_calls_ += call;
-  }
-
-  std::string DisplayIdsToString(
-      const std::vector<mojom::WsDisplayPtr>& wm_displays) {
-    std::string display_ids;
-    for (const auto& wm_display : wm_displays) {
-      if (!display_ids.empty())
-        display_ids += " ";
-      display_ids += base::Int64ToString(wm_display->display.id());
-    }
-    return display_ids;
-  }
-
-  // mojom::DisplayManagerObserver:
-  void OnDisplaysChanged(std::vector<mojom::WsDisplayPtr> displays,
-                         int64_t primary_display_id,
-                         int64_t internal_display_id) override {
-    AddCall("OnDisplaysChanged " + DisplayIdsToString(displays));
-  }
-
-  mojo::Binding<mojom::DisplayManagerObserver> binding_;
-  std::string observer_calls_;
-
-  DISALLOW_COPY_AND_ASSIGN(TestDisplayManagerObserver);
-};
-
 mojom::FrameDecorationValuesPtr CreateDefaultFrameDecorationValues() {
   return mojom::FrameDecorationValues::New();
 }
diff --git a/services/ui/ws/window_manager_state.cc b/services/ui/ws/window_manager_state.cc
index 6657f15..a36a9b1 100644
--- a/services/ui/ws/window_manager_state.cc
+++ b/services/ui/ws/window_manager_state.cc
@@ -13,6 +13,7 @@
 #include "services/ui/ws/accelerator.h"
 #include "services/ui/ws/cursor_location_manager.h"
 #include "services/ui/ws/display.h"
+#include "services/ui/ws/display_creation_config.h"
 #include "services/ui/ws/display_manager.h"
 #include "services/ui/ws/platform_display.h"
 #include "services/ui/ws/server_window.h"
@@ -22,6 +23,7 @@
 #include "services/ui/ws/window_server.h"
 #include "services/ui/ws/window_tree.h"
 #include "ui/events/event.h"
+#include "ui/gfx/geometry/dip_util.h"
 
 namespace ui {
 namespace ws {
@@ -154,9 +156,14 @@
     mojom::FrameDecorationValuesPtr values) {
   got_frame_decoration_values_ = true;
   frame_decoration_values_ = values.Clone();
-  display_manager()
-      ->GetUserDisplayManager(user_id())
-      ->OnFrameDecorationValuesChanged();
+  UserDisplayManager* user_display_manager =
+      display_manager()->GetUserDisplayManager(user_id());
+  user_display_manager->OnFrameDecorationValuesChanged();
+  if (window_server()->display_creation_config() ==
+          DisplayCreationConfig::MANUAL &&
+      display_manager()->got_initial_config_from_window_manager()) {
+    user_display_manager->CallOnDisplaysChanged();
+  }
 }
 
 bool WindowManagerState::SetCapture(ServerWindow* window,
@@ -273,10 +280,12 @@
   return window_server()->user_id_tracker()->active_id() == user_id();
 }
 
-void WindowManagerState::Activate(const gfx::Point& mouse_location_on_screen) {
+void WindowManagerState::Activate(const gfx::Point& mouse_location_on_display,
+                                  const int64_t display_id) {
   SetAllRootWindowsVisible(true);
   event_dispatcher_.Reset();
-  event_dispatcher_.SetMousePointerScreenLocation(mouse_location_on_screen);
+  event_dispatcher_.SetMousePointerDisplayLocation(mouse_location_on_display,
+                                                   display_id);
 }
 
 void WindowManagerState::Deactivate() {
@@ -321,9 +330,9 @@
     DCHECK(details->event->IsKeyEvent());
     if (!properties.empty())
       details->event->AsKeyEvent()->SetProperties(properties);
-    event_processing_display_id_ = details->display_id;
     event_dispatcher_.ProcessEvent(
-        *details->event, EventDispatcher::AcceleratorMatchPhase::POST_ONLY);
+        *details->event, details->display_id,
+        EventDispatcher::AcceleratorMatchPhase::POST_ONLY);
   } else {
     // We're not going to process the event any further, notify event observers.
     // We don't do this first to ensure we don't send an event twice to clients.
@@ -395,8 +404,8 @@
 
   if (result == mojom::EventResult::UNHANDLED &&
       details->post_target_accelerator) {
-    OnAccelerator(details->post_target_accelerator->id(), *details->event,
-                  AcceleratorPhase::POST);
+    OnAccelerator(details->post_target_accelerator->id(), details->display_id,
+                  *details->event, AcceleratorPhase::POST);
   }
 
   ProcessNextEventFromQueue();
@@ -417,9 +426,8 @@
 void WindowManagerState::ProcessEventImpl(const ui::Event& event,
                                           int64_t display_id) {
   // Debug accelerators are always checked and don't interfere with processing.
-  ProcessDebugAccelerator(event);
-  event_processing_display_id_ = display_id;
-  event_dispatcher_.ProcessEvent(event,
+  ProcessDebugAccelerator(event, display_id);
+  event_dispatcher_.ProcessEvent(event, display_id,
                                  EventDispatcher::AcceleratorMatchPhase::ANY);
 }
 
@@ -447,8 +455,8 @@
     if (queued_event->processed_target->IsValid()) {
       DispatchInputEventToWindowImpl(
           queued_event->processed_target->window(),
-          queued_event->processed_target->client_id(), *queued_event->event,
-          queued_event->processed_target->accelerator());
+          queued_event->processed_target->client_id(), queued_event->display_id,
+          *queued_event->event, queued_event->processed_target->accelerator());
       return;
     }
   }
@@ -457,6 +465,7 @@
 void WindowManagerState::DispatchInputEventToWindowImpl(
     ServerWindow* target,
     ClientSpecificId client_id,
+    const int64_t display_id,
     const ui::Event& event,
     base::WeakPtr<Accelerator> accelerator) {
   DCHECK(target);
@@ -470,7 +479,8 @@
 
   WindowTree* tree = window_server()->GetTreeWithId(client_id);
   DCHECK(tree);
-  ScheduleInputEventTimeout(tree, target, event, EventDispatchPhase::TARGET);
+  ScheduleInputEventTimeout(tree, target, display_id, event,
+                            EventDispatchPhase::TARGET);
   in_flight_event_details_->post_target_accelerator = accelerator;
 
   // Ignore |tree| because it will receive the event via normal dispatch.
@@ -491,20 +501,22 @@
   debug_accelerators_.push_back(accelerator);
 }
 
-void WindowManagerState::ProcessDebugAccelerator(const ui::Event& event) {
+void WindowManagerState::ProcessDebugAccelerator(const ui::Event& event,
+                                                 const int64_t display_id) {
   if (event.type() != ui::ET_KEY_PRESSED)
     return;
 
   const ui::KeyEvent& key_event = *event.AsKeyEvent();
   for (const DebugAccelerator& accelerator : debug_accelerators_) {
     if (accelerator.Matches(key_event)) {
-      HandleDebugAccelerator(accelerator.type);
+      HandleDebugAccelerator(accelerator.type, display_id);
       break;
     }
   }
 }
 
-void WindowManagerState::HandleDebugAccelerator(DebugAcceleratorType type) {
+void WindowManagerState::HandleDebugAccelerator(DebugAcceleratorType type,
+                                                const int64_t display_id) {
 #if DCHECK_IS_ON()
   // Error so it will be collected in system logs.
   for (Display* display : display_manager()->displays()) {
@@ -515,7 +527,7 @@
                  << display_root->root()->GetDebugWindowHierarchy();
     }
   }
-  ServerWindow* focused_window = GetFocusedWindowForEventDispatcher();
+  ServerWindow* focused_window = GetFocusedWindowForEventDispatcher(display_id);
   LOG(ERROR) << "Focused window: "
              << (focused_window ? focused_window->id().ToString() : "(null)");
 #endif
@@ -523,11 +535,12 @@
 
 void WindowManagerState::ScheduleInputEventTimeout(WindowTree* tree,
                                                    ServerWindow* target,
+                                                   const int64_t display_id,
                                                    const Event& event,
                                                    EventDispatchPhase phase) {
   std::unique_ptr<InFlightEventDetails> details =
-      base::MakeUnique<InFlightEventDetails>(
-          this, tree, event_processing_display_id_, event, phase);
+      base::MakeUnique<InFlightEventDetails>(this, tree, display_id, event,
+                                             phase);
 
   // TOOD(sad): Adjust this delay, possibly make this dynamic.
   const base::TimeDelta max_delay = base::debug::BeingDebugged()
@@ -540,10 +553,24 @@
   in_flight_event_details_ = std::move(details);
 }
 
+bool WindowManagerState::ConvertPointToScreen(const int64_t display_id,
+                                              gfx::Point* point) {
+  Display* display = display_manager()->GetDisplayById(display_id);
+  if (display) {
+    const display::Display& originated_display = display->GetDisplay();
+    *point = gfx::ConvertPointToDIP(originated_display.device_scale_factor(),
+                                    *point);
+    *point += originated_display.bounds().origin().OffsetFromOrigin();
+    return true;
+  }
+  return false;
+}
+
 ////////////////////////////////////////////////////////////////////////////////
 // EventDispatcherDelegate:
 
 void WindowManagerState::OnAccelerator(uint32_t accelerator_id,
+                                       const int64_t display_id,
                                        const ui::Event& event,
                                        AcceleratorPhase phase) {
   DCHECK(IsActive());
@@ -551,7 +578,7 @@
   WindowTree::AcceleratorCallback ack_callback;
   if (needs_ack) {
     DCHECK(!in_flight_event_details_);
-    ScheduleInputEventTimeout(window_tree_, nullptr, event,
+    ScheduleInputEventTimeout(window_tree_, nullptr, display_id, event,
                               EventDispatchPhase::PRE_TARGET_ACCELERATOR);
     ack_callback =
         base::BindOnce(&WindowManagerState::OnAcceleratorAck,
@@ -566,14 +593,15 @@
   window_server()->SetFocusedWindow(new_focused_window);
 }
 
-ServerWindow* WindowManagerState::GetFocusedWindowForEventDispatcher() {
+ServerWindow* WindowManagerState::GetFocusedWindowForEventDispatcher(
+    const int64_t display_id) {
   ServerWindow* focused_window = window_server()->GetFocusedWindow();
   if (focused_window)
     return focused_window;
 
   // When none of the windows have focus return the window manager's root.
   for (auto& display_root_ptr : window_manager_display_roots_) {
-    if (display_root_ptr->display()->GetId() == event_processing_display_id_)
+    if (display_root_ptr->display()->GetId() == display_id)
       return display_root_ptr->GetClientVisibleRoot();
   }
   if (!window_manager_display_roots_.empty())
@@ -611,15 +639,23 @@
   window_server()->ProcessCaptureChanged(new_capture, old_capture);
 }
 
-void WindowManagerState::OnMouseCursorLocationChanged(const gfx::Point& point) {
-  window_server()
-      ->display_manager()
-      ->GetCursorLocationManager(user_id())
-      ->OnMouseCursorLocationChanged(point);
+void WindowManagerState::OnMouseCursorLocationChanged(
+    const gfx::Point& point_in_display,
+    const int64_t display_id) {
+  gfx::Point point_in_screen(point_in_display);
+  if (ConvertPointToScreen(display_id, &point_in_screen)) {
+    window_server()
+        ->display_manager()
+        ->GetCursorLocationManager(user_id())
+        ->OnMouseCursorLocationChanged(point_in_screen);
+  }
+  // If the display the |point_in_display| is on has been deleted, keep the old
+  // cursor location.
 }
 
 void WindowManagerState::DispatchInputEventToWindow(ServerWindow* target,
                                                     ClientSpecificId client_id,
+                                                    const int64_t display_id,
                                                     const ui::Event& event,
                                                     Accelerator* accelerator) {
   DCHECK(IsActive());
@@ -628,15 +664,15 @@
   if (in_flight_event_details_) {
     std::unique_ptr<ProcessedEventTarget> processed_event_target(
         new ProcessedEventTarget(target, client_id, accelerator));
-    QueueEvent(event, std::move(processed_event_target),
-               event_processing_display_id_);
+    QueueEvent(event, std::move(processed_event_target), display_id);
     return;
   }
 
   base::WeakPtr<Accelerator> weak_accelerator;
   if (accelerator)
     weak_accelerator = accelerator->GetWeakPtr();
-  DispatchInputEventToWindowImpl(target, client_id, event, weak_accelerator);
+  DispatchInputEventToWindowImpl(target, client_id, display_id, event,
+                                 weak_accelerator);
 }
 
 ClientSpecificId WindowManagerState::GetEventTargetClientId(
@@ -668,16 +704,19 @@
 }
 
 ServerWindow* WindowManagerState::GetRootWindowContaining(
-    gfx::Point* location) {
+    gfx::Point* location_in_display,
+    int64_t* display_id) {
   if (window_manager_display_roots_.empty())
     return nullptr;
 
-  // TODO(riajiang): This is broken for HDPI because it mixes PPs and DIPs. See
-  // http://crbug.com/701036 for details.
+  gfx::Point location_in_screen(*location_in_display);
+  if (!ConvertPointToScreen(*display_id, &location_in_screen))
+    return nullptr;
+
   WindowManagerDisplayRoot* target_display_root = nullptr;
   for (auto& display_root_ptr : window_manager_display_roots_) {
     if (display_root_ptr->display()->GetDisplay().bounds().Contains(
-            *location)) {
+            location_in_screen)) {
       target_display_root = display_root_ptr.get();
       break;
     }
@@ -686,22 +725,30 @@
   // TODO(kylechar): Better handle locations outside the window. Overlapping X11
   // windows, dragging and touch sensors need to be handled properly.
   if (!target_display_root) {
-    DVLOG(1) << "Invalid event location " << location->ToString();
+    DVLOG(1) << "Invalid event location " << location_in_display->ToString()
+             << " / display id " << *display_id;
     target_display_root = window_manager_display_roots_.begin()->get();
   }
 
-  // Translate the location to be relative to the display instead of relative
-  // to the screen space.
-  gfx::Point origin =
-      target_display_root->display()->GetDisplay().bounds().origin();
-  *location -= origin.OffsetFromOrigin();
+  // Update |location_in_display| and |display_id| if the target display is
+  // different from the originated display, e.g. drag-and-drop.
+  if (*display_id != target_display_root->display()->GetId()) {
+    gfx::Point origin =
+        target_display_root->display()->GetDisplay().bounds().origin();
+    *location_in_display = location_in_screen - origin.OffsetFromOrigin();
+    *location_in_display = gfx::ConvertPointToPixel(
+        target_display_root->display()->GetDisplay().device_scale_factor(),
+        *location_in_display);
+    *display_id = target_display_root->display()->GetId();
+  }
+
   return target_display_root->GetClientVisibleRoot();
 }
 
-void WindowManagerState::OnEventTargetNotFound(const ui::Event& event) {
+void WindowManagerState::OnEventTargetNotFound(const ui::Event& event,
+                                               const int64_t display_id) {
   window_server()->SendToPointerWatchers(event, user_id(), nullptr, /* window */
-                                         nullptr /* ignore_tree */,
-                                         event_processing_display_id_);
+                                         nullptr /* ignore_tree */, display_id);
   if (event.IsMousePointerEvent())
     UpdateNativeCursorFromDispatcher();
 }
diff --git a/services/ui/ws/window_manager_state.h b/services/ui/ws/window_manager_state.h
index ce897244..68e808b 100644
--- a/services/ui/ws/window_manager_state.h
+++ b/services/ui/ws/window_manager_state.h
@@ -96,7 +96,8 @@
   // Returns true if this is the WindowManager of the active user.
   bool IsActive() const;
 
-  void Activate(const gfx::Point& mouse_location_on_screen);
+  void Activate(const gfx::Point& mouse_location_on_display,
+                const int64_t display_id);
   void Deactivate();
 
   // Processes an event from PlatformDisplay.
@@ -221,6 +222,7 @@
   // Dispatches the event to the appropriate client and starts the ack timer.
   void DispatchInputEventToWindowImpl(ServerWindow* target,
                                       ClientSpecificId client_id,
+                                      const int64_t display_id,
                                       const Event& event,
                                       base::WeakPtr<Accelerator> accelerator);
 
@@ -229,37 +231,51 @@
 
   // Finds the debug accelerator for |event| and if one is found calls
   // HandleDebugAccelerator().
-  void ProcessDebugAccelerator(const Event& event);
+  void ProcessDebugAccelerator(const Event& event, const int64_t display_id);
 
   // Runs the specified debug accelerator.
-  void HandleDebugAccelerator(DebugAcceleratorType type);
+  void HandleDebugAccelerator(DebugAcceleratorType type,
+                              const int64_t display_id);
 
   // Called when waiting for an event or accelerator to be processed by |tree|.
   void ScheduleInputEventTimeout(WindowTree* tree,
                                  ServerWindow* target,
+                                 const int64_t display_id,
                                  const Event& event,
                                  EventDispatchPhase phase);
 
+  // Helper function to convert |point| to be in screen coordinates. |point| as
+  // the input should be in display-physical-pixel space, and the output is in
+  // screen-dip space. Returns true if the |point| is successfully converted,
+  // false otherwise.
+  bool ConvertPointToScreen(const int64_t display_id, gfx::Point* point);
+
   // EventDispatcherDelegate:
   void OnAccelerator(uint32_t accelerator_id,
+                     const int64_t display_id,
                      const Event& event,
                      AcceleratorPhase phase) override;
   void SetFocusedWindowFromEventDispatcher(ServerWindow* window) override;
-  ServerWindow* GetFocusedWindowForEventDispatcher() override;
+  ServerWindow* GetFocusedWindowForEventDispatcher(
+      const int64_t display_id) override;
   void SetNativeCapture(ServerWindow* window) override;
   void ReleaseNativeCapture() override;
   void UpdateNativeCursorFromDispatcher() override;
   void OnCaptureChanged(ServerWindow* new_capture,
                         ServerWindow* old_capture) override;
-  void OnMouseCursorLocationChanged(const gfx::Point& point) override;
+  void OnMouseCursorLocationChanged(const gfx::Point& point,
+                                    const int64_t display_id) override;
   void DispatchInputEventToWindow(ServerWindow* target,
                                   ClientSpecificId client_id,
+                                  const int64_t display_id,
                                   const Event& event,
                                   Accelerator* accelerator) override;
   ClientSpecificId GetEventTargetClientId(const ServerWindow* window,
                                           bool in_nonclient_area) override;
-  ServerWindow* GetRootWindowContaining(gfx::Point* location) override;
-  void OnEventTargetNotFound(const Event& event) override;
+  ServerWindow* GetRootWindowContaining(gfx::Point* location_in_display,
+                                        int64_t* display_id) override;
+  void OnEventTargetNotFound(const Event& event,
+                             const int64_t display_id) override;
 
   // ServerWindowObserver:
   void OnWindowEmbeddedAppDisconnected(ServerWindow* window) override;
@@ -288,9 +304,6 @@
   // All the active WindowManagerDisplayRoots.
   WindowManagerDisplayRoots window_manager_display_roots_;
 
-  // Id of the display the current event being processed originated from.
-  int64_t event_processing_display_id_ = 0;
-
   // Set of WindowManagerDisplayRoots corresponding to Displays that have been
   // destroyed. WindowManagerDisplayRoots are not destroyed immediately when
   // the Display is destroyed to allow the client to destroy the window when it
diff --git a/services/ui/ws/window_manager_state_unittest.cc b/services/ui/ws/window_manager_state_unittest.cc
index 9b1bfe32..d1dc651 100644
--- a/services/ui/ws/window_manager_state_unittest.cc
+++ b/services/ui/ws/window_manager_state_unittest.cc
@@ -47,6 +47,7 @@
                            ServerWindow** server_window);
 
   void DispatchInputEventToWindow(ServerWindow* target,
+                                  const int64_t display_id,
                                   const ui::Event& event,
                                   Accelerator* accelerator);
   void OnEventAckTimeout(ClientSpecificId client_id);
@@ -138,11 +139,13 @@
 
 void WindowManagerStateTest::DispatchInputEventToWindow(
     ServerWindow* target,
+    const int64_t display_id,
     const ui::Event& event,
     Accelerator* accelerator) {
   WindowManagerStateTestApi test_api(window_manager_state_);
   ClientSpecificId client_id = test_api.GetEventTargetClientId(target, false);
-  test_api.DispatchInputEventToWindow(target, client_id, event, accelerator);
+  test_api.DispatchInputEventToWindow(target, client_id, display_id, event,
+                                      accelerator);
 }
 
 void WindowManagerStateTest::OnEventAckTimeout(
@@ -182,8 +185,10 @@
   EXPECT_TRUE(state);
 
   ServerWindow* target = window();
+  const Display* display = window_tree()->GetDisplay(target);
+  DCHECK(display);
   ui::KeyEvent key(ui::ET_KEY_PRESSED, ui::VKEY_W, ui::EF_CONTROL_DOWN);
-  DispatchInputEventToWindow(target, key, nullptr);
+  DispatchInputEventToWindow(target, display->GetId(), key, nullptr);
   WindowTree* target_tree = window_tree();
   TestChangeTracker* tracker = window_tree_client()->tracker();
   ASSERT_EQ(1u, tracker->changes()->size());
@@ -201,7 +206,9 @@
   std::unique_ptr<Accelerator> accelerator = CreateAccelerator();
 
   ServerWindow* target = window();
-  DispatchInputEventToWindow(target, key, accelerator.get());
+  const Display* display = window_tree()->GetDisplay(target);
+  DCHECK(display);
+  DispatchInputEventToWindow(target, display->GetId(), key, accelerator.get());
   TestChangeTracker* tracker = window_tree_client()->tracker();
   ASSERT_EQ(1u, tracker->changes()->size());
   EXPECT_EQ("InputEvent window=1,1 event_action=7",
@@ -350,7 +357,9 @@
   std::unique_ptr<Accelerator> accelerator = CreateAccelerator();
 
   ServerWindow* target = window();
-  DispatchInputEventToWindow(target, key, accelerator.get());
+  const Display* display = window_tree()->GetDisplay(target);
+  DCHECK(display);
+  DispatchInputEventToWindow(target, display->GetId(), key, accelerator.get());
   TestChangeTracker* tracker = window_tree_client()->tracker();
   ASSERT_EQ(1u, tracker->changes()->size());
   EXPECT_EQ("InputEvent window=1,1 event_action=7",
@@ -368,7 +377,9 @@
   std::unique_ptr<Accelerator> accelerator(CreateAccelerator());
 
   ServerWindow* target = window();
-  DispatchInputEventToWindow(target, key, accelerator.get());
+  const Display* display = window_tree()->GetDisplay(target);
+  DCHECK(display);
+  DispatchInputEventToWindow(target, display->GetId(), key, accelerator.get());
   TestChangeTracker* tracker = window_tree_client()->tracker();
   ASSERT_EQ(1u, tracker->changes()->size());
   EXPECT_EQ("InputEvent window=1,1 event_action=7",
@@ -387,7 +398,9 @@
   std::unique_ptr<Accelerator> accelerator(CreateAccelerator());
 
   ServerWindow* target = window();
-  DispatchInputEventToWindow(target, key, accelerator.get());
+  const Display* display = window_tree()->GetDisplay(target);
+  DCHECK(display);
+  DispatchInputEventToWindow(target, display->GetId(), key, accelerator.get());
   TestChangeTracker* tracker = window_tree_client()->tracker();
   ASSERT_EQ(1u, tracker->changes()->size());
   EXPECT_EQ("InputEvent window=1,1 event_action=7",
@@ -401,7 +414,8 @@
   uint32_t accelerator_id = 2;
   std::unique_ptr<Accelerator> accelerator2(
       new Accelerator(accelerator_id, *matcher));
-  DispatchInputEventToWindow(target, key2, accelerator2.get());
+  DispatchInputEventToWindow(target, display->GetId(), key2,
+                             accelerator2.get());
   EXPECT_TRUE(tracker->changes()->empty());
 
   WindowTreeTestApi(window_tree()).AckOldestEvent();
@@ -418,7 +432,9 @@
   std::unique_ptr<Accelerator> accelerator = CreateAccelerator();
 
   ServerWindow* target = window();
-  DispatchInputEventToWindow(target, key, accelerator.get());
+  const Display* display = window_tree()->GetDisplay(target);
+  DCHECK(display);
+  DispatchInputEventToWindow(target, display->GetId(), key, accelerator.get());
   TestChangeTracker* tracker = window_tree_client()->tracker();
   ASSERT_EQ(1u, tracker->changes()->size());
   EXPECT_EQ("InputEvent window=1,1 event_action=7",
@@ -441,7 +457,9 @@
 
   ui::KeyEvent key(ui::ET_KEY_PRESSED, ui::VKEY_W, ui::EF_CONTROL_DOWN);
   std::unique_ptr<Accelerator> accelerator = CreateAccelerator();
-  DispatchInputEventToWindow(target, key, accelerator.get());
+  const Display* display = target_tree->GetDisplay(target);
+  DCHECK(display);
+  DispatchInputEventToWindow(target, display->GetId(), key, accelerator.get());
   TestChangeTracker* tracker = embed_connection->tracker();
   ASSERT_EQ(1u, tracker->changes()->size());
   EXPECT_EQ("InputEvent window=2,1 event_action=7",
@@ -459,10 +477,12 @@
   ServerWindow* target = window();
   TestChangeTracker* tracker = window_tree_client()->tracker();
 
+  const Display* display = window_tree()->GetDisplay(target);
+  DCHECK(display);
   ui::MouseEvent press(ui::ET_MOUSE_PRESSED, gfx::Point(5, 5), gfx::Point(5, 5),
                        base::TimeTicks(), EF_LEFT_MOUSE_BUTTON,
                        EF_LEFT_MOUSE_BUTTON);
-  DispatchInputEventToWindow(target, press, nullptr);
+  DispatchInputEventToWindow(target, display->GetId(), press, nullptr);
   ASSERT_EQ(1u, tracker->changes()->size());
   EXPECT_EQ("InputEvent window=1,1 event_action=1",
             ChangesToDescription1(*tracker->changes())[0]);
@@ -474,7 +494,7 @@
   ui::MouseEvent release(ui::ET_MOUSE_RELEASED, gfx::Point(5, 5),
                          gfx::Point(5, 5), base::TimeTicks(),
                          EF_LEFT_MOUSE_BUTTON, EF_LEFT_MOUSE_BUTTON);
-  DispatchInputEventToWindow(target, release, nullptr);
+  DispatchInputEventToWindow(target, display->GetId(), release, nullptr);
   EXPECT_EQ(0u, tracker->changes()->size());
 
   // Destroying a window tree with an event in queue shouldn't crash.
@@ -485,7 +505,10 @@
 TEST_F(WindowManagerStateTest, AckTimeout) {
   ui::KeyEvent key(ui::ET_KEY_PRESSED, ui::VKEY_W, ui::EF_CONTROL_DOWN);
   std::unique_ptr<Accelerator> accelerator = CreateAccelerator();
-  DispatchInputEventToWindow(window(), key, accelerator.get());
+  const Display* display = window_tree()->GetDisplay(window());
+  DCHECK(display);
+  DispatchInputEventToWindow(window(), display->GetId(), key,
+                             accelerator.get());
   TestChangeTracker* tracker = window_tree_client()->tracker();
   ASSERT_EQ(1u, tracker->changes()->size());
   EXPECT_EQ("InputEvent window=1,1 event_action=7",
@@ -520,9 +543,12 @@
     ASSERT_TRUE(embed_client_proxy);
 
     // Send an event to the embed window. It should go to the embedded client.
+    const Display* display = embed_tree->GetDisplay(embedder_window);
+    DCHECK(display);
     ui::MouseEvent mouse(ui::ET_MOUSE_MOVED, gfx::Point(), gfx::Point(),
                          base::TimeTicks(), 0, 0);
-    DispatchInputEventToWindow(embedder_window, mouse, nullptr);
+    DispatchInputEventToWindow(embedder_window, display->GetId(), mouse,
+                               nullptr);
     ASSERT_EQ(1u, embed_client_proxy->tracker()->changes()->size());
     EXPECT_EQ(CHANGE_TYPE_INPUT_EVENT,
               (*embed_client_proxy->tracker()->changes())[0].type);
@@ -543,9 +569,12 @@
 
     // Send an event to the embed window. But this time, it should reach the
     // embedder.
+    const Display* display = embed_tree->GetDisplay(embedder_window);
+    DCHECK(display);
     ui::MouseEvent mouse(ui::ET_MOUSE_MOVED, gfx::Point(), gfx::Point(),
                          base::TimeTicks(), 0, 0);
-    DispatchInputEventToWindow(embedder_window, mouse, nullptr);
+    DispatchInputEventToWindow(embedder_window, display->GetId(), mouse,
+                               nullptr);
     ASSERT_EQ(0u, embed_client_proxy->tracker()->changes()->size());
     ASSERT_EQ(1u, embedder_client->tracker()->changes()->size());
     EXPECT_EQ(CHANGE_TYPE_INPUT_EVENT,
@@ -577,7 +606,8 @@
     DCHECK(nested_embed_window->parent());
     mouse = ui::MouseEvent(ui::ET_MOUSE_MOVED, gfx::Point(), gfx::Point(),
                            base::TimeTicks(), 0, 0);
-    DispatchInputEventToWindow(nested_embed_window, mouse, nullptr);
+    DispatchInputEventToWindow(nested_embed_window, display->GetId(), mouse,
+                               nullptr);
     ASSERT_EQ(0u, nested_embed_client_proxy->tracker()->changes()->size());
     ASSERT_EQ(0u, embed_client_proxy->tracker()->changes()->size());
 
@@ -597,7 +627,10 @@
                                ui::EF_CONTROL_DOWN);
   std::unique_ptr<Accelerator> accelerator = CreateAccelerator();
   ServerWindow* target = window();
-  DispatchInputEventToWindow(target, accelerator_key, accelerator.get());
+  const Display* display = window_tree()->GetDisplay(target);
+  DCHECK(display);
+  DispatchInputEventToWindow(target, display->GetId(), accelerator_key,
+                             accelerator.get());
   TestChangeTracker* tracker = window_tree_client()->tracker();
   ASSERT_EQ(1u, tracker->changes()->size());
   EXPECT_EQ("InputEvent window=1,1 event_action=7",
@@ -610,7 +643,8 @@
   // shouldn't be called.
   ui::KeyEvent non_accelerator_key(ui::ET_KEY_PRESSED, ui::VKEY_T,
                                    ui::EF_CONTROL_DOWN);
-  DispatchInputEventToWindow(target, non_accelerator_key, nullptr);
+  DispatchInputEventToWindow(target, display->GetId(), non_accelerator_key,
+                             nullptr);
   ASSERT_EQ(1u, tracker->changes()->size());
   EXPECT_EQ("InputEvent window=1,1 event_action=7",
             ChangesToDescription1(*tracker->changes())[0]);
diff --git a/services/ui/ws/window_manager_window_tree_factory.cc b/services/ui/ws/window_manager_window_tree_factory.cc
index 89711f2..ab38d7b2 100644
--- a/services/ui/ws/window_manager_window_tree_factory.cc
+++ b/services/ui/ws/window_manager_window_tree_factory.cc
@@ -5,6 +5,7 @@
 #include "services/ui/ws/window_manager_window_tree_factory.h"
 
 #include "base/bind.h"
+#include "services/ui/ws/display_creation_config.h"
 #include "services/ui/ws/window_manager_window_tree_factory_set.h"
 #include "services/ui/ws/window_server.h"
 #include "services/ui/ws/window_server_delegate.h"
@@ -39,9 +40,13 @@
   if (binding_.is_bound())
     binding_.Close();
 
-  window_manager_window_tree_factory_set_->window_server()
-      ->delegate()
-      ->OnWillCreateTreeForWindowManager(automatically_create_display_roots);
+  // If the config is MANUAL, then all WindowManagers must connect as MANUAL.
+  if (!automatically_create_display_roots &&
+      GetWindowServer()->display_creation_config() ==
+          DisplayCreationConfig::AUTOMATIC) {
+    DVLOG(1) << "CreateWindowTree() called with manual and automatic.";
+    return;
+  }
 
   SetWindowTree(GetWindowServer()->CreateTreeForWindowManager(
       user_id_, std::move(window_tree_request), std::move(window_tree_client),
diff --git a/services/ui/ws/window_server.cc b/services/ui/ws/window_server.cc
index 67b31254..812240d 100644
--- a/services/ui/ws/window_server.cc
+++ b/services/ui/ws/window_server.cc
@@ -11,6 +11,7 @@
 #include "base/memory/ptr_util.h"
 #include "base/stl_util.h"
 #include "services/ui/ws/display.h"
+#include "services/ui/ws/display_creation_config.h"
 #include "services/ui/ws/display_manager.h"
 #include "services/ui/ws/frame_generator.h"
 #include "services/ui/ws/gpu_host.h"
@@ -69,7 +70,8 @@
       next_wm_change_id_(0),
       gpu_host_(new GpuHost(this)),
       window_manager_window_tree_factory_set_(this, &user_id_tracker_),
-      frame_sink_manager_client_binding_(this) {
+      frame_sink_manager_client_binding_(this),
+      display_creation_config_(DisplayCreationConfig::UNKNOWN) {
   user_id_tracker_.AddObserver(this);
   OnUserIdAdded(user_id_tracker_.active_id());
   gpu_host_->CreateFrameSinkManager(
@@ -94,6 +96,13 @@
   display_manager_.reset();
 }
 
+void WindowServer::SetDisplayCreationConfig(DisplayCreationConfig config) {
+  DCHECK(tree_map_.empty());
+  DCHECK_EQ(DisplayCreationConfig::UNKNOWN, display_creation_config_);
+  display_creation_config_ = config;
+  display_manager_->OnDisplayCreationConfigSet();
+}
+
 ServerWindow* WindowServer::CreateServerWindow(
     const WindowId& id,
     const std::map<std::string, std::vector<uint8_t>>& properties) {
@@ -150,6 +159,9 @@
     mojom::WindowTreeRequest window_tree_request,
     mojom::WindowTreeClientPtr window_tree_client,
     bool automatically_create_display_roots) {
+  delegate_->OnWillCreateTreeForWindowManager(
+      automatically_create_display_roots);
+
   std::unique_ptr<WindowTree> window_tree(new WindowTree(
       this, user_id, nullptr, base::WrapUnique(new WindowManagerAccessPolicy)));
   std::unique_ptr<WindowTreeBinding> window_tree_binding =
diff --git a/services/ui/ws/window_server.h b/services/ui/ws/window_server.h
index f79cf963..f072f77 100644
--- a/services/ui/ws/window_server.h
+++ b/services/ui/ws/window_server.h
@@ -42,6 +42,8 @@
 class WindowTree;
 class WindowTreeBinding;
 
+enum class DisplayCreationConfig;
+
 // WindowServer manages the set of clients of the window server (all the
 // WindowTrees) as well as providing the root of the hierarchy.
 class WindowServer : public ServerWindowDelegate,
@@ -66,6 +68,11 @@
 
   GpuHost* gpu_host() { return gpu_host_.get(); }
 
+  void SetDisplayCreationConfig(DisplayCreationConfig config);
+  DisplayCreationConfig display_creation_config() const {
+    return display_creation_config_;
+  }
+
   // Creates a new ServerWindow. The return value is owned by the caller, but
   // must be destroyed before WindowServer.
   ServerWindow* CreateServerWindow(
@@ -388,6 +395,8 @@
       frame_sink_manager_client_binding_;
   cc::mojom::FrameSinkManagerPtr frame_sink_manager_;
 
+  DisplayCreationConfig display_creation_config_;
+
   DISALLOW_COPY_AND_ASSIGN(WindowServer);
 };
 
diff --git a/services/ui/ws/window_server_delegate.cc b/services/ui/ws/window_server_delegate.cc
index 1250bade..6d1194b 100644
--- a/services/ui/ws/window_server_delegate.cc
+++ b/services/ui/ws/window_server_delegate.cc
@@ -21,8 +21,5 @@
   return nullptr;
 }
 
-void WindowServerDelegate::OnWillCreateTreeForWindowManager(
-    bool automatically_create_display_roots) {}
-
 }  // namespace ws
 }  // namespace ui
diff --git a/services/ui/ws/window_server_delegate.h b/services/ui/ws/window_server_delegate.h
index 5256e11..6588bec 100644
--- a/services/ui/ws/window_server_delegate.h
+++ b/services/ui/ws/window_server_delegate.h
@@ -57,7 +57,7 @@
   // WindowManagerWindowTreeFactory. |automatically_create_display_roots|
   // mirrors that of CreateWindowTree(). See it for details.
   virtual void OnWillCreateTreeForWindowManager(
-      bool automatically_create_display_roots);
+      bool automatically_create_display_roots) = 0;
 
  protected:
   virtual ~WindowServerDelegate() {}
diff --git a/services/ui/ws/window_tree.cc b/services/ui/ws/window_tree.cc
index c758775..6e8ca14 100644
--- a/services/ui/ws/window_tree.cc
+++ b/services/ui/ws/window_tree.cc
@@ -283,8 +283,7 @@
   DCHECK(window_manager_state_);  // Only called for window manager.
   DVLOG(3) << "SetDisplayRoot client=" << id_
            << " global window_id=" << client_window_id.id;
-  Display* display =
-      window_server_->display_manager()->GetDisplayById(display_to_create.id());
+  Display* display = display_manager()->GetDisplayById(display_to_create.id());
   if (display) {
     DVLOG(1) << "SetDisplayRoot called with existing display "
              << display_to_create.id();
@@ -310,23 +309,14 @@
     return nullptr;
   }
 
-  const display::DisplayList::Type display_type =
-      is_primary_display ? display::DisplayList::Type::PRIMARY
-                         : display::DisplayList::Type::NOT_PRIMARY;
-  display::ScreenManager::GetInstance()->GetScreen()->display_list().AddDisplay(
-      display_to_create, display_type);
   display::ViewportMetrics viewport_metrics;
   viewport_metrics.bounds_in_pixels =
       transport_viewport_metrics.bounds_in_pixels;
   viewport_metrics.device_scale_factor =
       transport_viewport_metrics.device_scale_factor;
   viewport_metrics.ui_scale_factor = transport_viewport_metrics.ui_scale_factor;
-  window_server_->display_manager()->AddDisplayForWindowManager(
-      display_to_create, viewport_metrics);
-
-  // OnDisplayAdded() should trigger creation of the Display.
-  display =
-      window_server_->display_manager()->GetDisplayById(display_to_create.id());
+  display = display_manager()->AddDisplayForWindowManager(
+      is_primary_display, display_to_create, viewport_metrics);
   DCHECK(display);
   WindowManagerDisplayRoot* display_root =
       display->GetWindowManagerDisplayRootForUser(
@@ -1155,7 +1145,7 @@
   roots_.erase(window);
 
   if (window->id().client_id == id_) {
-    // This cllient created the window. If this client is the window manager and
+    // This client created the window. If this client is the window manager and
     // display roots are manually created, then |window| is a display root and
     // needs be cleaned.
     if (window_manager_state_ && !automatically_create_display_roots_) {
@@ -1980,7 +1970,7 @@
 
 void WindowTree::GetCursorLocationMemory(
     const GetCursorLocationMemoryCallback& callback) {
-  callback.Run(window_server_->display_manager()
+  callback.Run(display_manager()
                    ->GetCursorLocationManager(user_id_)
                    ->GetCursorLocationMemory());
 }
@@ -2202,7 +2192,7 @@
     return;
   }
   // Use the first display.
-  std::set<Display*> displays = window_server_->display_manager()->displays();
+  std::set<Display*> displays = display_manager()->displays();
   if (displays.empty())
     return;
 
@@ -2240,6 +2230,15 @@
   callback.Run(display_root->current_local_surface_id());
 }
 
+void WindowTree::SetDisplayConfiguration(
+    const std::vector<display::Display>& displays,
+    std::vector<ui::mojom::WmViewportMetricsPtr> viewport_metrics,
+    int64_t primary_display_id,
+    const SetDisplayConfigurationCallback& callback) {
+  callback.Run(display_manager()->SetDisplayConfiguration(
+      displays, std::move(viewport_metrics), primary_display_id));
+}
+
 void WindowTree::WmResponse(uint32_t change_id, bool response) {
   if (window_server_->in_move_loop() &&
       window_server_->GetCurrentMoveLoopChangeId() == change_id) {
diff --git a/services/ui/ws/window_tree.h b/services/ui/ws/window_tree.h
index e95912c..45fef34 100644
--- a/services/ui/ws/window_tree.h
+++ b/services/ui/ws/window_tree.h
@@ -516,6 +516,11 @@
                       bool is_primary_display,
                       Id window_id,
                       const SetDisplayRootCallback& callback) override;
+  void SetDisplayConfiguration(
+      const std::vector<display::Display>& displays,
+      std::vector<ui::mojom::WmViewportMetricsPtr> viewport_metrics,
+      int64_t primary_display_id,
+      const SetDisplayConfigurationCallback& callback) override;
   void WmResponse(uint32_t change_id, bool response) override;
   void WmSetBoundsResponse(uint32_t change_id) override;
   void WmRequestClose(Id transport_window_id) override;
diff --git a/services/ui/ws/window_tree_unittest.cc b/services/ui/ws/window_tree_unittest.cc
index cff48cc..83b9de09 100644
--- a/services/ui/ws/window_tree_unittest.cc
+++ b/services/ui/ws/window_tree_unittest.cc
@@ -11,8 +11,10 @@
 
 #include "base/macros.h"
 #include "base/memory/ptr_util.h"
+#include "base/message_loop/message_loop.h"
 #include "base/strings/stringprintf.h"
 #include "services/service_manager/public/interfaces/connector.mojom.h"
+#include "services/ui/common/task_runner_test_base.h"
 #include "services/ui/common/types.h"
 #include "services/ui/common/util.h"
 #include "services/ui/public/interfaces/window_tree.mojom.h"
@@ -25,6 +27,7 @@
 #include "services/ui/ws/test_change_tracker.h"
 #include "services/ui/ws/test_server_window_delegate.h"
 #include "services/ui/ws/test_utils.h"
+#include "services/ui/ws/user_display_manager.h"
 #include "services/ui/ws/window_manager_access_policy.h"
 #include "services/ui/ws/window_manager_display_root.h"
 #include "services/ui/ws/window_server.h"
@@ -121,6 +124,19 @@
   DISALLOW_COPY_AND_ASSIGN(TestMoveLoopWindowManager);
 };
 
+// This creates a WindowTree similar to how connecting via WindowTreeFactory
+// creates a tree.
+WindowTree* CreateTreeViaFactory(WindowServer* window_server,
+                                 const UserId& user_id,
+                                 TestWindowTreeBinding** binding) {
+  WindowTree* tree = new WindowTree(window_server, user_id, nullptr,
+                                    base::MakeUnique<DefaultAccessPolicy>());
+  *binding = new TestWindowTreeBinding(tree);
+  window_server->AddTree(base::WrapUnique(tree), base::WrapUnique(*binding),
+                         nullptr);
+  return tree;
+}
+
 }  // namespace
 
 // -----------------------------------------------------------------------------
@@ -180,13 +196,7 @@
   // a WindowTreeFactory does.
   WindowTree* CreateNewTree(const UserId& user_id,
                             TestWindowTreeBinding** binding) {
-    WindowTree* tree =
-        new WindowTree(window_server(), user_id, nullptr,
-                       base::WrapUnique(new DefaultAccessPolicy));
-    *binding = new TestWindowTreeBinding(tree);
-    window_server()->AddTree(base::WrapUnique(tree), base::WrapUnique(*binding),
-                             nullptr);
-    return tree;
+    return CreateTreeViaFactory(window_server(), user_id, binding);
   }
 
  protected:
@@ -1516,7 +1526,7 @@
 }
 
 // Used to test the window manager configured to manually create displays roots.
-class WindowTreeManualDisplayTest : public testing::Test {
+class WindowTreeManualDisplayTest : public TaskRunnerTestBase {
  public:
   WindowTreeManualDisplayTest() {}
   ~WindowTreeManualDisplayTest() override {}
@@ -1533,6 +1543,7 @@
  protected:
   // testing::Test:
   void SetUp() override {
+    TaskRunnerTestBase::SetUp();
     screen_manager_.Init(window_server()->display_manager());
     window_server()->user_id_tracker()->AddUserId(kTestUserId1);
   }
@@ -1548,6 +1559,7 @@
   const bool automatically_create_display_roots = false;
   AddWindowManager(window_server(), kTestUserId1,
                    automatically_create_display_roots);
+
   WindowManagerState* window_manager_state =
       window_server()->GetWindowManagerStateForUser(kTestUserId1);
   ASSERT_TRUE(window_manager_state);
@@ -1593,6 +1605,143 @@
                   .empty());
 }
 
+TEST_F(WindowTreeManualDisplayTest,
+       DisplayManagerObserverNotifiedWithManualRoots) {
+  const bool automatically_create_display_roots = false;
+  AddWindowManager(window_server(), kTestUserId1,
+                   automatically_create_display_roots);
+
+  TestDisplayManagerObserver display_manager_observer;
+  DisplayManager* display_manager = window_server()->display_manager();
+  UserDisplayManager* user_display_manager =
+      display_manager->GetUserDisplayManager(kTestUserId1);
+  ASSERT_TRUE(user_display_manager);
+  user_display_manager->AddObserver(display_manager_observer.GetPtr());
+
+  // Observer should not have been notified yet.
+  //
+  // NOTE: the RunUntilIdle() calls are necessary anytime the calls are checked
+  // as the observer is called via mojo, which is async.
+  RunUntilIdle();
+  EXPECT_TRUE(display_manager_observer.GetAndClearObserverCalls().empty());
+
+  // Set frame decorations, again observer should not be notified.
+  WindowManagerState* window_manager_state =
+      window_server()->GetWindowManagerStateForUser(kTestUserId1);
+  ASSERT_TRUE(window_manager_state);
+  WindowTree* window_manager_tree = window_manager_state->window_tree();
+  window_manager_state->SetFrameDecorationValues(
+      mojom::FrameDecorationValues::New());
+  RunUntilIdle();
+  EXPECT_TRUE(display_manager_observer.GetAndClearObserverCalls().empty());
+
+  // Create a window for the windowmanager and set it as the root.
+  ClientWindowId display_root_id = BuildClientWindowId(window_manager_tree, 10);
+  ASSERT_TRUE(window_manager_tree->NewWindow(display_root_id,
+                                             ServerWindow::Properties()));
+  ServerWindow* display_root =
+      window_manager_tree->GetWindowByClientId(display_root_id);
+  ASSERT_TRUE(display_root);
+  RunUntilIdle();
+  EXPECT_TRUE(display_manager_observer.GetAndClearObserverCalls().empty());
+
+  // Add a new display.
+  display::Display display1 = MakeDisplay(0, 0, 1024, 768, 1.0f);
+  const int64_t display_id1 = 101;
+  display1.set_id(display_id1);
+  mojom::WmViewportMetrics metrics1;
+  metrics1.bounds_in_pixels = display1.bounds();
+  metrics1.device_scale_factor = 1.5;
+  metrics1.ui_scale_factor = 2.5;
+  const bool is_primary_display = true;
+  ASSERT_TRUE(WindowTreeTestApi(window_manager_tree)
+                  .ProcessSetDisplayRoot(display1, metrics1, is_primary_display,
+                                         display_root_id));
+  RunUntilIdle();
+  EXPECT_TRUE(display_manager_observer.GetAndClearObserverCalls().empty());
+
+  // Configure the displays.
+  std::vector<display::Display> displays;
+  displays.push_back(display1);
+  std::vector<ui::mojom::WmViewportMetricsPtr> viewport_metrics;
+  viewport_metrics.push_back(ui::mojom::WmViewportMetrics::New(metrics1));
+  ASSERT_TRUE(display_manager->SetDisplayConfiguration(
+      displays, std::move(viewport_metrics), display_id1));
+  RunUntilIdle();
+  EXPECT_EQ("OnDisplaysChanged " + std::to_string(display_id1),
+            display_manager_observer.GetAndClearObserverCalls());
+
+  // Create a window for the windowmanager and set it as the root.
+  ClientWindowId display_root_id2 =
+      BuildClientWindowId(window_manager_tree, 11);
+  ASSERT_TRUE(window_manager_tree->NewWindow(display_root_id2,
+                                             ServerWindow::Properties()));
+  ServerWindow* display_root2 =
+      window_manager_tree->GetWindowByClientId(display_root_id);
+  ASSERT_TRUE(display_root2);
+  RunUntilIdle();
+  EXPECT_TRUE(display_manager_observer.GetAndClearObserverCalls().empty());
+
+  // Add another display.
+  display::Display display2 = MakeDisplay(0, 0, 1024, 768, 1.0f);
+  const int64_t display_id2 = 102;
+  display2.set_id(display_id2);
+  mojom::WmViewportMetrics metrics2;
+  metrics2.bounds_in_pixels = display2.bounds();
+  metrics2.device_scale_factor = 1.5;
+  metrics2.ui_scale_factor = 2.5;
+  ASSERT_TRUE(
+      WindowTreeTestApi(window_manager_tree)
+          .ProcessSetDisplayRoot(display2, metrics2, false, display_root_id2));
+  RunUntilIdle();
+  EXPECT_TRUE(display_manager_observer.GetAndClearObserverCalls().empty());
+
+  // Make |display2| the default, and resize both displays.
+  display1.set_bounds(gfx::Rect(0, 0, 1024, 1280));
+  metrics1.bounds_in_pixels = display1.bounds();
+  displays.clear();
+  displays.push_back(display1);
+
+  display2.set_bounds(gfx::Rect(0, 0, 500, 600));
+  metrics2.bounds_in_pixels = display2.bounds();
+  displays.push_back(display2);
+
+  viewport_metrics.push_back(ui::mojom::WmViewportMetrics::New(metrics1));
+  viewport_metrics.push_back(ui::mojom::WmViewportMetrics::New(metrics2));
+  ASSERT_TRUE(display_manager->SetDisplayConfiguration(
+      displays, std::move(viewport_metrics), display_id2));
+  RunUntilIdle();
+  EXPECT_EQ("OnDisplaysChanged " + std::to_string(display_id1) + " " +
+                std::to_string(display_id2),
+            display_manager_observer.GetAndClearObserverCalls());
+
+  // Delete the second display, no notification should be sent.
+  EXPECT_TRUE(window_manager_tree->DeleteWindow(display_root_id2));
+  RunUntilIdle();
+  EXPECT_TRUE(display_manager_observer.GetAndClearObserverCalls().empty());
+  EXPECT_FALSE(display_manager->GetDisplayById(display_id2));
+
+  // Set the config back to only the first.
+  displays.clear();
+  displays.push_back(display1);
+
+  viewport_metrics.push_back(ui::mojom::WmViewportMetrics::New(metrics1));
+  ASSERT_TRUE(display_manager->SetDisplayConfiguration(
+      displays, std::move(viewport_metrics), display_id1));
+  RunUntilIdle();
+  EXPECT_EQ("OnDisplaysChanged " + std::to_string(display_id1),
+            display_manager_observer.GetAndClearObserverCalls());
+
+  // The display list should not have display2.
+  display::DisplayList& display_list =
+      display::ScreenManager::GetInstance()->GetScreen()->display_list();
+  EXPECT_TRUE(display_list.FindDisplayById(display_id2) ==
+              display_list.displays().end());
+  ASSERT_TRUE(display_list.GetPrimaryDisplayIterator() !=
+              display_list.displays().end());
+  EXPECT_EQ(display_id1, display_list.GetPrimaryDisplayIterator()->id());
+}
+
 }  // namespace test
 }  // namespace ws
 }  // namespace ui
diff --git a/third_party/WebKit/LayoutTests/TestExpectations b/third_party/WebKit/LayoutTests/TestExpectations
index ebf06c239..5ea6c46 100644
--- a/third_party/WebKit/LayoutTests/TestExpectations
+++ b/third_party/WebKit/LayoutTests/TestExpectations
@@ -122,6 +122,8 @@
 # ====== Paint team owned tests to here ======
 
 #### external/wpt/css/css-position-3
+crbug.com/702927 external/wpt/css/css-position-3/position-sticky-table-tfoot-bottom.html [ Failure ]
+crbug.com/702927 external/wpt/css/css-position-3/position-sticky-table-thead-top.html [ Failure ]
 crbug.com/702927 external/wpt/css/css-position-3/position-sticky-table-tr-top.html [ Failure ]
 crbug.com/702927 external/wpt/css/css-position-3/position-sticky-table-tr-bottom.html [ Failure ]
 
diff --git a/third_party/WebKit/LayoutTests/external/wpt/css/css-position-3/position-sticky-table-tfoot-bottom-ref.html b/third_party/WebKit/LayoutTests/external/wpt/css/css-position-3/position-sticky-table-tfoot-bottom-ref.html
new file mode 100644
index 0000000..a89dd6a
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/css/css-position-3/position-sticky-table-tfoot-bottom-ref.html
@@ -0,0 +1,62 @@
+<!DOCTYPE html>
+<title>Reference for position:sticky bottom constraint should behave correctly for &lt;tfoot&gt; elements</title>
+
+<style>
+.group {
+  display: inline-block;
+  position: relative;
+  width: 150px;
+  height: 200px;
+}
+
+.scroller {
+  position: relative;
+  width: 100px;
+  height: 150px;
+  overflow-x: hidden;
+  overflow-y: auto;
+}
+
+.contents {
+  height: 550px;
+}
+
+.indicator {
+  position: absolute;
+  background-color: green;
+  left: 0;
+  height: 50px;
+  width: 50px;
+}
+</style>
+
+<script>
+window.addEventListener('load', function() {
+  document.getElementById('scroller1').scrollTop = 0;
+  document.getElementById('scroller2').scrollTop = 75;
+  document.getElementById('scroller3').scrollTop = 200;
+});
+</script>
+
+<div class="group">
+  <div id="scroller1" class="scroller">
+    <div class="indicator" style="top: 100px;"></div>
+    <div class="contents"></div>
+  </div>
+</div>
+
+<div class="group">
+  <div id="scroller2" class="scroller">
+    <div class="indicator" style="top: 150px;"></div>
+    <div class="contents"></div>
+  </div>
+</div>
+
+<div class="group">
+  <div id="scroller3" class="scroller">
+    <div class="indicator" style="top: 250px;"></div>
+    <div class="contents"></div>
+  </div>
+</div>
+
+<div>You should see three green boxes above. No red should be visible.</div>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/css/css-position-3/position-sticky-table-tfoot-bottom.html b/third_party/WebKit/LayoutTests/external/wpt/css/css-position-3/position-sticky-table-tfoot-bottom.html
new file mode 100644
index 0000000..17fe3599
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/css/css-position-3/position-sticky-table-tfoot-bottom.html
@@ -0,0 +1,121 @@
+<!DOCTYPE html>
+<title>position:sticky bottom constraint should behave correctly for &lt;tfoot&gt; elements</title>
+<link rel="match" href="position-sticky-table-tfoot-bottom-ref.html" />
+<link rel="help" href="https://www.w3.org/TR/css-position-3/#sticky-pos" />
+<meta name="assert" content="This test checks that the position:sticky bottom constraint behaves correctly for &lt;tfoot&gt; elements" />
+
+<style>
+table {
+  border-collapse:collapse;
+}
+
+td, th {
+  padding: 0;
+}
+
+td > div, th > div {
+  height: 50px;
+  width: 50px;
+}
+
+.group {
+  display: inline-block;
+  position: relative;
+  width: 150px;
+  height: 200px;
+}
+
+.scroller {
+  position: relative;
+  width: 100px;
+  height: 150px;
+  overflow-x: hidden;
+  overflow-y: auto;
+}
+
+.prepadding {
+  height: 100px;
+}
+
+.postpadding {
+  height: 250px;
+}
+
+.indicator {
+  position: absolute;
+  background-color: red;
+  left: 0;
+  height: 50px;
+  width: 50px;
+}
+
+.sticky {
+  position: sticky;
+  bottom: 25px;
+  background-color: green;
+}
+</style>
+
+<script>
+window.addEventListener('load', function() {
+  document.getElementById('scroller1').scrollTop = 0;
+  document.getElementById('scroller2').scrollTop = 75;
+  document.getElementById('scroller3').scrollTop = 200;
+});
+</script>
+
+<div class="group">
+  <div id="scroller1" class="scroller">
+    <div class="indicator" style="top: 100px;"></div>
+    <div class="prepadding"></div>
+    <table>
+      <tbody>
+        <tr><td><div></div></td></tr>
+        <tr><td><div></div></td></tr>
+        <tr><td><div></div></td></tr>
+      </tbody>
+      <tfoot class="sticky">
+        <tr><th><div></div></th></tr>
+      </tfoot>
+    </table>
+    <div class="postpadding"></div>
+  </div>
+</div>
+
+<div class="group">
+  <div id="scroller2" class="scroller">
+    <div class="indicator" style="top: 150px;"></div>
+    <div class="prepadding"></div>
+    <table>
+      <tbody>
+        <tr><td><div></div></td></tr>
+        <tr><td><div></div></td></tr>
+        <tr><td><div></div></td></tr>
+      </tbody>
+      <tfoot class="sticky">
+        <tr><th><div></div></th></tr>
+      </tfoot>
+    </table>
+    <div class="postpadding"></div>
+  </div>
+</div>
+
+<div class="group">
+  <div id="scroller3" class="scroller">
+    <div class="indicator" style="top: 250px;"></div>
+    <div class="prepadding"></div>
+    <table>
+      <tbody>
+        <tr><td><div></div></td></tr>
+        <tr><td><div></div></td></tr>
+        <tr><td><div></div></td></tr>
+      </tbody>
+      <tfoot class="sticky">
+        <tr><th><div></div></th></tr>
+      </tfoot>
+    </table>
+    <div class="postpadding"></div>
+  </div>
+</div>
+
+<div>You should see three green boxes above. No red should be visible.</div>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/css/css-position-3/position-sticky-table-thead-top-ref.html b/third_party/WebKit/LayoutTests/external/wpt/css/css-position-3/position-sticky-table-thead-top-ref.html
new file mode 100644
index 0000000..f313d60
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/css/css-position-3/position-sticky-table-thead-top-ref.html
@@ -0,0 +1,62 @@
+<!DOCTYPE html>
+<title>Reference for position:sticky top constraint should behave correctly for &lt;thead&gt; elements</title>
+
+<style>
+.group {
+  display: inline-block;
+  position: relative;
+  width: 150px;
+  height: 200px;
+}
+
+.scroller {
+  position: relative;
+  width: 100px;
+  height: 150px;
+  overflow-x: hidden;
+  overflow-y: auto;
+}
+
+.contents {
+  height: 550px;
+}
+
+.indicator {
+  position: absolute;
+  background-color: green;
+  left: 0;
+  height: 50px;
+  width: 50px;
+}
+</style>
+
+<script>
+window.addEventListener('load', function() {
+  document.getElementById('scroller1').scrollTop = 50;
+  document.getElementById('scroller2').scrollTop = 125;
+  document.getElementById('scroller3').scrollTop = 250;
+});
+</script>
+
+<div class="group">
+  <div id="scroller1" class="scroller">
+    <div class="indicator" style="top: 100px;"></div>
+    <div class="contents"></div>
+  </div>
+</div>
+
+<div class="group">
+  <div id="scroller2" class="scroller">
+    <div class="indicator" style="top: 150px;"></div>
+    <div class="contents"></div>
+  </div>
+</div>
+
+<div class="group">
+  <div id="scroller3" class="scroller">
+    <div class="indicator" style="top: 250px;"></div>
+    <div class="contents"></div>
+  </div>
+</div>
+
+<div>You should see three green boxes above. No red should be visible.</div>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/css/css-position-3/position-sticky-table-thead-top.html b/third_party/WebKit/LayoutTests/external/wpt/css/css-position-3/position-sticky-table-thead-top.html
new file mode 100644
index 0000000..560a45e
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/css/css-position-3/position-sticky-table-thead-top.html
@@ -0,0 +1,121 @@
+<!DOCTYPE html>
+<title>position:sticky top constraint should behave correctly for &lt;thead&gt; elements</title>
+<link rel="match" href="position-sticky-table-thead-top-ref.html" />
+<link rel="help" href="https://www.w3.org/TR/css-position-3/#sticky-pos" />
+<meta name="assert" content="This test checks that the position:sticky top constraint behaves correctly for &lt;thead&gt; elements" />
+
+<style>
+table {
+  border-collapse:collapse;
+}
+
+td, th {
+  padding: 0;
+}
+
+td > div, th > div {
+  height: 50px;
+  width: 50px;
+}
+
+.group {
+  display: inline-block;
+  position: relative;
+  width: 150px;
+  height: 200px;
+}
+
+.scroller {
+  position: relative;
+  width: 100px;
+  height: 150px;
+  overflow-x: hidden;
+  overflow-y: auto;
+}
+
+.prepadding {
+  height: 100px;
+}
+
+.postpadding {
+  height: 250px;
+}
+
+.indicator {
+  position: absolute;
+  background-color: red;
+  left: 0;
+  height: 50px;
+  width: 50px;
+}
+
+.sticky {
+  position: sticky;
+  top: 25px;
+  background-color: green;
+}
+</style>
+
+<script>
+window.addEventListener('load', function() {
+  document.getElementById('scroller1').scrollTop = 50;
+  document.getElementById('scroller2').scrollTop = 125;
+  document.getElementById('scroller3').scrollTop = 250;
+});
+</script>
+
+<div class="group">
+  <div id="scroller1" class="scroller">
+    <div class="indicator" style="top: 100px;"></div>
+    <div class="prepadding"></div>
+    <table>
+      <thead class="sticky">
+        <tr><th><div></div></th></tr>
+      </thead>
+      <tbody>
+        <tr><td><div></div></td></tr>
+        <tr><td><div></div></td></tr>
+        <tr><td><div></div></td></tr>
+      </tbody>
+    </table>
+    <div class="postpadding"></div>
+  </div>
+</div>
+
+<div class="group">
+  <div id="scroller2" class="scroller">
+    <div class="indicator" style="top: 150px;"></div>
+    <div class="prepadding"></div>
+    <table>
+      <thead class="sticky">
+        <tr><th><div></div></th></tr>
+      </thead>
+      <tbody>
+        <tr><td><div></div></td></tr>
+        <tr><td><div></div></td></tr>
+        <tr><td><div></div></td></tr>
+      </tbody>
+    </table>
+    <div class="postpadding"></div>
+  </div>
+</div>
+
+<div class="group">
+  <div id="scroller3" class="scroller">
+    <div class="indicator" style="top: 250px;"></div>
+    <div class="prepadding"></div>
+    <table>
+      <thead class="sticky">
+        <tr><th><div></div></th></tr>
+      </thead>
+      <tbody>
+        <tr><td><div></div></td></tr>
+        <tr><td><div></div></td></tr>
+        <tr><td><div></div></td></tr>
+      </tbody>
+    </table>
+    <div class="postpadding"></div>
+  </div>
+</div>
+
+<div>You should see three green boxes above. No red should be visible.</div>
diff --git a/third_party/WebKit/LayoutTests/fast/css/sticky/sticky-position-works-with-scroll-apis.html b/third_party/WebKit/LayoutTests/fast/css/sticky/sticky-position-works-with-scroll-apis.html
new file mode 100644
index 0000000..6c58672
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/fast/css/sticky/sticky-position-works-with-scroll-apis.html
@@ -0,0 +1,130 @@
+<!DOCTYPE html>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<style>
+body {
+    margin: 0;
+}
+
+#scroller {
+    height: 100px;
+    width: 200px;
+    overflow-y: scroll;
+}
+
+.paddingBefore {
+    height: 150px;
+}
+
+#sticky {
+    height: 50px;
+    position: sticky;
+    top: 20px;
+}
+
+.paddingAfter {
+    height: 500px;
+}
+</style>
+
+<div id="scroller">
+  <div class="paddingBefore"></div>
+  <div id="writer"></div>
+  <div id="sticky"></div>
+  <div class="paddingAfter"></div>
+</div>
+
+<script>
+if (window.internals) {
+  internals.settings.setCSSStickyPositionEnabled(true);
+}
+
+// These tests currently mimic the behavior of Firefox for the interaction
+// between scrollIntoView() and position:sticky, where the offset location of
+// the sticky element is used to determine how far to scroll. This means that
+// scrollIntoView() may scroll even when the sticky is already 'in view', and
+// attempts to scroll so that the offset position of the sticky is at the top
+// of the screen.
+//
+// TODO(smcgruer): Update tests once http://crbug.com/664246 is resolved.
+
+test(function() {
+    var scroller = document.getElementById('scroller');
+    var sticky = document.getElementById('sticky');
+    var writer = document.getElementById('writer');
+
+    // Clean the writer.
+    writer.innerHTML = '';
+
+    // With no scroll, the sticky element is outside the scroller viewport.
+    scroller.scrollTop = 0;
+
+    // Deliberately dirty layout to make sure that scrollIntoView() still works.
+    writer.innerHTML = '<div style="height: 50px;"></div>';
+
+    sticky.scrollIntoView();
+    assert_equals(scroller.scrollTop, 200);
+}, "scrollIntoView should scroll when sticky is not visible");
+
+test(function() {
+    var scroller = document.getElementById('scroller');
+    var sticky = document.getElementById('sticky');
+    var writer = document.getElementById('writer');
+
+    // Clean the writer.
+    writer.innerHTML = '';
+
+    // Scroll so that the sticky element is past the top of the scroller
+    // viewport, and is thus sticking.
+    scroller.scrollTop = 200;
+
+    // Deliberately dirty layout to make sure that scrollIntoView() still works.
+    writer.innerHTML = '<div style="height: 10px;"></div>';
+
+    // See comment above tests for why this shifts by an additional 20 pixels.
+    sticky.scrollIntoView();
+    assert_equals(scroller.scrollTop, 230);
+}, "scrollIntoView should scroll when sticky is already in view");
+
+test(function() {
+    var scroller = document.getElementById('scroller');
+    var sticky = document.getElementById('sticky');
+    var writer = document.getElementById('writer');
+
+    // Clean the writer.
+    writer.innerHTML = '';
+
+    // With no scroll, the sticky element is outside the scroller viewport.
+    scroller.scrollTop = 0;
+
+    // Deliberately dirty layout to make sure that scrollIntoViewIfNeeded()
+    // still works.
+    writer.innerHTML = '<div style="height: 70px;"></div>';
+
+    sticky.scrollIntoViewIfNeeded();
+    assert_equals(scroller.scrollTop, 195);
+}, "scrollIntoViewIfNeeded should scroll when sticky is not visible");
+
+test(function() {
+    var scroller = document.getElementById('scroller');
+    var sticky = document.getElementById('sticky');
+    var writer = document.getElementById('writer');
+
+    // Clean the writer.
+    writer.innerHTML = '';
+
+    // Scroll so that the sticky element is at the top of the scroller viewport.
+    scroller.scrollTop = 150;
+
+    // Deliberately dirty layout to make sure that scrollIntoViewIfNeeded()
+    // still works.
+    writer.innerHTML = '<div style="height: 20px;"></div>';
+
+    // The scroll top moves to 170 due to scroll-anchoring, but should then not
+    // shift if we call scrollIntoViewIfNeeded.
+    assert_equals(scroller.scrollTop, 170);
+    sticky.scrollIntoViewIfNeeded();
+    assert_equals(scroller.scrollTop, 170);
+}, "scrollIntoViewIfNeeded should not scroll when sticky is already in view");
+</script>
+
diff --git a/third_party/WebKit/LayoutTests/http/tests/feature-policy/resources/blank.html b/third_party/WebKit/LayoutTests/http/tests/feature-policy/resources/blank.html
new file mode 100644
index 0000000..8de20fe
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/http/tests/feature-policy/resources/blank.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<html>
+<title>Informs the test runner when this page has loaded</title>
+<script>
+window.addEventListener('DOMContentLoaded', () => {
+  if (window.testRunner)
+    testRunner.notifyDone();
+});
+</script>
+</html>
diff --git a/third_party/WebKit/LayoutTests/http/tests/feature-policy/resources/xslt-document-insertion.xsl b/third_party/WebKit/LayoutTests/http/tests/feature-policy/resources/xslt-document-insertion.xsl
new file mode 100644
index 0000000..8dc9150
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/http/tests/feature-policy/resources/xslt-document-insertion.xsl
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
+  <xsl:template match="/">
+    <html>
+      <title>Checks that an XSLT-generated HTML doc allows first-party cookies</title>
+      <script>
+        if (window.testRunner) {
+          testRunner.waitUntilDone();
+          testRunner.dumpAsText();
+          testRunner.dumpChildFramesAsText();
+        }
+      </script>
+      <body>
+        <iframe allow="payment" src="http://127.0.0.1:8000/feature-policy/resources/blank.html"></iframe>
+      </body>
+    </html>
+  </xsl:template>
+</xsl:stylesheet>
diff --git a/third_party/WebKit/LayoutTests/http/tests/feature-policy/xslt-document-insertion-expected.txt b/third_party/WebKit/LayoutTests/http/tests/feature-policy/xslt-document-insertion-expected.txt
new file mode 100644
index 0000000..fd6f97229
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/http/tests/feature-policy/xslt-document-insertion-expected.txt
@@ -0,0 +1,6 @@
+
+
+--------
+Frame: '<!--framePath //<!--frame0-->-->'
+--------
+
diff --git a/third_party/WebKit/LayoutTests/http/tests/feature-policy/xslt-document-insertion.xml b/third_party/WebKit/LayoutTests/http/tests/feature-policy/xslt-document-insertion.xml
new file mode 100644
index 0000000..f9d0dafd
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/http/tests/feature-policy/xslt-document-insertion.xml
@@ -0,0 +1,3 @@
+<?xml version="1.0" encoding="utf-8"?>
+<?xml-stylesheet type="text/xsl" href="resources/xslt-document-insertion.xsl"?>
+<body/>
diff --git a/third_party/WebKit/LayoutTests/http/tests/serviceworker/chromium/resources/get-interface-names.js b/third_party/WebKit/LayoutTests/http/tests/serviceworker/chromium/resources/get-interface-names.js
deleted file mode 100644
index 7b9b1696..0000000
--- a/third_party/WebKit/LayoutTests/http/tests/serviceworker/chromium/resources/get-interface-names.js
+++ /dev/null
@@ -1,37 +0,0 @@
-function get_interface_names(global_object, interface_names) {
-  var result = [];
-  function collect_property_info(object, property_name, output) {
-    var descriptor = Object.getOwnPropertyDescriptor(object, property_name);
-    if ('value' in descriptor) {
-      if (typeof descriptor.value === 'function') {
-        output.push(' method ' + property_name);
-      } else {
-        output.push(' attribute ' + property_name);
-      }
-    } else {
-      if (descriptor.get) {
-        output.push(' getter ' + property_name);
-      }
-      if (descriptor.set) {
-        output.push(' setter ' + property_name);
-      }
-    }
-  }
-  interface_names.sort();
-  interface_names.forEach(function(interface_name) {
-    if (this[interface_name] === undefined) {
-      return;
-    }
-    result.push('interface ' + interface_name);
-    var property_names =
-        Object.getOwnPropertyNames(this[interface_name].prototype);
-    var property_strings = [];
-    property_names.forEach(function(property_name) {
-      collect_property_info(this[interface_name].prototype,
-                            property_name,
-                            property_strings);
-    });
-    result.push.apply(result, property_strings.sort());
-  });
-  return result.join("\n");;
-}
diff --git a/third_party/WebKit/LayoutTests/svg/as-image/adopt-while-async-svg-load-is-in-progress-crash.html b/third_party/WebKit/LayoutTests/svg/as-image/adopt-while-async-svg-load-is-in-progress-crash.html
new file mode 100644
index 0000000..dfcb1075
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/svg/as-image/adopt-while-async-svg-load-is-in-progress-crash.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
+<script>
+var t = async_test("Garbage collection of ImageResourceContent while " +
+                   "asynchronous loading of SVG is in progress " +
+                   "shouldn't crash. crbug.com/726220");
+var img;
+
+function step1() {
+  setTimeout(t.step_func(step2), 0);
+
+  // 1. Creating an <img> element with SVG of which loading is not completed
+  //    synchronously.
+  img = document.createElement('img');
+  img.src='data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg"><foreignObject><body xmlns="http://www.w3.org/1999/xhtml"><marquee></marquee></body></foreignObject></svg>';
+  document.body.appendChild(img);
+  assert_false(img.complete);
+}
+
+function step2() {
+  // 2. Adopt the <img> to a new document.
+  newdoc = document.implementation.createDocument("svg", null);
+  newdoc.adoptNode(img);
+
+  // 3. Do garbage collection. The oold ImageResourceContent is garbage
+  //    collected while async SVG loading is still in progress.
+  gc();
+
+  setTimeout(t.step_func_done(), 100);
+}
+
+</script>
+<body onload="setTimeout(t.step_func(step1), 0)"></body>
diff --git a/third_party/WebKit/Source/core/dom/Document.cpp b/third_party/WebKit/Source/core/dom/Document.cpp
index 234401cb..4f428b56 100644
--- a/third_party/WebKit/Source/core/dom/Document.cpp
+++ b/third_party/WebKit/Source/core/dom/Document.cpp
@@ -5644,6 +5644,48 @@
   return 0;
 }
 
+void Document::SetFeaturePolicy(const String& feature_policy_header) {
+  if (!RuntimeEnabledFeatures::featurePolicyEnabled())
+    return;
+
+  WebFeaturePolicy* parent_feature_policy = nullptr;
+  WebParsedFeaturePolicy container_policy;
+  Vector<String> messages;
+  const WebParsedFeaturePolicy& parsed_header =
+      ParseFeaturePolicy(feature_policy_header, GetSecurityOrigin(), &messages);
+
+  // If this frame is not the main frame, then get the appropriate parent policy
+  // and container policy to construct the policy for this frame.
+  if (frame_) {
+    if (!frame_->IsMainFrame()) {
+      parent_feature_policy =
+          frame_->Tree().Parent()->GetSecurityContext()->GetFeaturePolicy();
+    }
+    if (frame_->Owner())
+      container_policy = frame_->Owner()->ContainerPolicy();
+  }
+
+  // Check that if there is a parent frame, that its feature policy is
+  // correctly initialized. Crash if that is not the case. (Temporary crash for
+  // isolating the cause of https://crbug.com/722333)
+  // Note that even with this check removed, the process will stil crash in
+  // feature_policy.cc when it attempts to dereference parent_feature_policy.
+  // This check is to distinguish between two possible causes.
+  if (!container_policy.empty())
+    CHECK(frame_ && (frame_->IsMainFrame() || parent_feature_policy));
+
+  InitializeFeaturePolicy(parsed_header, container_policy,
+                          parent_feature_policy);
+
+  for (const auto& message : messages) {
+    AddConsoleMessage(
+        ConsoleMessage::Create(kOtherMessageSource, kErrorMessageLevel,
+                               "Error with Feature-Policy header: " + message));
+  }
+  if (frame_ && !parsed_header.empty())
+    frame_->Client()->DidSetFeaturePolicyHeader(parsed_header);
+}
+
 void Document::InitSecurityContext(const DocumentInit& initializer) {
   DCHECK(!GetSecurityOrigin());
 
@@ -5653,6 +5695,7 @@
     cookie_url_ = KURL(kParsedURLString, g_empty_string);
     SetSecurityOrigin(SecurityOrigin::CreateUnique());
     InitContentSecurityPolicy();
+    SetFeaturePolicy(g_empty_string);
     // Unique security origins cannot have a suborigin
     return;
   }
@@ -5753,6 +5796,8 @@
 
   if (GetSecurityOrigin()->HasSuborigin())
     EnforceSuborigin(*GetSecurityOrigin()->GetSuborigin());
+
+  SetFeaturePolicy(g_empty_string);
 }
 
 void Document::InitContentSecurityPolicy(ContentSecurityPolicy* csp) {
diff --git a/third_party/WebKit/Source/core/dom/Document.h b/third_party/WebKit/Source/core/dom/Document.h
index a9a4333..1b4703b 100644
--- a/third_party/WebKit/Source/core/dom/Document.h
+++ b/third_party/WebKit/Source/core/dom/Document.h
@@ -1336,6 +1336,8 @@
 
   CoreProbeSink* GetProbeSink() final;
 
+  void SetFeaturePolicy(const String& feature_policy_header);
+
  protected:
   Document(const DocumentInit&, DocumentClassFlags = kDefaultDocumentClass);
 
diff --git a/third_party/WebKit/Source/core/dom/SecurityContext.cpp b/third_party/WebKit/Source/core/dom/SecurityContext.cpp
index e5777d0..24cb6329 100644
--- a/third_party/WebKit/Source/core/dom/SecurityContext.cpp
+++ b/third_party/WebKit/Source/core/dom/SecurityContext.cpp
@@ -105,7 +105,6 @@
     const WebParsedFeaturePolicy& parsed_header,
     const WebParsedFeaturePolicy& container_policy,
     const WebFeaturePolicy* parent_feature_policy) {
-  DCHECK(!feature_policy_);
   WebSecurityOrigin origin = WebSecurityOrigin(security_origin_);
   feature_policy_ = Platform::Current()->CreateFeaturePolicy(
       parent_feature_policy, container_policy, parsed_header, origin);
diff --git a/third_party/WebKit/Source/core/editing/FrameSelection.cpp b/third_party/WebKit/Source/core/editing/FrameSelection.cpp
index 7315099..ab71a5b0 100644
--- a/third_party/WebKit/Source/core/editing/FrameSelection.cpp
+++ b/third_party/WebKit/Source/core/editing/FrameSelection.cpp
@@ -443,6 +443,12 @@
   if (focused_element->IsTextControl())
     return focused_element->ContainsIncludingHostElements(*current);
 
+  if (ComputeVisibleSelectionInFlatTree().IsNone()) {
+    // TODO(editing-dev): We should avoid any case where VSInFlatTree is none
+    // but VSInDOMTree is not none.
+    DLOG(FATAL) << ComputeVisibleSelectionInDOMTree();
+  }
+
   // Selection has focus if it contains the focused element.
   const PositionInFlatTree& focused_position =
       PositionInFlatTree::FirstPositionInNode(focused_element);
diff --git a/third_party/WebKit/Source/core/editing/markers/TextMatchMarker.h b/third_party/WebKit/Source/core/editing/markers/TextMatchMarker.h
index b7025f5..74c69dd4 100644
--- a/third_party/WebKit/Source/core/editing/markers/TextMatchMarker.h
+++ b/third_party/WebKit/Source/core/editing/markers/TextMatchMarker.h
@@ -75,10 +75,12 @@
   State state_;
 };
 
-DEFINE_TYPE_CASTS(TextMatchMarker, DocumentMarker, marker, true, true);
+DEFINE_TYPE_CASTS(TextMatchMarker,
+                  DocumentMarker,
+                  marker,
+                  marker->GetType() == DocumentMarker::kTextMatch,
+                  marker.GetType() == DocumentMarker::kTextMatch);
 
 }  // namespace blink
 
-WTF_ALLOW_MOVE_INIT_AND_COMPARE_WITH_MEM_FUNCTIONS(blink::TextMatchMarker);
-
 #endif
diff --git a/third_party/WebKit/Source/core/frame/UseCounter.h b/third_party/WebKit/Source/core/frame/UseCounter.h
index ae8a328..4a71b34e 100644
--- a/third_party/WebKit/Source/core/frame/UseCounter.h
+++ b/third_party/WebKit/Source/core/frame/UseCounter.h
@@ -1616,6 +1616,7 @@
     kHTMLOListElementStartGetterReversedWithoutStartAttribute = 2011,
     kCredentialManagerPreventSilentAccess = 2012,
     kNetInfoEffectiveType = 2013,
+    kV8SpeechRecognition_Start_Method = 2014,
 
     // Add new features immediately above this line. Don't change assigned
     // numbers of any item, and don't reuse removed slots.
diff --git a/third_party/WebKit/Source/core/frame/WebFrameWidgetBase.cpp b/third_party/WebKit/Source/core/frame/WebFrameWidgetBase.cpp
index 214e27b..4657b6d 100644
--- a/third_party/WebKit/Source/core/frame/WebFrameWidgetBase.cpp
+++ b/third_party/WebKit/Source/core/frame/WebFrameWidgetBase.cpp
@@ -10,11 +10,14 @@
 #include "core/frame/LocalFrameView.h"
 #include "core/frame/VisualViewport.h"
 #include "core/frame/WebLocalFrameBase.h"
+#include "core/input/ContextMenuAllowedScope.h"
 #include "core/input/EventHandler.h"
+#include "core/page/ContextMenuController.h"
 #include "core/page/DragActions.h"
 #include "core/page/DragController.h"
 #include "core/page/DragData.h"
 #include "core/page/DragSession.h"
+#include "core/page/FocusController.h"
 #include "core/page/Page.h"
 #include "core/page/PointerLockController.h"
 #include "platform/UserGestureIndicator.h"
@@ -284,4 +287,19 @@
   }
 }
 
+void WebFrameWidgetBase::ShowContextMenu(WebMenuSourceType source_type) {
+  if (!GetPage())
+    return;
+
+  GetPage()->GetContextMenuController().ClearContextMenu();
+  {
+    ContextMenuAllowedScope scope;
+    if (LocalFrame* focused_frame =
+            GetPage()->GetFocusController().FocusedFrame()) {
+      focused_frame->GetEventHandler().ShowNonLocatedContextMenu(nullptr,
+                                                                 source_type);
+    }
+  }
+}
+
 }  // namespace blink
diff --git a/third_party/WebKit/Source/core/frame/WebFrameWidgetBase.h b/third_party/WebKit/Source/core/frame/WebFrameWidgetBase.h
index 843c2e3..3d8d07af 100644
--- a/third_party/WebKit/Source/core/frame/WebFrameWidgetBase.h
+++ b/third_party/WebKit/Source/core/frame/WebFrameWidgetBase.h
@@ -82,6 +82,7 @@
   void DidAcquirePointerLock() override;
   void DidNotAcquirePointerLock() override;
   void DidLosePointerLock() override;
+  void ShowContextMenu(WebMenuSourceType) override;
 
  protected:
   enum DragAction { kDragEnter, kDragOver };
diff --git a/third_party/WebKit/Source/core/html/HTMLCanvasElement.cpp b/third_party/WebKit/Source/core/html/HTMLCanvasElement.cpp
index 7b04491..463937a 100644
--- a/third_party/WebKit/Source/core/html/HTMLCanvasElement.cpp
+++ b/third_party/WebKit/Source/core/html/HTMLCanvasElement.cpp
@@ -914,6 +914,9 @@
     return nullptr;
   }
 
+  if (MemoryCoordinator::IsLowEndDevice())
+    surface->DisableDeferral(kDisableDeferralReasonLowEndDevice);
+
   CanvasMetrics::CountCanvasContextUsage(
       CanvasMetrics::kGPUAccelerated2DCanvasImageBufferCreated);
   return std::move(surface);
diff --git a/third_party/WebKit/Source/core/html/HTMLVideoElement.cpp b/third_party/WebKit/Source/core/html/HTMLVideoElement.cpp
index a3466a0..5e6d31b 100644
--- a/third_party/WebKit/Source/core/html/HTMLVideoElement.cpp
+++ b/third_party/WebKit/Source/core/html/HTMLVideoElement.cpp
@@ -508,21 +508,18 @@
 }
 
 void HTMLVideoElement::MediaRemotingStopped() {
-  // Early return because this was already called when media remoting was
-  // disabled.
-  if (media_remoting_status_ == MediaRemotingStatus::kDisabled)
-    return;
-  DCHECK(media_remoting_status_ == MediaRemotingStatus::kStarted);
+  DCHECK(media_remoting_status_ == MediaRemotingStatus::kDisabled ||
+         media_remoting_status_ == MediaRemotingStatus::kStarted);
+  if (media_remoting_status_ != MediaRemotingStatus::kDisabled)
+    media_remoting_status_ = MediaRemotingStatus::kNotStarted;
   DCHECK(remoting_interstitial_);
-  media_remoting_status_ = MediaRemotingStatus::kNotStarted;
   remoting_interstitial_->Hide();
 }
 
 void HTMLVideoElement::DisableMediaRemoting() {
+  media_remoting_status_ = MediaRemotingStatus::kDisabled;
   if (GetWebMediaPlayer())
     GetWebMediaPlayer()->RequestRemotePlaybackDisabled(true);
-  media_remoting_status_ = MediaRemotingStatus::kDisabled;
-  MediaRemotingStopped();
 }
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/core/html/canvas/CanvasFontCache.cpp b/third_party/WebKit/Source/core/html/canvas/CanvasFontCache.cpp
index b5c5260..6bdcd59f 100644
--- a/third_party/WebKit/Source/core/html/canvas/CanvasFontCache.cpp
+++ b/third_party/WebKit/Source/core/html/canvas/CanvasFontCache.cpp
@@ -15,7 +15,9 @@
 namespace {
 
 const unsigned CanvasFontCacheMaxFonts = 50;
+const unsigned CanvasFontCacheMaxFontsLowEnd = 5;
 const unsigned CanvasFontCacheHardMaxFonts = 250;
+const unsigned CanvasFontCacheHardMaxFontsLowEnd = 20;
 const unsigned CanvasFontCacheHiddenMaxFonts = 1;
 const int defaultFontSize = 10;
 const char defaultFontFamily[] = "sans-serif";
@@ -45,12 +47,15 @@
 }
 
 unsigned CanvasFontCache::MaxFonts() {
-  return CanvasFontCacheMaxFonts;
+  return MemoryCoordinator::IsLowEndDevice() ? CanvasFontCacheMaxFontsLowEnd
+                                             : CanvasFontCacheMaxFonts;
 }
 
 unsigned CanvasFontCache::HardMaxFonts() {
   return document_->hidden() ? CanvasFontCacheHiddenMaxFonts
-                             : CanvasFontCacheHardMaxFonts;
+                             : (MemoryCoordinator::IsLowEndDevice()
+                                    ? CanvasFontCacheHardMaxFontsLowEnd
+                                    : CanvasFontCacheHardMaxFonts);
 }
 
 bool CanvasFontCache::GetFontUsingDefaultStyle(const String& font_string,
diff --git a/third_party/WebKit/Source/core/loader/DocumentLoader.cpp b/third_party/WebKit/Source/core/loader/DocumentLoader.cpp
index e2748316..2ffab13 100644
--- a/third_party/WebKit/Source/core/loader/DocumentLoader.cpp
+++ b/third_party/WebKit/Source/core/loader/DocumentLoader.cpp
@@ -995,41 +995,6 @@
   frame_->GetPage()->DidCommitLoad(frame_);
 }
 
-void SetFeaturePolicy(Document* document, const String& feature_policy_header) {
-  if (!RuntimeEnabledFeatures::featurePolicyEnabled())
-    return;
-  LocalFrame* frame = document->GetFrame();
-  WebFeaturePolicy* parent_feature_policy =
-      frame->IsMainFrame()
-          ? nullptr
-          : frame->Tree().Parent()->GetSecurityContext()->GetFeaturePolicy();
-  Vector<String> messages;
-  const WebParsedFeaturePolicy& parsed_header = ParseFeaturePolicy(
-      feature_policy_header, frame->GetSecurityContext()->GetSecurityOrigin(),
-      &messages);
-  WebParsedFeaturePolicy container_policy;
-  if (frame->Owner())
-    container_policy = frame->Owner()->ContainerPolicy();
-  // Check that if there is a parent frame, that its feature policy is
-  // correctly initialized. Crash if that is not the case. (Temporary crash for
-  // isolating the cause of https://crbug.com/722333)
-  // Note that even with this check removed, the process will stil crash in
-  // feature_policy.cc when it attempts to dereference parent_feature_policy.
-  // This check is to distinguish between two possible causes.
-  if (!container_policy.empty())
-    CHECK(frame->IsMainFrame() || parent_feature_policy);
-  frame->GetSecurityContext()->InitializeFeaturePolicy(
-      parsed_header, container_policy, parent_feature_policy);
-
-  for (auto& message : messages) {
-    document->AddConsoleMessage(
-        ConsoleMessage::Create(kOtherMessageSource, kErrorMessageLevel,
-                               "Error with Feature-Policy header: " + message));
-  }
-  if (!parsed_header.empty())
-    frame->Client()->DidSetFeaturePolicyHeader(parsed_header);
-}
-
 // static
 bool DocumentLoader::ShouldClearWindowName(
     const LocalFrame& frame,
@@ -1096,8 +1061,8 @@
   // FeaturePolicy is reset in the browser process on commit, so this needs to
   // be initialized and replicated to the browser process after commit messages
   // are sent in didCommitNavigation().
-  SetFeaturePolicy(document,
-                   response_.HttpHeaderField(HTTPNames::Feature_Policy));
+  document->SetFeaturePolicy(
+      response_.HttpHeaderField(HTTPNames::Feature_Policy));
 
   GetFrameLoader().DispatchDidClearDocumentOfWindowObject();
 }
diff --git a/third_party/WebKit/Source/core/svg/graphics/SVGImage.cpp b/third_party/WebKit/Source/core/svg/graphics/SVGImage.cpp
index d235c5f..51c7354 100644
--- a/third_party/WebKit/Source/core/svg/graphics/SVGImage.cpp
+++ b/third_party/WebKit/Source/core/svg/graphics/SVGImage.cpp
@@ -577,6 +577,9 @@
 }
 
 void SVGImage::ServiceAnimations(double monotonic_animation_start_time) {
+  if (!GetImageObserver())
+    return;
+
   // If none of our observers (sic!) are visible, or for some other reason
   // does not want us to keep running animations, stop them until further
   // notice (next paint.)
diff --git a/third_party/WebKit/Source/core/svg/graphics/SVGImageChromeClient.cpp b/third_party/WebKit/Source/core/svg/graphics/SVGImageChromeClient.cpp
index de6e9ec..5af954d 100644
--- a/third_party/WebKit/Source/core/svg/graphics/SVGImageChromeClient.cpp
+++ b/third_party/WebKit/Source/core/svg/graphics/SVGImageChromeClient.cpp
@@ -114,11 +114,12 @@
 
   // The SVGImageChromeClient object's lifetime is dependent on
   // the ImageObserver (an ImageResourceContent) of its image. Should it
-  // be dead and about to be lazily swept out, do not proceed.
+  // be dead and about to be lazily swept out, then GetImageObserver()
+  // becomes null and we do not proceed.
   //
   // TODO(Oilpan): move (SVG)Image to the Oilpan heap, and avoid
   // this explicit lifetime check.
-  if (ThreadHeap::WillObjectBeLazilySwept(image_->GetImageObserver()))
+  if (!image_->GetImageObserver())
     return;
 
   image_->ServiceAnimations(MonotonicallyIncreasingTime());
diff --git a/third_party/WebKit/Source/devtools/front_end/accessibility/AccessibilityModel.js b/third_party/WebKit/Source/devtools/front_end/accessibility/AccessibilityModel.js
index 87dd668..61184bd 100644
--- a/third_party/WebKit/Source/devtools/front_end/accessibility/AccessibilityModel.js
+++ b/third_party/WebKit/Source/devtools/front_end/accessibility/AccessibilityModel.js
@@ -244,30 +244,18 @@
    * @param {!SDK.DOMNode} node
    * @return {!Promise}
    */
-  requestPartialAXTree(node) {
-    /**
-     * @this {Accessibility.AccessibilityModel}
-     * @param {?string} error
-     * @param {!Array<!Protocol.Accessibility.AXNode>=} payloads
-     */
-    function parsePayload(error, payloads) {
-      if (error) {
-        console.error('AccessibilityAgent.getAXNodeChain(): ' + error);
-        return null;
-      }
+  async requestPartialAXTree(node) {
+    var payloads = await this._agent.getPartialAXTree(node.id, true);
+    if (!payloads)
+      return;
 
-      if (!payloads)
-        return;
+    for (var payload of payloads)
+      new Accessibility.AccessibilityNode(this, payload);
 
-      for (var payload of payloads)
-        new Accessibility.AccessibilityNode(this, payload);
-
-      for (var axNode of this._axIdToAXNode.values()) {
-        for (var axChild of axNode.children())
-          axChild._setParentNode(axNode);
-      }
+    for (var axNode of this._axIdToAXNode.values()) {
+      for (var axChild of axNode.children())
+        axChild._setParentNode(axNode);
     }
-    return this._agent.getPartialAXTree(node.id, true, parsePayload.bind(this));
   }
 
   /**
diff --git a/third_party/WebKit/Source/devtools/scripts/build/generate_protocol_externs.py b/third_party/WebKit/Source/devtools/scripts/build/generate_protocol_externs.py
index 12c747f..5470571 100755
--- a/third_party/WebKit/Source/devtools/scripts/build/generate_protocol_externs.py
+++ b/third_party/WebKit/Source/devtools/scripts/build/generate_protocol_externs.py
@@ -47,7 +47,6 @@
 ref_types = {}
 
 NON_PROMISIFIED_DOMAINS = frozenset([
-    "Accessibility",
     "Animation",
     "ApplicationCache",
     "CacheStorage",
diff --git a/third_party/WebKit/Source/modules/speech/SpeechRecognition.idl b/third_party/WebKit/Source/modules/speech/SpeechRecognition.idl
index a1b29ced..27f29ed 100644
--- a/third_party/WebKit/Source/modules/speech/SpeechRecognition.idl
+++ b/third_party/WebKit/Source/modules/speech/SpeechRecognition.idl
@@ -43,7 +43,7 @@
     [RuntimeEnabled=MediaStreamSpeech] attribute MediaStreamTrack? audioTrack;
 
     // methods to drive the speech interaction
-    [RaisesException] void start();
+    [RaisesException, Measure] void start();
     [ImplementedAs=stopFunction] void stop();
     void abort();
 
diff --git a/third_party/WebKit/Source/platform/graphics/GraphicsTypes.h b/third_party/WebKit/Source/platform/graphics/GraphicsTypes.h
index d22c298e..365cb14d 100644
--- a/third_party/WebKit/Source/platform/graphics/GraphicsTypes.h
+++ b/third_party/WebKit/Source/platform/graphics/GraphicsTypes.h
@@ -116,6 +116,7 @@
   kDisableDeferralReasonDrawImageOfAnimated2dCanvas = 4,
   kDisableDeferralReasonSubPixelTextAntiAliasingSupport = 5,
   kDisableDeferralDrawImageWithTextureBackedSourceImage = 6,
+  kDisableDeferralReasonLowEndDevice = 7,
   kDisableDeferralReasonCount,
 };
 
diff --git a/third_party/WebKit/Source/platform/graphics/Image.h b/third_party/WebKit/Source/platform/graphics/Image.h
index faecfa1..b3e30393 100644
--- a/third_party/WebKit/Source/platform/graphics/Image.h
+++ b/third_party/WebKit/Source/platform/graphics/Image.h
@@ -229,9 +229,11 @@
   // TODO(Oilpan): consider having Image on the Oilpan heap and
   // turn this into a Member<>.
   //
-  // The observer (an ImageResourceContent) is an untraced member, with the
-  // ImageResourceContent being responsible for clearing itself out.
-  UntracedMember<ImageObserver> image_observer_;
+  // The observer (an ImageResourceContent) is responsible for clearing
+  // itself out when it switches to another Image.
+  // When the ImageResourceContent is garbage collected while Image is still
+  // alive, |image_observer_| is cleared by WeakPersistent mechanism.
+  WeakPersistent<ImageObserver> image_observer_;
   PaintImage::Id stable_image_id_;
 };
 
diff --git a/third_party/WebKit/Source/platform/graphics/RecordingImageBufferSurface.cpp b/third_party/WebKit/Source/platform/graphics/RecordingImageBufferSurface.cpp
index 6e8101b..45a340d 100644
--- a/third_party/WebKit/Source/platform/graphics/RecordingImageBufferSurface.cpp
+++ b/third_party/WebKit/Source/platform/graphics/RecordingImageBufferSurface.cpp
@@ -220,6 +220,8 @@
     case kDisableDeferralDrawImageWithTextureBackedSourceImage:
       return RecordingImageBufferSurface::
           kFallbackReasonDrawImageWithTextureBackedSourceImage;
+    // The LowEndDevice reason should only be used on Canvas2DLayerBridge.
+    case kDisableDeferralReasonLowEndDevice:
     case kDisableDeferralReasonCount:
       NOTREACHED();
       break;
diff --git a/third_party/WebKit/Source/web/WebViewImpl.cpp b/third_party/WebKit/Source/web/WebViewImpl.cpp
index c747a7a..12e2e3b5 100644
--- a/third_party/WebKit/Source/web/WebViewImpl.cpp
+++ b/third_party/WebKit/Source/web/WebViewImpl.cpp
@@ -3500,18 +3500,12 @@
 }
 
 void WebViewImpl::ShowContextMenu(WebMenuSourceType source_type) {
-  if (!GetPage())
+  if (!MainFrameImpl())
     return;
 
-  GetPage()->GetContextMenuController().ClearContextMenu();
-  {
-    ContextMenuAllowedScope scope;
-    if (LocalFrame* focused_frame = ToLocalFrame(
-            GetPage()->GetFocusController().FocusedOrMainFrame())) {
-      focused_frame->GetEventHandler().ShowNonLocatedContextMenu(nullptr,
-                                                                 source_type);
-    }
-  }
+  // If MainFrameImpl() is non-null, then FrameWidget() will also be non-null.
+  DCHECK(MainFrameImpl()->FrameWidget());
+  MainFrameImpl()->FrameWidget()->ShowContextMenu(source_type);
 }
 
 void WebViewImpl::DidCloseContextMenu() {
diff --git a/third_party/WebKit/Source/web/WebViewImpl.h b/third_party/WebKit/Source/web/WebViewImpl.h
index ba65d8f..116282d 100644
--- a/third_party/WebKit/Source/web/WebViewImpl.h
+++ b/third_party/WebKit/Source/web/WebViewImpl.h
@@ -150,6 +150,7 @@
   void DidAcquirePointerLock() override;
   void DidNotAcquirePointerLock() override;
   void DidLosePointerLock() override;
+  void ShowContextMenu(WebMenuSourceType) override;
 
   // WebView methods:
   virtual bool IsWebView() const { return true; }
@@ -230,7 +231,6 @@
                           unsigned inactive_background_color,
                           unsigned inactive_foreground_color) override;
   void PerformCustomContextMenuAction(unsigned action) override;
-  void ShowContextMenu(WebMenuSourceType) override;
   void DidCloseContextMenu() override;
   void HidePopups() override;
   void SetPageOverlayColor(WebColor) override;
diff --git a/third_party/WebKit/Source/web/tests/WebViewTest.cpp b/third_party/WebKit/Source/web/tests/WebViewTest.cpp
index 661727b..dad995b 100644
--- a/third_party/WebKit/Source/web/tests/WebViewTest.cpp
+++ b/third_party/WebKit/Source/web/tests/WebViewTest.cpp
@@ -2457,7 +2457,7 @@
   EXPECT_TRUE(main_frame->GetFrame()->Selection().IsCaretBlinkingSuspended());
 
   // Caret blinking is still suspended after showing context menu.
-  web_view->ShowContextMenu(kMenuSourceMouse);
+  web_view->GetWidget()->ShowContextMenu(kMenuSourceMouse);
   EXPECT_TRUE(main_frame->GetFrame()->Selection().IsCaretBlinkingSuspended());
 
   // Caret blinking will be resumed only after context menu is closed.
diff --git a/third_party/WebKit/public/web/WebView.h b/third_party/WebKit/public/web/WebView.h
index c55118ba..04b8b90 100644
--- a/third_party/WebKit/public/web/WebView.h
+++ b/third_party/WebKit/public/web/WebView.h
@@ -31,7 +31,6 @@
 #ifndef WebView_h
 #define WebView_h
 
-#include "WebMenuSourceType.h"
 #include "WebWidget.h"
 #include "public/platform/WebColor.h"
 #include "public/platform/WebDisplayMode.h"
@@ -389,9 +388,6 @@
 
   virtual void PerformCustomContextMenuAction(unsigned action) = 0;
 
-  // Shows a context menu for the currently focused element.
-  virtual void ShowContextMenu(WebMenuSourceType) = 0;
-
   // Notify that context menu has been closed.
   virtual void DidCloseContextMenu() = 0;
 
diff --git a/third_party/WebKit/public/web/WebWidget.h b/third_party/WebKit/public/web/WebWidget.h
index 4dae362..1985722 100644
--- a/third_party/WebKit/public/web/WebWidget.h
+++ b/third_party/WebKit/public/web/WebWidget.h
@@ -40,9 +40,10 @@
 #include "public/platform/WebRect.h"
 #include "public/platform/WebSize.h"
 #include "public/platform/WebTextInputInfo.h"
-#include "WebCompositionUnderline.h"
-#include "WebRange.h"
-#include "WebTextDirection.h"
+#include "public/web/WebCompositionUnderline.h"
+#include "public/web/WebMenuSourceType.h"
+#include "public/web/WebRange.h"
+#include "public/web/WebTextDirection.h"
 
 namespace blink {
 
@@ -224,6 +225,9 @@
     return false;
   }
 
+  // Called by client to request showing the context menu.
+  virtual void ShowContextMenu(WebMenuSourceType) {}
+
  protected:
   ~WebWidget() {}
 };
diff --git a/third_party/android_media/BUILD.gn b/third_party/android_media/BUILD.gn
new file mode 100644
index 0000000..a4a375ae
--- /dev/null
+++ b/third_party/android_media/BUILD.gn
@@ -0,0 +1,26 @@
+# 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.
+
+import("//build/config/android/rules.gni")
+
+assert(is_android)
+
+android_resources("android_media_resources") {
+  custom_package = "org.chromium.third_party.android.media"
+  resource_dirs = [ "java/res" ]
+  deps = [
+    "//third_party/android_tools:android_support_v7_appcompat_java",
+    "//third_party/android_tools:android_support_v7_mediarouter_java",
+  ]
+}
+
+android_library("android_media_java") {
+  java_files =
+      [ "java/src/org/chromium/third_party/android/media/MediaController.java" ]
+  deps = [
+    ":android_media_resources",
+    "//third_party/android_tools:android_support_v7_appcompat_java",
+    "//third_party/android_tools:android_support_v7_mediarouter_java",
+  ]
+}
diff --git a/third_party/android_media/LICENSE b/third_party/android_media/LICENSE
new file mode 100644
index 0000000..d645695
--- /dev/null
+++ b/third_party/android_media/LICENSE
@@ -0,0 +1,202 @@
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
diff --git a/third_party/android_media/OWNERS b/third_party/android_media/OWNERS
new file mode 100644
index 0000000..d88be63
--- /dev/null
+++ b/third_party/android_media/OWNERS
@@ -0,0 +1,3 @@
+aberent@chromium.org
+dgn@chromium.org
+avayvod@chromium.org
\ No newline at end of file
diff --git a/third_party/android_media/README.chromium b/third_party/android_media/README.chromium
new file mode 100644
index 0000000..5ca4a46
--- /dev/null
+++ b/third_party/android_media/README.chromium
@@ -0,0 +1,20 @@
+Name: MediaController Android sample.
+URL: https://android.googlesource.com/platform/development/+/b356564/samples/Support4Demos/src/com/example/android/supportv4/media/MediaController.java
+Version: 74c5bcff29959931c8a4b15e4afa613975a47f4c
+License: Apache 2.0
+Security Critical: no
+
+Description:
+This contains a modified copy of MediaController.java and the associated
+resources.
+
+MediaController.java is based on a public Android sample that was
+available as part of the Android support libraries installation (see
+https://developer.android.com/tools/support-library/index.html). It is
+also available from:
+https://android.googlesource.com/platform/development/+/b356564/samples/Support4Demos/src/com/example/android/supportv4/media/MediaController.java
+
+Local Modifications:
+- TransportController and TransportMediator are replaced with a custom
+  interface
+- minor bug fixes.
diff --git a/third_party/android_media/java/res/layout/media_controller.xml b/third_party/android_media/java/res/layout/media_controller.xml
new file mode 100644
index 0000000..734b4b9
--- /dev/null
+++ b/third_party/android_media/java/res/layout/media_controller.xml
@@ -0,0 +1,99 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2007 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<!-- This file is taken from the Android codebase.
+     samples/Support4Demos/res/layout/media_controller.xml
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:orientation="vertical">
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:gravity="center"
+        android:paddingTop="4dip"
+        android:orientation="horizontal">
+
+        <!-- Android Lint is giving a false warning that layout_width and layout_height are not
+             set for buttons below, however MediaButton style sets the width and the height. -->
+        <!--suppress RequiredSize -->
+        <ImageButton
+            android:id="@+id/prev"
+            android:contentDescription="@null"
+            style="@android:style/MediaButton.Previous" />
+        <!--suppress RequiredSize -->
+        <ImageButton
+            android:id="@+id/rew"
+            android:contentDescription="@null"
+            style="@android:style/MediaButton.Rew" />
+        <!--suppress RequiredSize -->
+        <ImageButton
+            android:id="@+id/pause"
+            android:contentDescription="@null"
+            style="@android:style/MediaButton.Play" />
+        <!--suppress RequiredSize -->
+        <ImageButton
+            android:id="@+id/ffwd"
+            android:contentDescription="@null"
+            style="@android:style/MediaButton.Ffwd" />
+        <!--suppress RequiredSize -->
+        <ImageButton
+            android:id="@+id/next"
+            android:contentDescription="@null"
+            style="@android:style/MediaButton.Next" />
+
+    </LinearLayout>
+
+    <LinearLayout
+        android:id="@+id/mediacontroller_progress_container"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="horizontal">
+
+        <TextView android:id="@+id/time_current"
+            android:textSize="14sp"
+            android:textStyle="bold"
+            android:paddingTop="4dip"
+            android:paddingStart="4dip"
+            android:layout_gravity="center_horizontal"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:paddingEnd="4dip"
+            android:textColor="@color/cast_media_controller_text" />
+
+        <SeekBar
+            android:id="@+id/mediacontroller_progress_bar"
+            style="?android:attr/progressBarStyleHorizontal"
+            android:layout_width="0dip"
+            android:layout_weight="1"
+            android:layout_height="32dip" />
+
+        <TextView android:id="@+id/time"
+            android:textSize="14sp"
+            android:textStyle="bold"
+            android:paddingTop="4dip"
+            android:paddingEnd="4dip"
+            android:layout_gravity="center_horizontal"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:paddingStart="4dip"
+            android:textColor="@color/cast_media_controller_text" />
+    </LinearLayout>
+
+</LinearLayout>
+
diff --git a/third_party/android_media/java/res/values/colors.xml b/third_party/android_media/java/res/values/colors.xml
new file mode 100644
index 0000000..d6c6e3c
--- /dev/null
+++ b/third_party/android_media/java/res/values/colors.xml
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright 2014 The Chromium Authors. All rights reserved.
+     Use of this source code is governed by a BSD-style license that can be
+     found in the LICENSE file. -->
+
+<resources>
+    <!-- Cast colors -->
+    <!-- This is the same as the internal Android color "dim_foreground_dark"
+         which is used in MediaController. -->
+    <color name="cast_media_controller_text">#bebebe</color>
+
+</resources>
diff --git a/third_party/android_media/java/src/org/chromium/third_party/android/media/MediaController.java b/third_party/android_media/java/src/org/chromium/third_party/android/media/MediaController.java
new file mode 100644
index 0000000..fd6b7f2
--- /dev/null
+++ b/third_party/android_media/java/src/org/chromium/third_party/android/media/MediaController.java
@@ -0,0 +1,391 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.chromium.third_party.android.media;
+
+import android.content.Context;
+import android.support.v4.media.session.PlaybackStateCompat;
+import android.util.AttributeSet;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityNodeInfo;
+import android.widget.FrameLayout;
+import android.widget.ImageButton;
+import android.widget.SeekBar;
+import android.widget.TextView;
+
+import org.chromium.third_party.android.media.R;
+
+import java.util.Formatter;
+import java.util.Locale;
+
+/**
+ * Helper for implementing media controls in an application.
+ * We use this custom version instead of {@link android.widget.MediaController} so that we can
+ * customize the look as we want. This file is taken directly from the public Android sample for
+ * supportv4, with tiny bug fixes.
+ */
+public class MediaController extends FrameLayout {
+    /**
+     * The interface that allows media controller to actually control media and provides some
+     * essential metadata for the UI like the current position, duration, etc.
+     */
+    public interface Delegate {
+        /**
+         * Called when the user wants to resume or start.
+         */
+        void play();
+
+        /**
+         * Called when the user wants to pause.
+         */
+        void pause();
+
+        /**
+         * Called when the user wants to seek.
+         */
+        void seekTo(long pos);
+
+        /**
+         * @return the current media duration, in milliseconds.
+         */
+        long getDuration();
+
+        /**
+         * @return the current playback position, in milliseconds.
+         */
+        long getPosition();
+
+        /**
+         * @return if the media is currently playing.
+         */
+        boolean isPlaying();
+
+        /**
+         * @return a combination of {@link PlaybackStateCompat} flags defining what UI elements will
+         *         be available to the user.
+         */
+        long getActionFlags();
+    }
+
+    private Delegate mDelegate;
+    private Context mContext;
+    private ViewGroup mProgressGroup;
+    private SeekBar mProgressBar;
+    private TextView mEndTime, mCurrentTime;
+    private boolean mDragging;
+    private boolean mUseFastForward;
+    private boolean mListenersSet;
+    private boolean mShowNext, mShowPrev;
+    private View.OnClickListener mNextListener, mPrevListener;
+    private StringBuilder mFormatBuilder;
+    private Formatter mFormatter;
+    private ImageButton mPauseButton;
+    private ImageButton mFfwdButton;
+    private ImageButton mRewButton;
+    private ImageButton mNextButton;
+    private ImageButton mPrevButton;
+
+    public MediaController(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        mContext = context;
+        mUseFastForward = true;
+        LayoutInflater inflate =
+                (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+        inflate.inflate(R.layout.media_controller, this, true);
+        initControllerView();
+    }
+
+    public MediaController(Context context, boolean useFastForward) {
+        super(context);
+        mContext = context;
+        mUseFastForward = useFastForward;
+    }
+
+    public MediaController(Context context) {
+        this(context, true);
+    }
+
+    public void setDelegate(Delegate delegate) {
+        mDelegate = delegate;
+        updatePausePlay();
+    }
+
+    private void initControllerView() {
+        mPauseButton = (ImageButton) findViewById(R.id.pause);
+        if (mPauseButton != null) {
+            mPauseButton.requestFocus();
+            mPauseButton.setOnClickListener(mPauseListener);
+        }
+
+        mFfwdButton = (ImageButton) findViewById(R.id.ffwd);
+        if (mFfwdButton != null) {
+            mFfwdButton.setOnClickListener(mFfwdListener);
+            mFfwdButton.setVisibility(mUseFastForward ? View.VISIBLE : View.GONE);
+        }
+
+        mRewButton = (ImageButton) findViewById(R.id.rew);
+        if (mRewButton != null) {
+            mRewButton.setOnClickListener(mRewListener);
+            mRewButton.setVisibility(mUseFastForward ? View.VISIBLE : View.GONE);
+        }
+
+        // By default these are hidden. They will be enabled when setPrevNextListeners() is called
+        mNextButton = (ImageButton) findViewById(R.id.next);
+        if (mNextButton != null && !mListenersSet) {
+            mNextButton.setVisibility(View.GONE);
+        }
+        mPrevButton = (ImageButton) findViewById(R.id.prev);
+        if (mPrevButton != null && !mListenersSet) {
+            mPrevButton.setVisibility(View.GONE);
+        }
+
+        mProgressGroup = (ViewGroup) findViewById(R.id.mediacontroller_progress_container);
+
+        if (mProgressGroup != null) {
+            mProgressBar = (SeekBar) mProgressGroup.findViewById(R.id.mediacontroller_progress_bar);
+            if (mProgressBar != null) {
+                mProgressBar.setOnSeekBarChangeListener(mSeekListener);
+                mProgressBar.setMax(1000);
+            }
+        }
+
+        mEndTime = (TextView) findViewById(R.id.time);
+        mCurrentTime = (TextView) findViewById(R.id.time_current);
+        mFormatBuilder = new StringBuilder();
+        mFormatter = new Formatter(mFormatBuilder, Locale.getDefault());
+
+        installPrevNextListeners();
+    }
+
+    /**
+     * Disable pause or seek buttons if the stream cannot be paused or seeked.
+     * This requires the control interface to be a MediaPlayerControlExt
+     */
+    void updateButtons() {
+        if (mDelegate == null) return;
+
+        long flags = mDelegate.getActionFlags();
+        boolean enabled = isEnabled();
+        if (mPauseButton != null) {
+            boolean needPlayPauseButton = (flags & PlaybackStateCompat.ACTION_PLAY) != 0
+                    || (flags & PlaybackStateCompat.ACTION_PAUSE) != 0;
+            mPauseButton.setEnabled(enabled && needPlayPauseButton);
+        }
+        if (mRewButton != null) {
+            mRewButton.setEnabled(enabled && (flags & PlaybackStateCompat.ACTION_REWIND) != 0);
+        }
+        if (mFfwdButton != null) {
+            mFfwdButton.setEnabled(
+                    enabled && (flags & PlaybackStateCompat.ACTION_FAST_FORWARD) != 0);
+        }
+        if (mPrevButton != null) {
+            mShowPrev =
+                    (flags & PlaybackStateCompat.ACTION_SKIP_TO_NEXT) != 0 || mPrevListener != null;
+            mPrevButton.setEnabled(enabled && mShowPrev);
+        }
+        if (mNextButton != null) {
+            mShowNext = (flags & PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS) != 0
+                    || mNextListener != null;
+            mNextButton.setEnabled(enabled && mShowNext);
+        }
+    }
+
+    public void refresh() {
+        updateProgress();
+        updateButtons();
+        updatePausePlay();
+    }
+
+    private String stringForTime(int timeMs) {
+        int totalSeconds = timeMs / 1000;
+
+        int seconds = totalSeconds % 60;
+        int minutes = (totalSeconds / 60) % 60;
+        int hours = totalSeconds / 3600;
+
+        mFormatBuilder.setLength(0);
+        if (hours > 0) {
+            return mFormatter.format("%d:%02d:%02d", hours, minutes, seconds).toString();
+        } else {
+            return mFormatter.format("%02d:%02d", minutes, seconds).toString();
+        }
+    }
+
+    public long updateProgress() {
+        if (mDelegate == null || mDragging) return 0;
+
+        long position = mDelegate.getPosition();
+        long duration = mDelegate.getDuration();
+        if (duration <= 0) {
+            // If there is no valid duration, hide the progress bar and time indicators.
+            if (mProgressGroup != null) mProgressGroup.setVisibility(View.INVISIBLE);
+        } else if (mProgressBar != null) {
+            if (mProgressGroup != null) mProgressGroup.setVisibility(View.VISIBLE);
+            // use long to avoid overflow
+            long pos = 1000L * position / duration;
+            mProgressBar.setProgress((int) pos);
+            mProgressBar.setSecondaryProgress((int) pos);
+        }
+
+        if (mEndTime != null) mEndTime.setText(stringForTime((int) duration));
+        if (mCurrentTime != null) mCurrentTime.setText(stringForTime((int) position));
+
+        return position;
+    }
+
+    private View.OnClickListener mPauseListener = new View.OnClickListener() {
+        @Override
+        public void onClick(View v) {
+            doPauseResume();
+        }
+    };
+
+    private void updatePausePlay() {
+        if (mDelegate == null || mPauseButton == null) return;
+
+        if (mDelegate.isPlaying()) {
+            mPauseButton.setImageResource(android.R.drawable.ic_media_pause);
+        } else {
+            mPauseButton.setImageResource(android.R.drawable.ic_media_play);
+        }
+    }
+
+    private void doPauseResume() {
+        if (mDelegate == null) return;
+
+        if (mDelegate.isPlaying()) {
+            mDelegate.pause();
+        } else {
+            mDelegate.play();
+        }
+        updatePausePlay();
+    }
+
+    // There are two scenarios that can trigger the seekbar listener to trigger:
+    //
+    // The first is the user using the touchpad to adjust the posititon of the
+    // seekbar's thumb. In this case onStartTrackingTouch is called followed by
+    // a number of onProgressChanged notifications, concluded by onStopTrackingTouch.
+    // We're setting the field "mDragging" to true for the duration of the dragging
+    // session to avoid jumps in the position in case of ongoing playback.
+    //
+    // The second scenario involves the user operating the scroll ball, in this
+    // case there WON'T BE onStartTrackingTouch/onStopTrackingTouch notifications,
+    // we will simply apply the updated position without suspending regular updates.
+    private SeekBar.OnSeekBarChangeListener mSeekListener = new SeekBar.OnSeekBarChangeListener() {
+        @Override
+        public void onStartTrackingTouch(SeekBar bar) {
+            mDragging = true;
+        }
+
+        @Override
+        public void onProgressChanged(SeekBar bar, int progress, boolean fromuser) {
+            if (mDelegate == null) return;
+
+            if (!fromuser) {
+                // We're not interested in programmatically generated changes to
+                // the progress bar's position.
+                return;
+            }
+
+            long duration = mDelegate.getDuration();
+            long newposition = (duration * progress) / 1000L;
+            mDelegate.seekTo(newposition);
+            if (mCurrentTime != null) mCurrentTime.setText(stringForTime((int) newposition));
+        }
+
+        @Override
+        public void onStopTrackingTouch(SeekBar bar) {
+            mDragging = false;
+            updateProgress();
+            updatePausePlay();
+        }
+    };
+
+    @Override
+    public void setEnabled(boolean enabled) {
+        super.setEnabled(enabled);
+        updateButtons();
+    }
+
+    @Override
+    public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
+        super.onInitializeAccessibilityEvent(event);
+        event.setClassName(MediaController.class.getName());
+    }
+
+    @Override
+    public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
+        super.onInitializeAccessibilityNodeInfo(info);
+        info.setClassName(MediaController.class.getName());
+    }
+
+    private View.OnClickListener mRewListener = new View.OnClickListener() {
+        @Override
+        public void onClick(View v) {
+            if (mDelegate == null) return;
+
+            long pos = mDelegate.getPosition();
+            pos -= 5000; // milliseconds
+            mDelegate.seekTo(pos);
+            updateProgress();
+        }
+    };
+
+    private View.OnClickListener mFfwdListener = new View.OnClickListener() {
+        @Override
+        public void onClick(View v) {
+            if (mDelegate == null) return;
+
+            long pos = mDelegate.getPosition();
+            pos += 15000; // milliseconds
+            mDelegate.seekTo(pos);
+            updateProgress();
+        }
+    };
+
+    private void installPrevNextListeners() {
+        if (mNextButton != null) {
+            mNextButton.setOnClickListener(mNextListener);
+            mNextButton.setEnabled(mShowNext);
+        }
+
+        if (mPrevButton != null) {
+            mPrevButton.setOnClickListener(mPrevListener);
+            mPrevButton.setEnabled(mShowPrev);
+        }
+    }
+
+    public void setPrevNextListeners(View.OnClickListener next, View.OnClickListener prev) {
+        mNextListener = next;
+        mPrevListener = prev;
+        mListenersSet = true;
+
+        installPrevNextListeners();
+
+        if (mNextButton != null) {
+            mNextButton.setVisibility(View.VISIBLE);
+            mShowNext = true;
+        }
+        if (mPrevButton != null) {
+            mPrevButton.setVisibility(View.VISIBLE);
+            mShowPrev = true;
+        }
+    }
+}
diff --git a/third_party/gvr-android-sdk/test-apks/daydream_home/apk_version_history.txt b/third_party/gvr-android-sdk/test-apks/daydream_home/apk_version_history.txt
index e9c4d4bd..04d47f04 100644
--- a/third_party/gvr-android-sdk/test-apks/daydream_home/apk_version_history.txt
+++ b/third_party/gvr-android-sdk/test-apks/daydream_home/apk_version_history.txt
@@ -5,3 +5,4 @@
 v1.3 db5eaf6e83a10e809b96375212d5c9dbbe517624
 v1.4 9afe32eb4676d4bcceae81605a3be6aa0e5be3d5
 v1.5 a02e2f95aa6f741f2be0ae03a4829e21fd749cf3
+v1.6 43b876df3398687dfa1ae059ef2f64009c76254e
diff --git a/third_party/gvr-android-sdk/test-apks/daydream_home/daydream_home_current.apk.sha1 b/third_party/gvr-android-sdk/test-apks/daydream_home/daydream_home_current.apk.sha1
index 40e771c..5c6216fb6 100644
--- a/third_party/gvr-android-sdk/test-apks/daydream_home/daydream_home_current.apk.sha1
+++ b/third_party/gvr-android-sdk/test-apks/daydream_home/daydream_home_current.apk.sha1
@@ -1 +1 @@
-a02e2f95aa6f741f2be0ae03a4829e21fd749cf3
\ No newline at end of file
+43b876df3398687dfa1ae059ef2f64009c76254e
\ No newline at end of file
diff --git a/third_party/gvr-android-sdk/test-apks/vr_services/apk_version_history.txt b/third_party/gvr-android-sdk/test-apks/vr_services/apk_version_history.txt
index 06abccf..7b87608 100644
--- a/third_party/gvr-android-sdk/test-apks/vr_services/apk_version_history.txt
+++ b/third_party/gvr-android-sdk/test-apks/vr_services/apk_version_history.txt
@@ -8,3 +8,4 @@
 vvv 64-bit
 v1.4 3e0cc24655847c7b922149754324a189e7092310
 v1.5 5d6d55728c7c728cef5416f37b0b71615719474e
+v1.6 abcdae2281956a76aa3b98d2e8f05a1975170dd0
diff --git a/third_party/gvr-android-sdk/test-apks/vr_services/vr_services_current.apk.sha1 b/third_party/gvr-android-sdk/test-apks/vr_services/vr_services_current.apk.sha1
index 33fec988..2877b099 100644
--- a/third_party/gvr-android-sdk/test-apks/vr_services/vr_services_current.apk.sha1
+++ b/third_party/gvr-android-sdk/test-apks/vr_services/vr_services_current.apk.sha1
@@ -1 +1 @@
-5d6d55728c7c728cef5416f37b0b71615719474e
\ No newline at end of file
+abcdae2281956a76aa3b98d2e8f05a1975170dd0
\ No newline at end of file
diff --git a/tools/android/eclipse/.classpath b/tools/android/eclipse/.classpath
index e66118f6..17a0858 100644
--- a/tools/android/eclipse/.classpath
+++ b/tools/android/eclipse/.classpath
@@ -287,6 +287,7 @@
     <classpathentry kind="lib" path="out/Debug/lib.java/sync/android/sync_java.jar"/>
     <classpathentry kind="lib" path="out/Debug/lib.java/sync/test_support_proto_java.jar"/>
     <classpathentry kind="lib" path="out/Debug/lib.java/third_party/android_data_chart/android_data_chart_java.jar"/>
+    <classpathentry kind="lib" path="out/Debug/lib.java/third_party/android_media/android_media_java.jar"/>
     <classpathentry kind="lib" path="out/Debug/lib.java/third_party/android_protobuf/protobuf_nano_javalib.jar"/>
     <classpathentry kind="lib" path="out/Debug/lib.java/third_party/android_swipe_refresh/android_swipe_refresh_java.jar"/>
     <classpathentry kind="lib" path="out/Debug/lib.java/third_party/cacheinvalidation/cacheinvalidation_javalib.jar"/>
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index 79756bc..0afb7ec 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -3408,6 +3408,8 @@
   <int value="3" label="Direct pixel write."/>
   <int value="4" label="Draw image of animated 2d canvas"/>
   <int value="5" label="Sub-pixel text anti-aliasing support (expect 0)"/>
+  <int value="6" label="drawImage called with a texture backed source image"/>
+  <int value="7" label="Running on low-end device"/>
 </enum>
 
 <enum name="CanvasHibernationEvent" type="int">
@@ -15073,6 +15075,7 @@
       label="HTMLOListElementStartGetterReversedWithoutStartAttribute"/>
   <int value="2012" label="CredentialManagerPreventSilentAccess"/>
   <int value="2013" label="NetInfoEffectiveType"/>
+  <int value="2014" label="V8SpeechRecognition_Start_Method"/>
 </enum>
 
 <enum name="FeedbackSource" type="int">
@@ -21683,6 +21686,7 @@
   <int value="-1463410070" label="IPH_DemoMode:enabled"/>
   <int value="-1460462432" label="disable-media-source"/>
   <int value="-1456004000" label="VrShell:disabled"/>
+  <int value="-1450576851" label="OmniboxUIExperimentVerticalLayout:enabled"/>
   <int value="-1443796945" label="OfflinePagesSharing:disabled"/>
   <int value="-1440440375" label="WebVrAutopresent:enabled"/>
   <int value="-1440152291" label="disable-gesture-typing"/>
@@ -22445,6 +22449,7 @@
   <int value="1318073661" label="MaterialDesignExtensions:enabled"/>
   <int value="1319725131" label="enable-distance-field-text"/>
   <int value="1320201920" label="enable-touchpad-three-finger-click"/>
+  <int value="1330264457" label="OmniboxUIExperimentVerticalLayout:disabled"/>
   <int value="1344833841" label="ImeThread:enabled"/>
   <int value="1351830811" label="do-not-ignore-autocomplete-off"/>
   <int value="1352447982" label="enable-lcd-text"/>
diff --git a/tools/perf/page_sets/webrtc_cases.py b/tools/perf/page_sets/webrtc_cases.py
index 50e5e57..a6cacbaf 100644
--- a/tools/perf/page_sets/webrtc_cases.py
+++ b/tools/perf/page_sets/webrtc_cases.py
@@ -140,5 +140,9 @@
 
 class WebrtcExpectations(story.expectations.StoryExpectations):
   def SetExpectations(self):
-    self.DisableStory('multiple_peerconnections', [story.expectations.ALL],
+    self.DisableStory('multiple_peerconnections',
+                      [story.expectations.ALL],
                       'crbug.com/725502')
+    self.DisableStory('30s_datachannel_transfer',
+                      [story.expectations.ALL_DESKTOP],
+                      'crbug.com/726811')
diff --git a/ui/aura/mus/window_manager_delegate.h b/ui/aura/mus/window_manager_delegate.h
index e1876e56..8ba30ad 100644
--- a/ui/aura/mus/window_manager_delegate.h
+++ b/ui/aura/mus/window_manager_delegate.h
@@ -91,6 +91,13 @@
   // DisplayInitParams on the returned object.
   virtual WindowTreeHostMusInitParams CreateInitParamsForNewDisplay() = 0;
 
+  // Configures the displays. This is used when the window manager manually
+  // configures display roots.
+  virtual void SetDisplayConfiguration(
+      const std::vector<display::Display>& displays,
+      std::vector<ui::mojom::WmViewportMetricsPtr> viewport_metrics,
+      int64_t primary_display_id) = 0;
+
  protected:
   virtual ~WindowManagerClient() {}
 };
diff --git a/ui/aura/mus/window_tree_client.cc b/ui/aura/mus/window_tree_client.cc
index 5b3a58f..0b93d12 100644
--- a/ui/aura/mus/window_tree_client.cc
+++ b/ui/aura/mus/window_tree_client.cc
@@ -193,6 +193,12 @@
   GetWindowTreeHostMus(target)->SendEventToSink(event);
 }
 
+// Use for acks from mus that are expected to always succeed and if they don't
+// a crash is triggered.
+void OnAckMustSucceed(bool success) {
+  CHECK(success);
+}
+
 }  // namespace
 
 WindowTreeClient::WindowTreeClient(
@@ -1869,6 +1875,18 @@
     window_manager_client_->WmRequestClose(WindowMus::Get(window)->server_id());
 }
 
+void WindowTreeClient::SetDisplayConfiguration(
+    const std::vector<display::Display>& displays,
+    std::vector<ui::mojom::WmViewportMetricsPtr> viewport_metrics,
+    int64_t primary_display_id) {
+  DCHECK_EQ(displays.size(), viewport_metrics.size());
+  if (window_manager_client_) {
+    window_manager_client_->SetDisplayConfiguration(
+        displays, std::move(viewport_metrics), primary_display_id,
+        base::Bind(&OnAckMustSucceed));
+  }
+}
+
 void WindowTreeClient::OnWindowTreeHostBoundsWillChange(
     WindowTreeHostMus* window_tree_host,
     const gfx::Rect& bounds) {
diff --git a/ui/aura/mus/window_tree_client.h b/ui/aura/mus/window_tree_client.h
index 41a22f9..06acb9a 100644
--- a/ui/aura/mus/window_tree_client.h
+++ b/ui/aura/mus/window_tree_client.h
@@ -477,6 +477,10 @@
   void RequestClose(Window* window) override;
   bool WaitForInitialDisplays() override;
   WindowTreeHostMusInitParams CreateInitParamsForNewDisplay() override;
+  void SetDisplayConfiguration(
+      const std::vector<display::Display>& displays,
+      std::vector<ui::mojom::WmViewportMetricsPtr> viewport_metrics,
+      int64_t primary_display_id) override;
 
   // Overriden from WindowTreeHostMusDelegate:
   void OnWindowTreeHostBoundsWillChange(WindowTreeHostMus* window_tree_host,
diff --git a/ui/aura/test/mus/test_window_manager_client.cc b/ui/aura/test/mus/test_window_manager_client.cc
index 11154fb7..0131e12 100644
--- a/ui/aura/test/mus/test_window_manager_client.cc
+++ b/ui/aura/test/mus/test_window_manager_client.cc
@@ -44,6 +44,12 @@
     Id window_id,
     const SetDisplayRootCallback& callback) {}
 
+void TestWindowManagerClient::SetDisplayConfiguration(
+    const std::vector<display::Display>& displays,
+    std::vector<::ui::mojom::WmViewportMetricsPtr> viewport_metrics,
+    int64_t primary_display_id,
+    const SetDisplayConfigurationCallback& callback) {}
+
 void TestWindowManagerClient::WmResponse(uint32_t change_id, bool response) {}
 
 void TestWindowManagerClient::WmSetBoundsResponse(uint32_t change_id) {}
diff --git a/ui/aura/test/mus/test_window_manager_client.h b/ui/aura/test/mus/test_window_manager_client.h
index 468f6d1..87373d231 100644
--- a/ui/aura/test/mus/test_window_manager_client.h
+++ b/ui/aura/test/mus/test_window_manager_client.h
@@ -41,6 +41,11 @@
                       bool is_primary_display,
                       Id window_id,
                       const SetDisplayRootCallback& callback) override;
+  void SetDisplayConfiguration(
+      const std::vector<display::Display>& displays,
+      std::vector<::ui::mojom::WmViewportMetricsPtr> viewport_metrics,
+      int64_t primary_display_id,
+      const SetDisplayConfigurationCallback& callback) override;
   void WmResponse(uint32_t change_id, bool response) override;
   void WmSetBoundsResponse(uint32_t change_id) override;
   void WmRequestClose(Id transport_window_id) override;
diff --git a/ui/display/display.h b/ui/display/display.h
index 03c9ddd..acb4535 100644
--- a/ui/display/display.h
+++ b/ui/display/display.h
@@ -209,6 +209,8 @@
   Rotation rotation_ = ROTATE_0;
   TouchSupport touch_support_ = TOUCH_SUPPORT_UNKNOWN;
   gfx::Size maximum_cursor_size_;
+  // NOTE: this is not currently written to the mojom as it is not used in
+  // aura.
   gfx::ICCProfile icc_profile_;
   int color_depth_;
   int depth_per_component_;
diff --git a/ui/display/display_list.cc b/ui/display/display_list.cc
index b5f830a..74eb5c9b 100644
--- a/ui/display/display_list.cc
+++ b/ui/display/display_list.cc
@@ -52,6 +52,13 @@
   return base::WrapUnique(new DisplayListObserverLock(this));
 }
 
+void DisplayList::AddOrUpdateDisplay(const Display& display, Type type) {
+  if (FindDisplayById(display.id()) == displays_.end())
+    AddDisplay(display, type);
+  else
+    UpdateDisplay(display, type);
+}
+
 uint32_t DisplayList::UpdateDisplay(const Display& display) {
   return UpdateDisplay(display, GetTypeByDisplayId(display.id()));
 }
diff --git a/ui/display/display_list.h b/ui/display/display_list.h
index 20a454d..295dc3f 100644
--- a/ui/display/display_list.h
+++ b/ui/display/display_list.h
@@ -64,6 +64,8 @@
   // callers release the last lock they call the observers appropriately.
   std::unique_ptr<DisplayListObserverLock> SuspendObserverUpdates();
 
+  void AddOrUpdateDisplay(const Display& display, Type type);
+
   // Updates the cached display based on display.id(). This returns a bitmask
   // of the changed values suitable for passing to
   // DisplayObserver::OnDisplayMetricsChanged().
diff --git a/ui/events/ozone/evdev/gamepad_event_converter_evdev.cc b/ui/events/ozone/evdev/gamepad_event_converter_evdev.cc
index 6724c75..ecd4513 100644
--- a/ui/events/ozone/evdev/gamepad_event_converter_evdev.cc
+++ b/ui/events/ozone/evdev/gamepad_event_converter_evdev.cc
@@ -56,7 +56,7 @@
   MapValue(abs_info.value, &tmp);
 }
 
-bool GamepadEventConverterEvdev::Axis::MapValue(uint16_t value,
+bool GamepadEventConverterEvdev::Axis::MapValue(int value,
                                                 double* mapped_value) {
   *mapped_value = value * scale_ + offset_;
   // As the definition of linux input_absinfo.flat, value within the range of
@@ -185,7 +185,7 @@
 
 void GamepadEventConverterEvdev::ProcessEvdevKey(
     uint16_t code,
-    uint16_t value,
+    int value,
     const base::TimeTicks& timestamp) {
   GamepadEventType mapped_type;
   uint16_t mapped_code;
@@ -203,7 +203,7 @@
 
 void GamepadEventConverterEvdev::ProcessEvdevAbs(
     uint16_t code,
-    uint16_t value,
+    int value,
     const base::TimeTicks& timestamp) {
   GamepadEventType mapped_type;
   uint16_t mapped_code;
diff --git a/ui/events/ozone/evdev/gamepad_event_converter_evdev.h b/ui/events/ozone/evdev/gamepad_event_converter_evdev.h
index 7ec4d2a1..d1f233a 100644
--- a/ui/events/ozone/evdev/gamepad_event_converter_evdev.h
+++ b/ui/events/ozone/evdev/gamepad_event_converter_evdev.h
@@ -46,12 +46,12 @@
  private:
   // This function processes EV_KEY event from gamepad device.
   void ProcessEvdevKey(uint16_t code,
-                       uint16_t value,
+                       int value,
                        const base::TimeTicks& timestamp);
 
   // This function processes EV_ABS event from gamepad device.
   void ProcessEvdevAbs(uint16_t code,
-                       uint16_t value,
+                       int value,
                        const base::TimeTicks& timestamp);
 
   // This function releases all the keys and resets all the axises.
@@ -82,7 +82,7 @@
          GamepadEventType mapped_type,
          uint16_t mapped_code);
 
-    bool MapValue(uint16_t value, double* mapped_value);
+    bool MapValue(int value, double* mapped_value);
 
     GamepadEventType mapped_type();