diff --git a/AUTHORS b/AUTHORS
index f43d3a3..e6615048 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -439,6 +439,7 @@
 Marco Rodrigues <gothicx@gmail.com>
 Mario Pistrich <m.pistrich@gmail.com>
 Mario Sanchez Prada <mario.prada@samsung.com>
+Marcin Wiacek <marcin@mwiacek.com>
 Mariusz Mlynski <marius.mlynski@gmail.com>
 Mark Hahnenberg <mhahnenb@andrew.cmu.edu>
 Mark Seaborn <mrs@mythic-beasts.com>
diff --git a/DEPS b/DEPS
index 5c14e0f3..6308819 100644
--- a/DEPS
+++ b/DEPS
@@ -40,7 +40,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling Skia
   # and whatever else without interference from each other.
-  'skia_revision': '2be687af58ec1f445c6380457d136562ef27155e',
+  'skia_revision': '0bd783f951a004ccca175b166f2b30a0fd18a6f6',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling V8
   # and whatever else without interference from each other.
@@ -64,7 +64,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling PDFium
   # and whatever else without interference from each other.
-  'pdfium_revision': 'dc39e377e1a3923e63569020beb29f9662c1a3ee',
+  'pdfium_revision': '0ef7ba086f9e48100f9caebd0a52de478f0ada0a',
   # 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.
diff --git a/ash/BUILD.gn b/ash/BUILD.gn
index 87f968a..aa46412 100644
--- a/ash/BUILD.gn
+++ b/ash/BUILD.gn
@@ -1027,6 +1027,8 @@
     "shell/bubble.cc",
     "shell/context_menu.cc",
     "shell/context_menu.h",
+    "shell/example_app_list_presenter.cc",
+    "shell/example_app_list_presenter.h",
     "shell/example_factory.h",
     "shell/lock_view.cc",
     "shell/panel_window.cc",
@@ -1058,6 +1060,7 @@
     "//components/user_manager",
     "//skia",
     "//ui/app_list/presenter",
+    "//ui/app_list/presenter:test_support",
     "//ui/aura",
     "//ui/base",
     "//ui/gfx",
@@ -1096,6 +1099,7 @@
     "//device/bluetooth",
     "//net",
     "//skia",
+    "//ui/app_list/presenter:test_support",
     "//ui/aura",
     "//ui/base",
     "//ui/base/ime",
@@ -1364,7 +1368,9 @@
     "//testing/gtest",
     "//third_party/icu",
     "//ui/accessibility",
+    "//ui/app_list:test_support",
     "//ui/app_list/presenter",
+    "//ui/app_list/presenter:test_support",
     "//ui/aura",
     "//ui/aura:test_support",
     "//ui/base",
diff --git a/ash/accelerators/DEPS b/ash/accelerators/DEPS
index 5471592..b6b2ae6 100644
--- a/ash/accelerators/DEPS
+++ b/ash/accelerators/DEPS
@@ -2,4 +2,7 @@
   "accelerator_controller_delegate_aura\.cc": [
     "+ash/host"
   ],
+  "accelerator_interactive_uitest_chromeos\.cc": [
+    "+mojo/edk/embedder/embedder.h"
+  ],
 }
diff --git a/ash/accelerators/accelerator_controller_unittest.cc b/ash/accelerators/accelerator_controller_unittest.cc
index abf2ab7..84aacdd 100644
--- a/ash/accelerators/accelerator_controller_unittest.cc
+++ b/ash/accelerators/accelerator_controller_unittest.cc
@@ -30,6 +30,8 @@
 #include "ash/wm/window_util.h"
 #include "base/test/user_action_tester.cc"
 #include "services/ui/public/interfaces/window_manager_constants.mojom.h"
+#include "ui/app_list/presenter/app_list.h"
+#include "ui/app_list/presenter/test/test_app_list_presenter.h"
 #include "ui/aura/client/aura_constants.h"
 #include "ui/aura/test/test_window_delegate.h"
 #include "ui/aura/test/test_windows.h"
@@ -274,7 +276,6 @@
   return WmShell::Get()->accelerator_controller();
 }
 
-#if !defined(OS_WIN)
 // Double press of exit shortcut => exiting
 TEST_F(AcceleratorControllerTest, ExitWarningHandlerTestDoublePress) {
   ui::Accelerator press(ui::VKEY_Q, ui::EF_SHIFT_DOWN | ui::EF_CONTROL_DOWN);
@@ -330,7 +331,6 @@
 
   // Exit ash and there should be no crash
 }
-#endif  // !defined(OS_WIN)
 
 TEST_F(AcceleratorControllerTest, Register) {
   const ui::Accelerator accelerator_a(ui::VKEY_A, ui::EF_NONE);
@@ -743,15 +743,8 @@
 }
 
 // TODO(oshima): Fix this test to use EventGenerator.
-#if defined(OS_WIN)
-// crbug.com/317592
-#define MAYBE_ProcessOnce DISABLED_ProcessOnce
-#else
-#define MAYBE_ProcessOnce ProcessOnce
-#endif
-
-#if defined(OS_WIN) || defined(USE_X11)
-TEST_F(AcceleratorControllerTest, MAYBE_ProcessOnce) {
+#if defined(USE_X11)
+TEST_F(AcceleratorControllerTest, ProcessOnce) {
   // The IME event filter interferes with the basic key event propagation we
   // attempt to do here, so we disable it.
   DisableIME();
@@ -762,22 +755,7 @@
   // The accelerator is processed only once.
   ui::EventProcessor* dispatcher =
       Shell::GetPrimaryRootWindow()->GetHost()->event_processor();
-#if defined(OS_WIN)
-  MSG msg1 = {NULL, WM_KEYDOWN, ui::VKEY_A, 0};
-  ui::KeyEvent key_event1(msg1);
-  ui::EventDispatchDetails details = dispatcher->OnEventFromSource(&key_event1);
-  EXPECT_TRUE(key_event1.handled() || details.dispatcher_destroyed);
 
-  MSG msg2 = {NULL, WM_CHAR, L'A', 0};
-  ui::KeyEvent key_event2(msg2);
-  details = dispatcher->OnEventFromSource(&key_event2);
-  EXPECT_FALSE(key_event2.handled() || details.dispatcher_destroyed);
-
-  MSG msg3 = {NULL, WM_KEYUP, ui::VKEY_A, 0};
-  ui::KeyEvent key_event3(msg3);
-  details = dispatcher->OnEventFromSource(&key_event3);
-  EXPECT_FALSE(key_event3.handled() || details.dispatcher_destroyed);
-#elif defined(USE_X11)
   ui::ScopedXI2Event key_event;
   key_event.InitKeyEvent(ui::ET_KEY_PRESSED, ui::VKEY_A, 0);
   ui::KeyEvent key_event1(key_event);
@@ -792,7 +770,6 @@
   ui::KeyEvent key_event3(key_event);
   details = dispatcher->OnEventFromSource(&key_event3);
   EXPECT_FALSE(key_event3.handled() || details.dispatcher_destroyed);
-#endif
   EXPECT_EQ(1, target.accelerator_pressed_count());
 }
 #endif
@@ -808,7 +785,6 @@
   EXPECT_TRUE(ProcessInController(
       ui::Accelerator(ui::VKEY_MEDIA_LAUNCH_APP1, ui::EF_NONE)));
 
-#if defined(OS_CHROMEOS)
   // The "Take Screenshot", "Take Partial Screenshot", volume, brightness, and
   // keyboard brightness accelerators are only defined on ChromeOS.
   {
@@ -895,9 +871,7 @@
     EXPECT_EQ(1, delegate->handle_keyboard_brightness_up_count());
     EXPECT_EQ(alt_brightness_up, delegate->last_accelerator());
   }
-#endif
 
-#if !defined(OS_WIN)
   // Exit
   ExitWarningHandler* ewh = GetController()->GetExitWarningHandlerForTest();
   ASSERT_TRUE(ewh);
@@ -912,7 +886,6 @@
   EXPECT_TRUE(is_idle(ewh));
   EXPECT_FALSE(is_ui_shown(ewh));
   Reset(ewh);
-#endif
 
   // New tab
   EXPECT_TRUE(
@@ -934,7 +907,6 @@
   EXPECT_TRUE(
       ProcessInController(ui::Accelerator(ui::VKEY_ESCAPE, ui::EF_SHIFT_DOWN)));
 
-#if defined(OS_CHROMEOS)
   // Open file manager
   EXPECT_TRUE(ProcessInController(
       ui::Accelerator(ui::VKEY_M, ui::EF_SHIFT_DOWN | ui::EF_ALT_DOWN)));
@@ -945,24 +917,25 @@
   // effect of locking the screen.
   EXPECT_TRUE(
       ProcessInController(ui::Accelerator(ui::VKEY_L, ui::EF_COMMAND_DOWN)));
-#endif
 }
 
 TEST_F(AcceleratorControllerTest, GlobalAcceleratorsToggleAppList) {
+  app_list::test::TestAppListPresenter test_app_list_presenter;
+  WmShell::Get()->app_list()->SetAppListPresenter(
+      test_app_list_presenter.CreateInterfacePtrAndBind());
   AccessibilityDelegate* delegate = WmShell::Get()->accessibility_delegate();
-  EXPECT_FALSE(WmShell::Get()->GetAppListTargetVisibility());
 
-  // The press event should not open the AppList, the release should instead.
+  // The press event should not toggle the AppList, the release should instead.
   EXPECT_FALSE(
       ProcessInController(ui::Accelerator(ui::VKEY_LWIN, ui::EF_NONE)));
+  RunAllPendingInMessageLoop();
   EXPECT_EQ(ui::VKEY_LWIN, GetCurrentAccelerator().key_code());
-
-  EXPECT_FALSE(WmShell::Get()->GetAppListTargetVisibility());
+  EXPECT_EQ(0u, test_app_list_presenter.toggle_count());
 
   EXPECT_TRUE(
       ProcessInController(ReleaseAccelerator(ui::VKEY_LWIN, ui::EF_NONE)));
-  EXPECT_TRUE(WmShell::Get()->GetAppListTargetVisibility());
-
+  RunAllPendingInMessageLoop();
+  EXPECT_EQ(1u, test_app_list_presenter.toggle_count());
   EXPECT_EQ(ui::VKEY_LWIN, GetPreviousAccelerator().key_code());
 
   // When spoken feedback is on, the AppList should not toggle.
@@ -972,32 +945,26 @@
   EXPECT_FALSE(
       ProcessInController(ReleaseAccelerator(ui::VKEY_LWIN, ui::EF_NONE)));
   delegate->ToggleSpokenFeedback(A11Y_NOTIFICATION_NONE);
-  EXPECT_TRUE(WmShell::Get()->GetAppListTargetVisibility());
+  RunAllPendingInMessageLoop();
+  EXPECT_EQ(1u, test_app_list_presenter.toggle_count());
 
+  // Turning off spoken feedback should allow the AppList to toggle again.
   EXPECT_FALSE(
       ProcessInController(ui::Accelerator(ui::VKEY_LWIN, ui::EF_NONE)));
   EXPECT_TRUE(
       ProcessInController(ReleaseAccelerator(ui::VKEY_LWIN, ui::EF_NONE)));
-  EXPECT_FALSE(WmShell::Get()->GetAppListTargetVisibility());
+  RunAllPendingInMessageLoop();
+  EXPECT_EQ(2u, test_app_list_presenter.toggle_count());
 
-  // When spoken feedback is on, the AppList should not toggle.
-  delegate->ToggleSpokenFeedback(A11Y_NOTIFICATION_NONE);
-  EXPECT_FALSE(
-      ProcessInController(ui::Accelerator(ui::VKEY_LWIN, ui::EF_NONE)));
-  EXPECT_FALSE(
-      ProcessInController(ReleaseAccelerator(ui::VKEY_LWIN, ui::EF_NONE)));
-  delegate->ToggleSpokenFeedback(A11Y_NOTIFICATION_NONE);
-  EXPECT_FALSE(WmShell::Get()->GetAppListTargetVisibility());
-
-#if defined(OS_CHROMEOS)
   // The press of VKEY_BROWSER_SEARCH should toggle the AppList
   EXPECT_TRUE(ProcessInController(
       ui::Accelerator(ui::VKEY_BROWSER_SEARCH, ui::EF_NONE)));
-  EXPECT_TRUE(WmShell::Get()->GetAppListTargetVisibility());
+  RunAllPendingInMessageLoop();
+  EXPECT_EQ(3u, test_app_list_presenter.toggle_count());
   EXPECT_FALSE(ProcessInController(
       ReleaseAccelerator(ui::VKEY_BROWSER_SEARCH, ui::EF_NONE)));
-  EXPECT_TRUE(WmShell::Get()->GetAppListTargetVisibility());
-#endif
+  RunAllPendingInMessageLoop();
+  EXPECT_EQ(3u, test_app_list_presenter.toggle_count());
 }
 
 TEST_F(AcceleratorControllerTest, ImeGlobalAccelerators) {
@@ -1052,13 +1019,12 @@
 }
 
 TEST_F(AcceleratorControllerTest, PreferredReservedAccelerators) {
-#if defined(OS_CHROMEOS)
   // Power key is reserved on chromeos.
   EXPECT_TRUE(GetController()->IsReserved(
       ui::Accelerator(ui::VKEY_POWER, ui::EF_NONE)));
   EXPECT_FALSE(GetController()->IsPreferred(
       ui::Accelerator(ui::VKEY_POWER, ui::EF_NONE)));
-#endif
+
   // ALT+Tab are not reserved but preferred.
   EXPECT_FALSE(GetController()->IsReserved(
       ui::Accelerator(ui::VKEY_TAB, ui::EF_ALT_DOWN)));
@@ -1115,14 +1081,13 @@
   ASSERT_TRUE(w1_state->IsFullscreen());
 
   ui::test::EventGenerator& generator = GetEventGenerator();
-#if defined(OS_CHROMEOS)
+
   // Power key (reserved) should always be handled.
   test::LockStateControllerTestApi test_api(
       Shell::GetInstance()->lock_state_controller());
   EXPECT_FALSE(test_api.is_animating_lock());
   generator.PressKey(ui::VKEY_POWER, ui::EF_NONE);
   EXPECT_TRUE(test_api.is_animating_lock());
-#endif
 
   auto press_and_release_alt_tab = [&generator]() {
     generator.PressKey(ui::VKEY_TAB, ui::EF_ALT_DOWN);
@@ -1165,14 +1130,13 @@
   }
 
   ui::test::EventGenerator& generator = GetEventGenerator();
-#if defined(OS_CHROMEOS)
+
   // Power key (reserved) should always be handled.
   test::LockStateControllerTestApi test_api(
       Shell::GetInstance()->lock_state_controller());
   EXPECT_FALSE(test_api.is_animating_lock());
   generator.PressKey(ui::VKEY_POWER, ui::EF_NONE);
   EXPECT_TRUE(test_api.is_animating_lock());
-#endif
 
   // A pinned window can consume ALT-TAB (preferred), but no side effect.
   ASSERT_EQ(w1, wm::GetActiveWindow());
@@ -1182,7 +1146,6 @@
   ASSERT_NE(w2, wm::GetActiveWindow());
 }
 
-#if defined(OS_CHROMEOS)
 TEST_F(AcceleratorControllerTest, DisallowedAtModalWindow) {
   std::set<AcceleratorAction> all_actions;
   for (size_t i = 0; i < kAcceleratorDataLength; ++i)
@@ -1282,7 +1245,6 @@
     EXPECT_EQ(1, user_action_tester.GetActionCount("Accel_VolumeUp_F10"));
   }
 }
-#endif
 
 TEST_F(AcceleratorControllerTest, DisallowedWithNoWindow) {
   AccessibilityDelegate* delegate = WmShell::Get()->accessibility_delegate();
@@ -1315,7 +1277,6 @@
   }
 }
 
-#if defined(OS_CHROMEOS)
 namespace {
 
 // defines a class to test the behavior of deprecated accelerators.
@@ -1417,6 +1378,5 @@
     ResetStateIfNeeded();
   }
 }
-#endif  // defined(OS_CHROMEOS)
 
 }  // namespace ash
diff --git a/ash/accelerators/accelerator_interactive_uitest_chromeos.cc b/ash/accelerators/accelerator_interactive_uitest_chromeos.cc
index 7663fc47..69755878 100644
--- a/ash/accelerators/accelerator_interactive_uitest_chromeos.cc
+++ b/ash/accelerators/accelerator_interactive_uitest_chromeos.cc
@@ -18,6 +18,9 @@
 #include "base/run_loop.h"
 #include "base/test/user_action_tester.cc"
 #include "chromeos/network/network_handler.h"
+#include "mojo/edk/embedder/embedder.h"
+#include "ui/app_list/presenter/app_list.h"
+#include "ui/app_list/presenter/test/test_app_list_presenter.h"
 #include "ui/base/test/ui_controls.h"
 
 namespace ash {
@@ -195,11 +198,18 @@
 
 // Tests the app list accelerator.
 TEST_F(AcceleratorInteractiveUITest, MAYBE_ToggleAppList) {
-  EXPECT_FALSE(WmShell::Get()->GetAppListTargetVisibility());
+  mojo::edk::Init();
+  app_list::test::TestAppListPresenter test_app_list_presenter;
+  WmShell::Get()->app_list()->SetAppListPresenter(
+      test_app_list_presenter.CreateInterfacePtrAndBind());
+
+  EXPECT_EQ(0u, test_app_list_presenter.toggle_count());
   SendKeyPressSync(ui::VKEY_LWIN, false, false, false);
-  EXPECT_TRUE(WmShell::Get()->GetAppListTargetVisibility());
+  RunAllPendingInMessageLoop();
+  EXPECT_EQ(1u, test_app_list_presenter.toggle_count());
   SendKeyPressSync(ui::VKEY_LWIN, false, false, false);
-  EXPECT_FALSE(WmShell::Get()->GetAppListTargetVisibility());
+  RunAllPendingInMessageLoop();
+  EXPECT_EQ(2u, test_app_list_presenter.toggle_count());
 }
 
 }  // namespace test
diff --git a/ash/app_list/app_list_presenter_delegate.cc b/ash/app_list/app_list_presenter_delegate.cc
index a9d89e1..14e9b3d5 100644
--- a/ash/app_list/app_list_presenter_delegate.cc
+++ b/ash/app_list/app_list_presenter_delegate.cc
@@ -24,7 +24,7 @@
 #include "base/command_line.h"
 #include "ui/app_list/app_list_constants.h"
 #include "ui/app_list/app_list_switches.h"
-#include "ui/app_list/presenter/app_list_presenter.h"
+#include "ui/app_list/presenter/app_list_presenter_impl.h"
 #include "ui/app_list/presenter/app_list_view_delegate_factory.h"
 #include "ui/app_list/views/app_list_view.h"
 #include "ui/aura/window.h"
@@ -75,7 +75,7 @@
 // AppListPresenterDelegate, public:
 
 AppListPresenterDelegate::AppListPresenterDelegate(
-    app_list::AppListPresenter* presenter,
+    app_list::AppListPresenterImpl* presenter,
     app_list::AppListViewDelegateFactory* view_delegate_factory)
     : presenter_(presenter), view_delegate_factory_(view_delegate_factory) {
   WmShell::Get()->AddShellObserver(this);
diff --git a/ash/app_list/app_list_presenter_delegate.h b/ash/app_list/app_list_presenter_delegate.h
index be6c897f..44130d5 100644
--- a/ash/app_list/app_list_presenter_delegate.h
+++ b/ash/app_list/app_list_presenter_delegate.h
@@ -16,7 +16,7 @@
 #include "ui/keyboard/keyboard_controller_observer.h"
 
 namespace app_list {
-class AppListPresenter;
+class AppListPresenterImpl;
 class AppListView;
 class AppListViewDelegateFactory;
 }
@@ -40,7 +40,7 @@
       public WmShelfObserver {
  public:
   AppListPresenterDelegate(
-      app_list::AppListPresenter* presenter,
+      app_list::AppListPresenterImpl* presenter,
       app_list::AppListViewDelegateFactory* view_delegate_factory);
   ~AppListPresenterDelegate() override;
 
@@ -78,7 +78,7 @@
   bool is_visible_ = false;
 
   // Not owned. Pointer is guaranteed to be valid while this object is alive.
-  app_list::AppListPresenter* presenter_;
+  app_list::AppListPresenterImpl* presenter_;
 
   // Not owned. Pointer is guaranteed to be valid while this object is alive.
   app_list::AppListViewDelegateFactory* view_delegate_factory_;
diff --git a/ash/app_list/app_list_presenter_delegate_factory.cc b/ash/app_list/app_list_presenter_delegate_factory.cc
index 0c312e72..e8275b1 100644
--- a/ash/app_list/app_list_presenter_delegate_factory.cc
+++ b/ash/app_list/app_list_presenter_delegate_factory.cc
@@ -18,7 +18,7 @@
 
 std::unique_ptr<app_list::AppListPresenterDelegate>
 AppListPresenterDelegateFactory::GetDelegate(
-    app_list::AppListPresenter* presenter) {
+    app_list::AppListPresenterImpl* presenter) {
   return base::MakeUnique<AppListPresenterDelegate>(
       presenter, view_delegate_factory_.get());
 }
diff --git a/ash/app_list/app_list_presenter_delegate_factory.h b/ash/app_list/app_list_presenter_delegate_factory.h
index c50f06b..382a1df 100644
--- a/ash/app_list/app_list_presenter_delegate_factory.h
+++ b/ash/app_list/app_list_presenter_delegate_factory.h
@@ -12,7 +12,6 @@
 #include "ui/app_list/presenter/app_list_presenter_delegate_factory.h"
 
 namespace app_list {
-class AppListPresenter;
 class AppListViewDelegateFactory;
 }
 
@@ -26,7 +25,7 @@
   ~AppListPresenterDelegateFactory() override;
 
   std::unique_ptr<app_list::AppListPresenterDelegate> GetDelegate(
-      app_list::AppListPresenter* presenter) override;
+      app_list::AppListPresenterImpl* presenter) override;
 
  private:
   std::unique_ptr<app_list::AppListViewDelegateFactory> view_delegate_factory_;
diff --git a/ash/app_list/app_list_presenter_delegate_unittest.cc b/ash/app_list/app_list_presenter_delegate_unittest.cc
index 106b83b..71324ae 100644
--- a/ash/app_list/app_list_presenter_delegate_unittest.cc
+++ b/ash/app_list/app_list_presenter_delegate_unittest.cc
@@ -9,67 +9,57 @@
 #include "ash/public/cpp/shell_window_ids.h"
 #include "ash/shell.h"
 #include "ash/test/ash_test_base.h"
-#include "ash/test/ash_test_helper.h"
-#include "ash/test/test_shell_delegate.h"
+#include "ash/test/test_app_list_view_presenter_impl.h"
 #include "ash/wm/window_util.h"
-#include "base/command_line.h"
-#include "ui/app_list/app_list_switches.h"
-#include "ui/app_list/presenter/app_list_presenter_impl.h"
+#include "base/macros.h"
 #include "ui/app_list/views/app_list_view.h"
 #include "ui/aura/test/test_windows.h"
 #include "ui/aura/window.h"
+#include "ui/display/manager/display_manager.h"
 #include "ui/events/test/event_generator.h"
 
 namespace ash {
 
-namespace {
-const int kMinimalAppListMargin = 10;
-}
-
 class AppListPresenterDelegateTest : public test::AshTestBase {
  public:
-  AppListPresenterDelegateTest();
-  ~AppListPresenterDelegateTest() override;
+  AppListPresenterDelegateTest() {}
+  ~AppListPresenterDelegateTest() override {}
+
+  app_list::AppListPresenterImpl* app_list_presenter_impl() {
+    return &app_list_presenter_impl_;
+  }
 
   // testing::Test:
-  void SetUp() override;
+  void SetUp() override {
+    AshTestBase::SetUp();
 
-  app_list::AppListPresenterImpl* GetAppListPresenter();
+    // Make the display big enough to hold the app list.
+    UpdateDisplay("1024x768");
+  }
+
+ private:
+  test::TestAppListViewPresenterImpl app_list_presenter_impl_;
+
+  DISALLOW_COPY_AND_ASSIGN(AppListPresenterDelegateTest);
 };
 
-AppListPresenterDelegateTest::AppListPresenterDelegateTest() {}
-
-AppListPresenterDelegateTest::~AppListPresenterDelegateTest() {}
-
-void AppListPresenterDelegateTest::SetUp() {
-  AshTestBase::SetUp();
-
-  // Make the display big enough to hold the app list.
-  UpdateDisplay("1024x768");
-}
-
-app_list::AppListPresenterImpl*
-AppListPresenterDelegateTest::GetAppListPresenter() {
-  return ash_test_helper()->test_shell_delegate()->app_list_presenter();
-}
-
 // Tests that app launcher hides when focus moves to a normal window.
 TEST_F(AppListPresenterDelegateTest, HideOnFocusOut) {
-  WmShell::Get()->ShowAppList();
-  EXPECT_TRUE(WmShell::Get()->GetAppListTargetVisibility());
+  app_list_presenter_impl()->Show(display_manager()->first_display_id());
+  EXPECT_TRUE(app_list_presenter_impl()->GetTargetVisibility());
 
   std::unique_ptr<aura::Window> window(CreateTestWindowInShellWithId(0));
   wm::ActivateWindow(window.get());
 
-  EXPECT_FALSE(WmShell::Get()->GetAppListTargetVisibility());
+  EXPECT_FALSE(app_list_presenter_impl()->GetTargetVisibility());
 }
 
 // Tests that app launcher remains visible when focus is moved to a different
 // window in kShellWindowId_AppListContainer.
 TEST_F(AppListPresenterDelegateTest,
        RemainVisibleWhenFocusingToApplistContainer) {
-  WmShell::Get()->ShowAppList();
-  EXPECT_TRUE(WmShell::Get()->GetAppListTargetVisibility());
+  app_list_presenter_impl()->Show(display_manager()->first_display_id());
+  EXPECT_TRUE(app_list_presenter_impl()->GetTargetVisibility());
 
   aura::Window* applist_container = Shell::GetContainer(
       Shell::GetPrimaryRootWindow(), kShellWindowId_AppListContainer);
@@ -77,19 +67,19 @@
       aura::test::CreateTestWindowWithId(0, applist_container));
   wm::ActivateWindow(window.get());
 
-  EXPECT_TRUE(WmShell::Get()->GetAppListTargetVisibility());
+  EXPECT_TRUE(app_list_presenter_impl()->GetTargetVisibility());
 }
 
 // Tests that clicking outside the app-list bubble closes it.
 TEST_F(AppListPresenterDelegateTest, ClickOutsideBubbleClosesBubble) {
-  WmShell::Get()->ShowAppList();
-  aura::Window* app_window = GetAppListPresenter()->GetWindow();
+  app_list_presenter_impl()->Show(display_manager()->first_display_id());
+  aura::Window* app_window = app_list_presenter_impl()->GetWindow();
   ASSERT_TRUE(app_window);
   ui::test::EventGenerator& generator = GetEventGenerator();
   // Click on the bubble itself. The bubble should remain visible.
   generator.MoveMouseToCenterOf(app_window);
   generator.ClickLeftButton();
-  EXPECT_TRUE(WmShell::Get()->GetAppListTargetVisibility());
+  EXPECT_TRUE(app_list_presenter_impl()->GetTargetVisibility());
 
   // Click outside the bubble. This should close it.
   gfx::Rect app_window_bounds = app_window->GetBoundsInRootWindow();
@@ -98,28 +88,28 @@
       gfx::Vector2d(10, 0);
   generator.MoveMouseToInHost(point_outside);
   generator.ClickLeftButton();
-  EXPECT_FALSE(WmShell::Get()->GetAppListTargetVisibility());
+  EXPECT_FALSE(app_list_presenter_impl()->GetTargetVisibility());
 }
 
 // Tests that clicking outside the app-list bubble closes it.
 TEST_F(AppListPresenterDelegateTest, TapOutsideBubbleClosesBubble) {
-  WmShell::Get()->ShowAppList();
+  app_list_presenter_impl()->Show(display_manager()->first_display_id());
 
-  aura::Window* app_window = GetAppListPresenter()->GetWindow();
+  aura::Window* app_window = app_list_presenter_impl()->GetWindow();
   ASSERT_TRUE(app_window);
   gfx::Rect app_window_bounds = app_window->GetBoundsInRootWindow();
 
   ui::test::EventGenerator& generator = GetEventGenerator();
   // Click on the bubble itself. The bubble should remain visible.
   generator.GestureTapAt(app_window_bounds.CenterPoint());
-  EXPECT_TRUE(WmShell::Get()->GetAppListTargetVisibility());
+  EXPECT_TRUE(app_list_presenter_impl()->GetTargetVisibility());
 
   // Click outside the bubble. This should close it.
   gfx::Point point_outside =
       gfx::Point(app_window_bounds.right(), app_window_bounds.y()) +
       gfx::Vector2d(10, 0);
   generator.GestureTapAt(point_outside);
-  EXPECT_FALSE(WmShell::Get()->GetAppListTargetVisibility());
+  EXPECT_FALSE(app_list_presenter_impl()->GetTargetVisibility());
 }
 
 // Tests opening the app launcher on a non-primary display, then deleting the
@@ -136,15 +126,15 @@
   WmWindow* secondary_root = root_windows[1];
   EXPECT_EQ("1024,0 1024x768", secondary_root->GetBoundsInScreen().ToString());
 
-  WmShell::Get()->delegate()->GetAppListPresenter()->Show(
+  app_list_presenter_impl()->Show(
       secondary_root->GetDisplayNearestWindow().id());
-  EXPECT_TRUE(WmShell::Get()->GetAppListTargetVisibility());
+  EXPECT_TRUE(app_list_presenter_impl()->GetTargetVisibility());
 
   // Remove the secondary display. Shouldn't crash (http://crbug.com/368990).
   UpdateDisplay("1024x768");
 
   // Updating the displays should close the app list.
-  EXPECT_FALSE(WmShell::Get()->GetAppListTargetVisibility());
+  EXPECT_FALSE(app_list_presenter_impl()->GetTargetVisibility());
 }
 
 // Tests opening the app launcher on a tiny display that is too small to contain
@@ -153,17 +143,18 @@
   // Set up a screen with a tiny display (height smaller than the app list).
   UpdateDisplay("400x300");
 
-  WmShell::Get()->ShowAppList();
-  EXPECT_TRUE(WmShell::Get()->GetAppListTargetVisibility());
+  app_list_presenter_impl()->Show(display_manager()->first_display_id());
+  EXPECT_TRUE(app_list_presenter_impl()->GetTargetVisibility());
 
   // The top of the app list should be on-screen (even if the bottom is not).
   // We need to manually calculate the Y coordinate of the top of the app list
   // from the anchor (center) and height. There isn't a bounds rect that gives
   // the actual app list position (the widget bounds include the bubble border
   // which is much bigger than the actual app list size).
-  app_list::AppListView* app_list = GetAppListPresenter()->GetView();
+  app_list::AppListView* app_list = app_list_presenter_impl()->GetView();
   int app_list_view_top =
       app_list->anchor_rect().y() - app_list->bounds().height() / 2;
+  const int kMinimalAppListMargin = 10;
   EXPECT_GE(app_list_view_top, kMinimalAppListMargin);
 }
 
diff --git a/ash/common/shell_delegate.h b/ash/common/shell_delegate.h
index c007086..f584cf7 100644
--- a/ash/common/shell_delegate.h
+++ b/ash/common/shell_delegate.h
@@ -14,10 +14,6 @@
 
 class GURL;
 
-namespace app_list {
-class AppListPresenter;
-}
-
 namespace gfx {
 class Image;
 }
@@ -93,11 +89,7 @@
   // Opens the |url| in a new browser tab.
   virtual void OpenUrlFromArc(const GURL& url) = 0;
 
-  // Get the AppListPresenter. Ownership stays with Chrome.
-  virtual app_list::AppListPresenter* GetAppListPresenter() = 0;
-
-  // Creates a new ShelfDelegate. Shell takes ownership of the returned
-  // value.
+  // Creates a new ShelfDelegate. Shell takes ownership of the returned value.
   virtual ShelfDelegate* CreateShelfDelegate(ShelfModel* model) = 0;
 
   // Creates a system-tray delegate. Shell takes ownership of the delegate.
diff --git a/ash/common/wm_shell.cc b/ash/common/wm_shell.cc
index 5e80f334..5a8c34b 100644
--- a/ash/common/wm_shell.cc
+++ b/ash/common/wm_shell.cc
@@ -52,7 +52,6 @@
 #include "services/preferences/public/interfaces/preferences.mojom.h"
 #include "services/service_manager/public/cpp/connector.h"
 #include "ui/app_list/presenter/app_list.h"
-#include "ui/app_list/presenter/app_list_presenter.h"
 #include "ui/display/display.h"
 #include "ui/views/focus/focus_manager_factory.h"
 
@@ -344,28 +343,25 @@
 
 void WmShell::ShowAppList() {
   // Show the app list on the default display for new windows.
-  int64_t display_id =
-      GetRootWindowForNewWindows()->GetDisplayNearestWindow().id();
-  delegate_->GetAppListPresenter()->Show(display_id);
+  app_list_->Show(GetRootWindowForNewWindows()->GetDisplayNearestWindow().id());
 }
 
 void WmShell::DismissAppList() {
-  delegate_->GetAppListPresenter()->Dismiss();
+  app_list_->Dismiss();
 }
 
 void WmShell::ToggleAppList() {
-  // Show the app list on the default display for new windows.
-  int64_t display_id =
-      GetRootWindowForNewWindows()->GetDisplayNearestWindow().id();
-  delegate_->GetAppListPresenter()->ToggleAppList(display_id);
+  // Toggle the app list on the default display for new windows.
+  app_list_->ToggleAppList(
+      GetRootWindowForNewWindows()->GetDisplayNearestWindow().id());
 }
 
 bool WmShell::IsApplistVisible() const {
-  return delegate_->GetAppListPresenter()->IsVisible();
+  return app_list_->IsVisible();
 }
 
 bool WmShell::GetAppListTargetVisibility() const {
-  return delegate_->GetAppListPresenter()->GetTargetVisibility();
+  return app_list_->GetTargetVisibility();
 }
 
 void WmShell::SetKeyboardUI(std::unique_ptr<KeyboardUI> keyboard_ui) {
diff --git a/ash/mus/BUILD.gn b/ash/mus/BUILD.gn
index 9b3847c..893c2ae 100644
--- a/ash/mus/BUILD.gn
+++ b/ash/mus/BUILD.gn
@@ -20,8 +20,6 @@
     "accelerators/accelerator_ids.h",
     "accessibility_delegate_mus.cc",
     "accessibility_delegate_mus.h",
-    "app_list_presenter_mus.cc",
-    "app_list_presenter_mus.h",
     "bridge/immersive_handler_factory_mus.cc",
     "bridge/immersive_handler_factory_mus.h",
     "bridge/wm_lookup_mus.cc",
diff --git a/ash/mus/app_list_presenter_mus.cc b/ash/mus/app_list_presenter_mus.cc
deleted file mode 100644
index 86d2cac..0000000
--- a/ash/mus/app_list_presenter_mus.cc
+++ /dev/null
@@ -1,49 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "ash/mus/app_list_presenter_mus.h"
-
-#include "ash/common/wm_shell.h"
-#include "ui/app_list/presenter/app_list.h"
-
-namespace ash {
-
-AppListPresenterMus::AppListPresenterMus() {}
-
-AppListPresenterMus::~AppListPresenterMus() {}
-
-void AppListPresenterMus::Show(int64_t display_id) {
-  app_list::mojom::AppListPresenter* app_list_presenter =
-      WmShell::Get()->app_list()->GetAppListPresenter();
-  if (app_list_presenter)
-    app_list_presenter->Show(display_id);
-}
-
-void AppListPresenterMus::Dismiss() {
-  app_list::mojom::AppListPresenter* app_list_presenter =
-      WmShell::Get()->app_list()->GetAppListPresenter();
-  if (app_list_presenter)
-    app_list_presenter->Dismiss();
-}
-
-void AppListPresenterMus::ToggleAppList(int64_t display_id) {
-  app_list::mojom::AppListPresenter* app_list_presenter =
-      WmShell::Get()->app_list()->GetAppListPresenter();
-  if (app_list_presenter)
-    app_list_presenter->ToggleAppList(display_id);
-}
-
-bool AppListPresenterMus::IsVisible() const {
-  return false;
-}
-
-bool AppListPresenterMus::GetTargetVisibility() const {
-  // TODO(mfomitchev): we have GetTargetVisibility() in mojom, but this
-  // shouldn't be a synchronous method. We should go through the call sites and
-  // either teach them to use a callback, or perhaps use a visibility observer.
-  //  NOTIMPLEMENTED();
-  return false;
-}
-
-}  // namespace ash
diff --git a/ash/mus/app_list_presenter_mus.h b/ash/mus/app_list_presenter_mus.h
deleted file mode 100644
index 5127557..0000000
--- a/ash/mus/app_list_presenter_mus.h
+++ /dev/null
@@ -1,33 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef ASH_MUS_APP_LIST_PRESENTER_MUS_H_
-#define ASH_MUS_APP_LIST_PRESENTER_MUS_H_
-
-#include "base/macros.h"
-#include "ui/app_list/presenter/app_list_presenter.h"
-
-namespace ash {
-
-// Mus+ash implementation of the AppListPresenter interface for mash, which
-// talks to the app list presenter service in chrome.
-class AppListPresenterMus : public app_list::AppListPresenter {
- public:
-  AppListPresenterMus();
-  ~AppListPresenterMus() override;
-
-  // app_list::AppListPresenter:
-  void Show(int64_t display_id) override;
-  void Dismiss() override;
-  void ToggleAppList(int64_t display_id) override;
-  bool IsVisible() const override;
-  bool GetTargetVisibility() const override;
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(AppListPresenterMus);
-};
-
-}  // namespace ash
-
-#endif  // ASH_MUS_APP_LIST_PRESENTER_MUS_H_
diff --git a/ash/mus/shell_delegate_mus.cc b/ash/mus/shell_delegate_mus.cc
index 37ec044..0d0ad713 100644
--- a/ash/mus/shell_delegate_mus.cc
+++ b/ash/mus/shell_delegate_mus.cc
@@ -146,10 +146,6 @@
   NOTIMPLEMENTED();
 }
 
-app_list::AppListPresenter* ShellDelegateMus::GetAppListPresenter() {
-  return &app_list_presenter_;
-}
-
 ShelfDelegate* ShellDelegateMus::CreateShelfDelegate(ShelfModel* model) {
   return new ShelfDelegateMus();
 }
diff --git a/ash/mus/shell_delegate_mus.h b/ash/mus/shell_delegate_mus.h
index a876c8ba..fd88232 100644
--- a/ash/mus/shell_delegate_mus.h
+++ b/ash/mus/shell_delegate_mus.h
@@ -8,7 +8,6 @@
 #include <memory>
 
 #include "ash/common/shell_delegate.h"
-#include "ash/mus/app_list_presenter_mus.h"
 #include "base/macros.h"
 
 namespace service_manager {
@@ -34,7 +33,6 @@
   void Exit() override;
   keyboard::KeyboardUI* CreateKeyboardUI() override;
   void OpenUrlFromArc(const GURL& url) override;
-  app_list::AppListPresenter* GetAppListPresenter() override;
   ShelfDelegate* CreateShelfDelegate(ShelfModel* model) override;
   SystemTrayDelegate* CreateSystemTrayDelegate() override;
   std::unique_ptr<WallpaperDelegate> CreateWallpaperDelegate() override;
@@ -53,7 +51,6 @@
 
  private:
   service_manager::Connector* connector_;
-  AppListPresenterMus app_list_presenter_;
 
   DISALLOW_COPY_AND_ASSIGN(ShellDelegateMus);
 };
diff --git a/ash/mus/window_manager.cc b/ash/mus/window_manager.cc
index 6ff5d61..a2630f2 100644
--- a/ash/mus/window_manager.cc
+++ b/ash/mus/window_manager.cc
@@ -14,7 +14,6 @@
 #include "ash/display/screen_position_controller.h"
 #include "ash/mus/accelerators/accelerator_handler.h"
 #include "ash/mus/accelerators/accelerator_ids.h"
-#include "ash/mus/app_list_presenter_mus.h"
 #include "ash/mus/bridge/wm_lookup_mus.h"
 #include "ash/mus/bridge/wm_root_window_controller_mus.h"
 #include "ash/mus/bridge/wm_shell_mus.h"
diff --git a/ash/shelf/shelf_layout_manager_unittest.cc b/ash/shelf/shelf_layout_manager_unittest.cc
index cd63f30..e7a3205 100644
--- a/ash/shelf/shelf_layout_manager_unittest.cc
+++ b/ash/shelf/shelf_layout_manager_unittest.cc
@@ -25,6 +25,7 @@
 #include "ash/root_window_controller.h"
 #include "ash/shell.h"
 #include "ash/test/ash_test_base.h"
+#include "ash/test/test_app_list_view_presenter_impl.h"
 #include "ash/test/test_system_tray_item.h"
 #include "ash/wm/window_state_aura.h"
 #include "ash/wm/window_util.h"
@@ -947,62 +948,63 @@
   EXPECT_EQ(SHELF_AUTO_HIDE_SHOWN, shelf->GetAutoHideState());
 }
 
-// Makes sure shelf will be visible when app list opens as shelf is in
-// SHELF_VISIBLE state,and toggling app list won't change shelf
-// visibility state.
+// Ensure a SHELF_VISIBLE shelf stays visible when the app list is shown.
 TEST_F(ShelfLayoutManagerTest, OpenAppListWithShelfVisibleState) {
-  WmShell* shell = WmShell::Get();
   WmShelf* shelf = GetPrimaryShelf();
-  ShelfLayoutManager* layout_manager = GetShelfLayoutManager();
-  layout_manager->LayoutShelf();
   shelf->SetAutoHideBehavior(SHELF_AUTO_HIDE_BEHAVIOR_NEVER);
 
-  // Create a normal unmaximized window, shelf should be visible.
+  // The tested behavior relies on the app list presenter implementation.
+  test::TestAppListViewPresenterImpl app_list_presenter_impl;
+
+  // Create a normal unmaximized window; the shelf should be visible.
   aura::Window* window = CreateTestWindow();
   window->SetBounds(gfx::Rect(0, 0, 100, 100));
   window->Show();
-  EXPECT_FALSE(shell->GetAppListTargetVisibility());
+  EXPECT_FALSE(app_list_presenter_impl.GetTargetVisibility());
   EXPECT_EQ(SHELF_VISIBLE, shelf->GetVisibilityState());
 
-  // Show app list and the shelf stays visible.
-  shell->ShowAppList();
-  EXPECT_TRUE(shell->GetAppListTargetVisibility());
+  // Show the app list and the shelf stays visible.
+  app_list_presenter_impl.Show(display_manager()->first_display_id());
+  EXPECT_TRUE(app_list_presenter_impl.GetTargetVisibility());
   EXPECT_EQ(SHELF_VISIBLE, shelf->GetVisibilityState());
 
-  // Hide app list and the shelf stays visible.
-  shell->DismissAppList();
-  EXPECT_FALSE(shell->GetAppListTargetVisibility());
+  // Hide the app list and the shelf stays visible.
+  app_list_presenter_impl.Dismiss();
+  EXPECT_FALSE(app_list_presenter_impl.GetTargetVisibility());
   EXPECT_EQ(SHELF_VISIBLE, shelf->GetVisibilityState());
 }
 
-// Makes sure shelf will be shown with SHELF_AUTO_HIDE_SHOWN state
-// when app list opens as shelf is in SHELF_AUTO_HIDE state, and
-// toggling app list won't change shelf visibility state.
+// Ensure a SHELF_AUTO_HIDE shelf is shown temporarily (SHELF_AUTO_HIDE_SHOWN)
+// when the app list is shown, but the visibility state doesn't change.
 TEST_F(ShelfLayoutManagerTest, OpenAppListWithShelfAutoHideState) {
-  WmShell* shell = WmShell::Get();
   WmShelf* shelf = GetPrimaryShelf();
-  ShelfLayoutManager* layout_manager = GetShelfLayoutManager();
-  layout_manager->LayoutShelf();
   shelf->SetAutoHideBehavior(SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS);
 
-  EXPECT_FALSE(shell->GetAppListTargetVisibility());
-  EXPECT_EQ(SHELF_AUTO_HIDE, shelf->GetVisibilityState());
-  EXPECT_EQ(SHELF_AUTO_HIDE_SHOWN, shelf->GetAutoHideState());
+  // The tested behavior relies on the app list presenter implementation.
+  test::TestAppListViewPresenterImpl app_list_presenter_impl;
 
-  // Show app list.
-  shell->ShowAppList();
+  // Create a normal unmaximized window; the shelf should be hidden.
+  aura::Window* window = CreateTestWindow();
+  window->SetBounds(gfx::Rect(0, 0, 100, 100));
+  window->Show();
+  EXPECT_FALSE(app_list_presenter_impl.GetTargetVisibility());
+  EXPECT_EQ(SHELF_AUTO_HIDE, shelf->GetVisibilityState());
+  EXPECT_EQ(SHELF_AUTO_HIDE_HIDDEN, shelf->GetAutoHideState());
+
+  // Show the app list and the shelf should be temporarily visible.
+  app_list_presenter_impl.Show(display_manager()->first_display_id());
   // The shelf's auto hide state won't be changed until the timer fires, so
   // force it to update now.
-  layout_manager->UpdateVisibilityState();
-  EXPECT_TRUE(shell->GetAppListTargetVisibility());
+  GetShelfLayoutManager()->UpdateVisibilityState();
+  EXPECT_TRUE(app_list_presenter_impl.GetTargetVisibility());
   EXPECT_EQ(SHELF_AUTO_HIDE, shelf->GetVisibilityState());
   EXPECT_EQ(SHELF_AUTO_HIDE_SHOWN, shelf->GetAutoHideState());
 
-  // Hide app list.
-  shell->DismissAppList();
-  EXPECT_FALSE(shell->GetAppListTargetVisibility());
+  // Hide the app list and the shelf should be hidden again.
+  app_list_presenter_impl.Dismiss();
+  EXPECT_FALSE(app_list_presenter_impl.GetTargetVisibility());
   EXPECT_EQ(SHELF_AUTO_HIDE, shelf->GetVisibilityState());
-  EXPECT_EQ(SHELF_AUTO_HIDE_SHOWN, shelf->GetAutoHideState());
+  EXPECT_EQ(SHELF_AUTO_HIDE_HIDDEN, shelf->GetAutoHideState());
 }
 
 // Makes sure that when we have dual displays, with one or both shelves are set
@@ -1043,65 +1045,63 @@
   EXPECT_EQ(shelf_2->GetWindow()->GetRootWindow(),
             WmWindowAura::Get(window_2)->GetRootWindow());
 
-  // Activate one window in one display and manually trigger the update of shelf
-  // visibility.
+  // Activate one window in one display.
   wm::ActivateWindow(window_1);
-  Shell::GetInstance()->UpdateShelfVisibility();
 
-  EXPECT_FALSE(WmShell::Get()->GetAppListTargetVisibility());
+  // The tested behavior relies on the app list presenter implementation.
+  test::TestAppListViewPresenterImpl app_list_presenter_impl;
+
+  Shell::GetInstance()->UpdateShelfVisibility();
+  EXPECT_FALSE(app_list_presenter_impl.GetTargetVisibility());
   EXPECT_EQ(SHELF_AUTO_HIDE, shelf_1->GetVisibilityState());
   EXPECT_EQ(SHELF_AUTO_HIDE, shelf_2->GetVisibilityState());
   EXPECT_EQ(SHELF_AUTO_HIDE_HIDDEN, shelf_1->GetAutoHideState());
   EXPECT_EQ(SHELF_AUTO_HIDE_HIDDEN, shelf_2->GetAutoHideState());
 
-  // Show app list.
-  WmShell::Get()->ShowAppList();
+  // Show the app list; only the shelf on the same display should be shown.
+  app_list_presenter_impl.Show(display_manager()->first_display_id());
   Shell::GetInstance()->UpdateShelfVisibility();
-
-  // Only the shelf in the active display should be shown, the other is hidden.
-  EXPECT_TRUE(WmShell::Get()->GetAppListTargetVisibility());
+  EXPECT_TRUE(app_list_presenter_impl.GetTargetVisibility());
   EXPECT_EQ(SHELF_AUTO_HIDE, shelf_1->GetVisibilityState());
   EXPECT_EQ(SHELF_AUTO_HIDE_SHOWN, shelf_1->GetAutoHideState());
   EXPECT_EQ(SHELF_AUTO_HIDE, shelf_2->GetVisibilityState());
   EXPECT_EQ(SHELF_AUTO_HIDE_HIDDEN, shelf_2->GetAutoHideState());
 
-  // Hide app list, both shelves should be hidden.
-  WmShell::Get()->DismissAppList();
-  EXPECT_FALSE(WmShell::Get()->GetAppListTargetVisibility());
+  // Hide the app list, both shelves should be hidden.
+  app_list_presenter_impl.Dismiss();
+  Shell::GetInstance()->UpdateShelfVisibility();
+  EXPECT_FALSE(app_list_presenter_impl.GetTargetVisibility());
   EXPECT_EQ(SHELF_AUTO_HIDE, shelf_1->GetVisibilityState());
   EXPECT_EQ(SHELF_AUTO_HIDE, shelf_2->GetVisibilityState());
   EXPECT_EQ(SHELF_AUTO_HIDE_HIDDEN, shelf_1->GetAutoHideState());
   EXPECT_EQ(SHELF_AUTO_HIDE_HIDDEN, shelf_2->GetAutoHideState());
 }
 
-// Makes sure the shelf will be hidden when we have a fullscreen window, and it
-// will unhide when we open the app list.
+// Ensure a SHELF_HIDDEN shelf (for a fullscreen window) is shown temporarily
+// when the app list is shown, and hidden again when the app list is dismissed.
 TEST_F(ShelfLayoutManagerTest, OpenAppListWithShelfHiddenState) {
-  WmShell* shell = WmShell::Get();
   WmShelf* shelf = GetPrimaryShelf();
-  ShelfLayoutManager* layout_manager = GetShelfLayoutManager();
-  // For shelf to be visible, app list is not open in initial state.
-  layout_manager->LayoutShelf();
 
-  // Create a window and make it full screen.
+  // The tested behavior relies on the app list presenter implementation.
+  test::TestAppListViewPresenterImpl app_list_presenter_impl;
+
+  // Create a window and make it full screen; the shelf should be hidden.
   aura::Window* window = CreateTestWindow();
   window->SetBounds(gfx::Rect(0, 0, 100, 100));
   window->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_FULLSCREEN);
   window->Show();
   wm::ActivateWindow(window);
-
-  // App list and shelf is not shown.
-  EXPECT_FALSE(shell->GetAppListTargetVisibility());
+  EXPECT_FALSE(app_list_presenter_impl.GetTargetVisibility());
   EXPECT_EQ(SHELF_HIDDEN, shelf->GetVisibilityState());
 
-  // Show app list.
-  shell->ShowAppList();
-  EXPECT_TRUE(shell->GetAppListTargetVisibility());
+  // Show the app list and the shelf should be temporarily visible.
+  app_list_presenter_impl.Show(display_manager()->first_display_id());
+  EXPECT_TRUE(app_list_presenter_impl.GetTargetVisibility());
   EXPECT_EQ(SHELF_VISIBLE, shelf->GetVisibilityState());
 
-  // Hide app list.
-  shell->DismissAppList();
-  EXPECT_FALSE(shell->GetAppListTargetVisibility());
+  // Hide the app list and the shelf should be hidden again.
+  app_list_presenter_impl.Dismiss();
+  EXPECT_FALSE(app_list_presenter_impl.GetTargetVisibility());
   EXPECT_EQ(SHELF_HIDDEN, shelf->GetVisibilityState());
 }
 
diff --git a/ash/shelf/shelf_view_unittest.cc b/ash/shelf/shelf_view_unittest.cc
index d5be149..d0f85d62 100644
--- a/ash/shelf/shelf_view_unittest.cc
+++ b/ash/shelf/shelf_view_unittest.cc
@@ -48,7 +48,7 @@
 #include "base/test/user_action_tester.h"
 #include "base/time/time.h"
 #include "testing/gmock/include/gmock/gmock.h"
-#include "ui/app_list/presenter/app_list_presenter.h"
+#include "ui/app_list/presenter/app_list.h"
 #include "ui/aura/test/aura_test_base.h"
 #include "ui/aura/window.h"
 #include "ui/aura/window_event_dispatcher.h"
@@ -1470,8 +1470,11 @@
 }
 
 TEST_F(ShelfViewTest, ShouldHideTooltipWithAppListWindowTest) {
-  WmShell::Get()->ShowAppList();
-  ASSERT_TRUE(WmShell::Get()->GetAppListTargetVisibility());
+  // Trigger mock notifications that the app list was shown.
+  WmShell::Get()->app_list()->OnTargetVisibilityChanged(true);
+  WmShell::Get()->app_list()->OnVisibilityChanged(true);
+  AppListButton* app_list_button = shelf_view_->GetAppListButton();
+  app_list_button->OnAppListShown();
 
   // The tooltip shouldn't hide if the mouse is on normal buttons.
   for (int i = 1; i < test_api_->GetButtonCount(); i++) {
@@ -1484,8 +1487,7 @@
         << "ShelfView tries to hide on button " << i;
   }
 
-  // The tooltip should hide on the app-list button.
-  AppListButton* app_list_button = shelf_view_->GetAppListButton();
+  // The tooltip should hide on the app list button if the app list is visible.
   EXPECT_TRUE(shelf_view_->ShouldHideTooltip(
       app_list_button->GetMirroredBounds().CenterPoint()));
 }
@@ -2052,61 +2054,6 @@
   DISALLOW_COPY_AND_ASSIGN(ListMenuShelfItemDelegate);
 };
 
-// A test implementation for AppListPresenter that does not change visibility
-// state immediately to simulate an in-flight animation. Calling
-// FinishVisibilityChange() will change the visibility to the requested one,
-// simulating end of the animation. Similar to the actual AppListPresenter, this
-// class toggles app list visibility based on the actual visibility rather than
-// the target visibility (which might be different due to in-flight animation).
-class TestAppListPresenter : public app_list::AppListPresenter {
- public:
-  TestAppListPresenter() {}
-  ~TestAppListPresenter() override {}
-
-  void FinishVisibilityChange() { is_visible_ = target_visibility_; }
-
-  // app_list::AppListPresenter:
-  void Show(int64_t display_id) override { target_visibility_ = true; }
-  void Dismiss() override { target_visibility_ = false; }
-  void ToggleAppList(int64_t display_id) override {
-    if (is_visible_)
-      Dismiss();
-    else
-      Show(display_id);
-  }
-  bool IsVisible() const override { return is_visible_; }
-  bool GetTargetVisibility() const override { return target_visibility_; }
-
- private:
-  bool is_visible_ = false;
-  bool target_visibility_ = false;
-
-  DISALLOW_COPY_AND_ASSIGN(TestAppListPresenter);
-};
-
-// A test ShellDelegate implementation that returns a TestAppListPresenter as
-// the app list presenter.
-class TestAppListShellDelegate : public TestShellDelegate {
- public:
-  TestAppListShellDelegate()
-      : app_list_presenter_(new TestAppListPresenter()) {}
-  ~TestAppListShellDelegate() override {}
-
-  TestAppListPresenter* app_list_presenter() const {
-    return app_list_presenter_.get();
-  }
-
-  // TestShellDelegate:
-  app_list::AppListPresenter* GetAppListPresenter() override {
-    return app_list_presenter();
-  }
-
- private:
-  std::unique_ptr<TestAppListPresenter> app_list_presenter_;
-
-  DISALLOW_COPY_AND_ASSIGN(TestAppListShellDelegate);
-};
-
 }  // namespace
 
 // Test fixture that forces material design mode in order to test ink drop
@@ -2129,8 +2076,7 @@
   // before calling base class's SetUp(). Shell will take ownership of the
   // returned object.
   virtual TestShellDelegate* CreateTestShellDelegate() {
-    shell_delegate_ = new TestAppListShellDelegate;
-    return shell_delegate_;
+    return new TestShellDelegate;
   }
 
   void InitAppListButtonInkDrop() {
@@ -2156,28 +2102,23 @@
   }
 
   void ShowAppList() {
-    DCHECK(shelf_delegate_);
-    shell_delegate_->app_list_presenter()->Show(0);
-    // Similar to real AppListPresenter, notify button that the app list is
-    // shown.
+    // Trigger a mock notification that the app list was shown.
+    WmShell::Get()->app_list()->OnTargetVisibilityChanged(true);
     app_list_button_->OnAppListShown();
   }
 
   void DismissAppList() {
-    DCHECK(shelf_delegate_);
-    shell_delegate_->app_list_presenter()->Dismiss();
-    // Similar to real AppListPresenter, notify button that the app list is
-    // dismissed.
+    // Trigger a mock notification that the app list was dismissed.
+    WmShell::Get()->app_list()->OnTargetVisibilityChanged(false);
     app_list_button_->OnAppListDismissed();
   }
 
   void FinishAppListVisibilityChange() {
-    DCHECK(shelf_delegate_);
-    shell_delegate_->app_list_presenter()->FinishVisibilityChange();
+    // Trigger a mock notification that the app list finished animating.
+    app_list::AppList* app_list = WmShell::Get()->app_list();
+    app_list->OnVisibilityChanged(app_list->GetTargetVisibility());
   }
 
-  TestAppListShellDelegate* shell_delegate_ = nullptr;  // Owned by Shell.
-
   AppListButton* app_list_button_ = nullptr;
   InkDropSpy* app_list_button_ink_drop_ = nullptr;
   ShelfButton* browser_button_ = nullptr;
@@ -2219,7 +2160,7 @@
   // Mouse press on the button, which shows the app list, should end up in the
   // activated state.
   generator.PressLeftButton();
-  // Similar to real AppListPresenter, notify button that the app list is shown.
+  // Trigger a mock button notification that the app list was shown.
   app_list_button_->OnAppListShown();
   FinishAppListVisibilityChange();
   EXPECT_EQ(views::InkDropState::ACTIVATED,
@@ -2245,7 +2186,6 @@
 TEST_F(ShelfViewInkDropTest, AppListButtonMouseEventsWhenVisible) {
   InitAppListButtonInkDrop();
 
-  // Show the app list.
   ShowAppList();
   FinishAppListVisibilityChange();
   EXPECT_EQ(views::InkDropState::ACTIVATED,
@@ -2258,7 +2198,6 @@
 
   // Mouse press on the button, which dismisses the app list, should end up in
   // the hidden state.
-  // Dismiss app list similar to pre-target handler in real AppListPresenter.
   DismissAppList();
   generator.PressLeftButton();
   FinishAppListVisibilityChange();
@@ -2298,7 +2237,7 @@
   // Touch release on the button, which shows the app list, should end up in the
   // activated state.
   generator.ReleaseTouch();
-  // Similar to real AppListPresenter, notify button that the app list is shown.
+  // Trigger a mock button notification that the app list was shown.
   app_list_button_->OnAppListShown();
   FinishAppListVisibilityChange();
   EXPECT_EQ(views::InkDropState::ACTIVATED,
@@ -2312,7 +2251,6 @@
 TEST_F(ShelfViewInkDropTest, AppListButtonGestureTapWhenVisible) {
   InitAppListButtonInkDrop();
 
-  // Show the app list.
   ShowAppList();
   FinishAppListVisibilityChange();
   EXPECT_EQ(views::InkDropState::ACTIVATED,
@@ -2325,7 +2263,6 @@
 
   // Touch press on the button, which dismisses the app list, should end up in
   // the hidden state.
-  // Dismiss app list similar to pre-target handler in real AppListPresenter.
   DismissAppList();
   generator.PressTouch();
   EXPECT_EQ(views::InkDropState::HIDDEN,
@@ -2380,7 +2317,6 @@
 TEST_F(ShelfViewInkDropTest, AppListButtonGestureTapDragWhenVisible) {
   InitAppListButtonInkDrop();
 
-  // Show the app list.
   ShowAppList();
   FinishAppListVisibilityChange();
   EXPECT_EQ(views::InkDropState::ACTIVATED,
@@ -2395,7 +2331,6 @@
 
   // Touch press on the button, which dismisses the app list, should end up in
   // the hidden state.
-  // Dismiss app list similar to pre-target handler in real AppListPresenter.
   DismissAppList();
   generator.PressTouch();
   EXPECT_EQ(views::InkDropState::HIDDEN,
diff --git a/ash/shell/content/client/shell_browser_main_parts.cc b/ash/shell/content/client/shell_browser_main_parts.cc
index 7fac63e..bb85840 100644
--- a/ash/shell/content/client/shell_browser_main_parts.cc
+++ b/ash/shell/content/client/shell_browser_main_parts.cc
@@ -10,6 +10,7 @@
 #include "ash/content/shell_content_state.h"
 #include "ash/shell.h"
 #include "ash/shell/content/shell_content_state_impl.h"
+#include "ash/shell/example_app_list_presenter.h"
 #include "ash/shell/shell_delegate_impl.h"
 #include "ash/shell/window_watcher.h"
 #include "ash/shell_init_params.h"
@@ -27,6 +28,7 @@
 #include "content/shell/browser/shell_browser_context.h"
 #include "content/shell/browser/shell_net_log.h"
 #include "net/base/net_module.h"
+#include "ui/app_list/presenter/app_list.h"
 #include "ui/aura/env.h"
 #include "ui/aura/window.h"
 #include "ui/aura/window_tree_host.h"
@@ -146,6 +148,11 @@
 
   ash::shell::InitWindowTypeLauncher();
 
+  // Initialize the example app list presenter.
+  example_app_list_presenter_ = base::MakeUnique<ExampleAppListPresenter>();
+  WmShell::Get()->app_list()->SetAppListPresenter(
+      example_app_list_presenter_->CreateInterfacePtrAndBind());
+
   ash::Shell::GetPrimaryRootWindow()->GetHost()->Show();
 }
 
diff --git a/ash/shell/content/client/shell_browser_main_parts.h b/ash/shell/content/client/shell_browser_main_parts.h
index d633848..a6fe25ee 100644
--- a/ash/shell/content/client/shell_browser_main_parts.h
+++ b/ash/shell/content/client/shell_browser_main_parts.h
@@ -30,6 +30,7 @@
 namespace ash {
 namespace shell {
 
+class ExampleAppListPresenter;
 class ShellDelegateImpl;
 class WindowWatcher;
 
@@ -57,6 +58,7 @@
   std::unique_ptr<ash::shell::WindowWatcher> window_watcher_;
   ShellDelegateImpl* delegate_;  // owned by Shell
   std::unique_ptr<wm::WMState> wm_state_;
+  std::unique_ptr<ExampleAppListPresenter> example_app_list_presenter_;
 
   DISALLOW_COPY_AND_ASSIGN(ShellBrowserMainParts);
 };
diff --git a/ash/shell/example_app_list_presenter.cc b/ash/shell/example_app_list_presenter.cc
new file mode 100644
index 0000000..0ade6d2
--- /dev/null
+++ b/ash/shell/example_app_list_presenter.cc
@@ -0,0 +1,66 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ash/shell/example_app_list_presenter.h"
+
+#include "ash/app_list/app_list_presenter_delegate_factory.h"
+#include "ash/shell/example_factory.h"
+#include "ui/app_list/presenter/app_list_view_delegate_factory.h"
+
+namespace {
+
+// An example app list view delegate factory.
+class ExampleAppListViewDelegateFactory
+    : public app_list::AppListViewDelegateFactory {
+ public:
+  ExampleAppListViewDelegateFactory()
+      : app_list_view_delegate_(ash::shell::CreateAppListViewDelegate()) {}
+  ~ExampleAppListViewDelegateFactory() override {}
+
+  // app_list::AppListViewDelegateFactory:
+  app_list::AppListViewDelegate* GetDelegate() override {
+    return app_list_view_delegate_.get();
+  }
+
+ private:
+  std::unique_ptr<app_list::AppListViewDelegate> app_list_view_delegate_;
+
+  DISALLOW_COPY_AND_ASSIGN(ExampleAppListViewDelegateFactory);
+};
+
+}  // namespace
+
+namespace ash {
+namespace shell {
+
+ExampleAppListPresenter::ExampleAppListPresenter()
+    : binding_(this),
+      app_list_presenter_impl_(
+          base::MakeUnique<AppListPresenterDelegateFactory>(
+              base::MakeUnique<ExampleAppListViewDelegateFactory>())) {
+  // Note: This example |app_list_presenter_impl_| does not report visibility
+  // changes to the app_list::mojom::AppList implementation owned by WmShell.
+}
+
+ExampleAppListPresenter::~ExampleAppListPresenter() {}
+
+app_list::mojom::AppListPresenterPtr
+ExampleAppListPresenter::CreateInterfacePtrAndBind() {
+  return binding_.CreateInterfacePtrAndBind();
+}
+
+void ExampleAppListPresenter::Show(int64_t display_id) {
+  app_list_presenter_impl_.Show(display_id);
+}
+
+void ExampleAppListPresenter::Dismiss() {
+  app_list_presenter_impl_.Dismiss();
+}
+
+void ExampleAppListPresenter::ToggleAppList(int64_t display_id) {
+  app_list_presenter_impl_.ToggleAppList(display_id);
+}
+
+}  // namespace shell
+}  // namespace ash
diff --git a/ash/shell/example_app_list_presenter.h b/ash/shell/example_app_list_presenter.h
new file mode 100644
index 0000000..d6c2bff
--- /dev/null
+++ b/ash/shell/example_app_list_presenter.h
@@ -0,0 +1,40 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef ASH_SHELL_EXAMPLE_APP_LIST_PRESENTER_H_
+#define ASH_SHELL_EXAMPLE_APP_LIST_PRESENTER_H_
+
+#include "base/macros.h"
+#include "mojo/public/cpp/bindings/binding.h"
+#include "ui/app_list/presenter/app_list_presenter.mojom.h"
+#include "ui/app_list/presenter/app_list_presenter_impl.h"
+#include "ui/app_list/presenter/test/test_app_list_view_delegate_factory.h"
+
+namespace ash {
+namespace shell {
+
+// An example implementation of AppListPresenter used for ash_shell.
+class ExampleAppListPresenter : public app_list::mojom::AppListPresenter {
+ public:
+  ExampleAppListPresenter();
+  ~ExampleAppListPresenter() override;
+
+  app_list::mojom::AppListPresenterPtr CreateInterfacePtrAndBind();
+
+  // app_list::mojom::AppListPresenter:
+  void Show(int64_t display_id) override;
+  void Dismiss() override;
+  void ToggleAppList(int64_t display_id) override;
+
+ private:
+  mojo::Binding<app_list::mojom::AppListPresenter> binding_;
+  app_list::AppListPresenterImpl app_list_presenter_impl_;
+
+  DISALLOW_COPY_AND_ASSIGN(ExampleAppListPresenter);
+};
+
+}  // namespace shell
+}  // namespace ash
+
+#endif  // ASH_SHELL_EXAMPLE_APP_LIST_PRESENTER_H_
diff --git a/ash/shell/shell_delegate_impl.cc b/ash/shell/shell_delegate_impl.cc
index 5670f7d4..ccb54f5 100644
--- a/ash/shell/shell_delegate_impl.cc
+++ b/ash/shell/shell_delegate_impl.cc
@@ -4,7 +4,6 @@
 
 #include "ash/shell/shell_delegate_impl.h"
 
-#include "ash/app_list/app_list_presenter_delegate_factory.h"
 #include "ash/common/accessibility_delegate.h"
 #include "ash/common/default_accessibility_delegate.h"
 #include "ash/common/gpu_support_stub.h"
@@ -24,9 +23,6 @@
 #include "base/message_loop/message_loop.h"
 #include "base/strings/utf_string_conversions.h"
 #include "components/user_manager/user_info_impl.h"
-#include "ui/app_list/app_list_view_delegate.h"
-#include "ui/app_list/presenter/app_list_presenter_impl.h"
-#include "ui/app_list/presenter/app_list_view_delegate_factory.h"
 #include "ui/aura/window.h"
 #include "ui/gfx/image/image.h"
 #include "ui/gfx/image/image_skia.h"
@@ -125,31 +121,9 @@
   DISALLOW_COPY_AND_ASSIGN(SessionStateDelegateImpl);
 };
 
-class AppListViewDelegateFactoryImpl
-    : public app_list::AppListViewDelegateFactory {
- public:
-  AppListViewDelegateFactoryImpl() {}
-  ~AppListViewDelegateFactoryImpl() override {}
-
-  // app_list::AppListViewDelegateFactory:
-  app_list::AppListViewDelegate* GetDelegate() override {
-    if (!app_list_view_delegate_.get())
-      app_list_view_delegate_.reset(CreateAppListViewDelegate());
-    return app_list_view_delegate_.get();
-  }
-
- private:
-  std::unique_ptr<app_list::AppListViewDelegate> app_list_view_delegate_;
-
-  DISALLOW_COPY_AND_ASSIGN(AppListViewDelegateFactoryImpl);
-};
-
 }  // namespace
 
-ShellDelegateImpl::ShellDelegateImpl()
-    : shelf_delegate_(nullptr),
-      app_list_presenter_delegate_factory_(new AppListPresenterDelegateFactory(
-          base::WrapUnique(new AppListViewDelegateFactoryImpl))) {}
+ShellDelegateImpl::ShellDelegateImpl() {}
 
 ShellDelegateImpl::~ShellDelegateImpl() {}
 
@@ -191,14 +165,6 @@
 
 void ShellDelegateImpl::OpenUrlFromArc(const GURL& url) {}
 
-app_list::AppListPresenter* ShellDelegateImpl::GetAppListPresenter() {
-  if (!app_list_presenter_) {
-    app_list_presenter_.reset(new app_list::AppListPresenterImpl(
-        app_list_presenter_delegate_factory_.get()));
-  }
-  return app_list_presenter_.get();
-}
-
 ShelfDelegate* ShellDelegateImpl::CreateShelfDelegate(ShelfModel* model) {
   shelf_delegate_ = new test::TestShelfDelegate(model);
   return shelf_delegate_;
diff --git a/ash/shell/shell_delegate_impl.h b/ash/shell/shell_delegate_impl.h
index 7d8ae41..4836b3a 100644
--- a/ash/shell/shell_delegate_impl.h
+++ b/ash/shell/shell_delegate_impl.h
@@ -11,11 +11,6 @@
 #include "ash/common/shell_delegate.h"
 #include "base/macros.h"
 
-namespace app_list {
-class AppListPresenterDelegateFactory;
-class AppListPresenterImpl;
-}
-
 namespace keyboard {
 class KeyboardUI;
 }
@@ -40,7 +35,6 @@
   void Exit() override;
   keyboard::KeyboardUI* CreateKeyboardUI() override;
   void OpenUrlFromArc(const GURL& url) override;
-  app_list::AppListPresenter* GetAppListPresenter() override;
   ShelfDelegate* CreateShelfDelegate(ShelfModel* model) override;
   SystemTrayDelegate* CreateSystemTrayDelegate() override;
   std::unique_ptr<WallpaperDelegate> CreateWallpaperDelegate() override;
@@ -58,10 +52,7 @@
   void UpdateTouchscreenStatusFromPrefs() override;
 
  private:
-  ShelfDelegate* shelf_delegate_;
-  std::unique_ptr<app_list::AppListPresenterDelegateFactory>
-      app_list_presenter_delegate_factory_;
-  std::unique_ptr<app_list::AppListPresenterImpl> app_list_presenter_;
+  ShelfDelegate* shelf_delegate_ = nullptr;
 
   DISALLOW_COPY_AND_ASSIGN(ShellDelegateImpl);
 };
diff --git a/ash/test/BUILD.gn b/ash/test/BUILD.gn
index d90861d7..a7baad0 100644
--- a/ash/test/BUILD.gn
+++ b/ash/test/BUILD.gn
@@ -108,6 +108,8 @@
     "task_switch_time_tracker_test_api.h",
     "test_activation_delegate.cc",
     "test_activation_delegate.h",
+    "test_app_list_view_presenter_impl.cc",
+    "test_app_list_view_presenter_impl.h",
     "test_keyboard_ui.cc",
     "test_keyboard_ui.h",
     "test_overlay_delegate.cc",
@@ -156,6 +158,7 @@
     "//ui/accessibility",
     "//ui/app_list:test_support",
     "//ui/app_list/presenter",
+    "//ui/app_list/presenter:test_support",
     "//ui/aura",
     "//ui/aura:test_support",
     "//ui/base:test_support",
diff --git a/ash/test/test_app_list_view_presenter_impl.cc b/ash/test/test_app_list_view_presenter_impl.cc
new file mode 100644
index 0000000..579babf
--- /dev/null
+++ b/ash/test/test_app_list_view_presenter_impl.cc
@@ -0,0 +1,23 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ash/test/test_app_list_view_presenter_impl.h"
+
+#include "ash/app_list/app_list_presenter_delegate_factory.h"
+#include "base/memory/ptr_util.h"
+#include "ui/app_list/presenter/test/test_app_list_view_delegate_factory.h"
+
+namespace ash {
+namespace test {
+
+TestAppListViewPresenterImpl::TestAppListViewPresenterImpl()
+    : app_list::AppListPresenterImpl(base::MakeUnique<
+                                     AppListPresenterDelegateFactory>(
+          base::MakeUnique<app_list::test::TestAppListViewDelegateFactory>())) {
+}
+
+TestAppListViewPresenterImpl::~TestAppListViewPresenterImpl() {}
+
+}  // namespace test
+}  // namespace ash
diff --git a/ash/test/test_app_list_view_presenter_impl.h b/ash/test/test_app_list_view_presenter_impl.h
new file mode 100644
index 0000000..6d414f9
--- /dev/null
+++ b/ash/test/test_app_list_view_presenter_impl.h
@@ -0,0 +1,29 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef ASH_TEST_TEST_APP_LIST_VIEW_PRESENTER_IMPL_H_
+#define ASH_TEST_TEST_APP_LIST_VIEW_PRESENTER_IMPL_H_
+
+#include "ash/ash_export.h"
+#include "base/macros.h"
+#include "ui/app_list/presenter/app_list_presenter_impl.h"
+
+namespace ash {
+namespace test {
+
+// An app list presenter impl subclass that presents a no-op test app list view.
+// Some tests rely on the presenter and delegate implementations' behavior.
+class TestAppListViewPresenterImpl : public app_list::AppListPresenterImpl {
+ public:
+  TestAppListViewPresenterImpl();
+  ~TestAppListViewPresenterImpl() override;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(TestAppListViewPresenterImpl);
+};
+
+}  // namespace test
+}  // namespace ash
+
+#endif  // ASH_TEST_TEST_APP_LIST_VIEW_PRESENTER_IMPL_H_
diff --git a/ash/test/test_shell_delegate.cc b/ash/test/test_shell_delegate.cc
index 3402d6a..133a98b3 100644
--- a/ash/test/test_shell_delegate.cc
+++ b/ash/test/test_shell_delegate.cc
@@ -6,8 +6,6 @@
 
 #include <limits>
 
-#include "ash/app_list/app_list_presenter_delegate.h"
-#include "ash/app_list/app_list_presenter_delegate_factory.h"
 #include "ash/common/default_accessibility_delegate.h"
 #include "ash/common/gpu_support_stub.h"
 #include "ash/common/palette_delegate.h"
@@ -23,9 +21,6 @@
 #include "ash/wm/window_util.h"
 #include "base/logging.h"
 #include "base/memory/ptr_util.h"
-#include "ui/app_list/presenter/app_list_presenter_impl.h"
-#include "ui/app_list/presenter/app_list_view_delegate_factory.h"
-#include "ui/app_list/test/app_list_test_view_delegate.h"
 #include "ui/aura/window.h"
 #include "ui/gfx/image/image.h"
 
@@ -35,38 +30,12 @@
 
 namespace ash {
 namespace test {
-namespace {
-
-class AppListViewDelegateFactoryImpl
-    : public app_list::AppListViewDelegateFactory {
- public:
-  AppListViewDelegateFactoryImpl() {}
-  ~AppListViewDelegateFactoryImpl() override {}
-
-  // app_list::AppListViewDelegateFactory:
-  app_list::AppListViewDelegate* GetDelegate() override {
-    if (!app_list_view_delegate_.get()) {
-      app_list_view_delegate_.reset(
-          new app_list::test::AppListTestViewDelegate);
-    }
-    return app_list_view_delegate_.get();
-  }
-
- private:
-  std::unique_ptr<app_list::AppListViewDelegate> app_list_view_delegate_;
-
-  DISALLOW_COPY_AND_ASSIGN(AppListViewDelegateFactoryImpl);
-};
-
-}  // namespace
 
 TestShellDelegate::TestShellDelegate()
     : num_exit_requests_(0),
       multi_profiles_enabled_(false),
       force_maximize_on_first_run_(false),
-      touchscreen_enabled_in_local_pref_(true),
-      app_list_presenter_delegate_factory_(new AppListPresenterDelegateFactory(
-          base::WrapUnique(new AppListViewDelegateFactoryImpl))) {}
+      touchscreen_enabled_in_local_pref_(true) {}
 
 TestShellDelegate::~TestShellDelegate() {}
 
@@ -108,14 +77,6 @@
 
 void TestShellDelegate::OpenUrlFromArc(const GURL& url) {}
 
-app_list::AppListPresenter* TestShellDelegate::GetAppListPresenter() {
-  if (!app_list_presenter_) {
-    app_list_presenter_.reset(new app_list::AppListPresenterImpl(
-        app_list_presenter_delegate_factory_.get()));
-  }
-  return app_list_presenter_.get();
-}
-
 ShelfDelegate* TestShellDelegate::CreateShelfDelegate(ShelfModel* model) {
   return new TestShelfDelegate(model);
 }
diff --git a/ash/test/test_shell_delegate.h b/ash/test/test_shell_delegate.h
index a2722be..fe30c3c 100644
--- a/ash/test/test_shell_delegate.h
+++ b/ash/test/test_shell_delegate.h
@@ -12,11 +12,6 @@
 #include "ash/common/test/test_session_state_delegate.h"
 #include "base/macros.h"
 
-namespace app_list {
-class AppListPresenterDelegateFactory;
-class AppListPresenterImpl;
-}
-
 namespace keyboard {
 class KeyboardUI;
 }
@@ -45,7 +40,6 @@
   void Exit() override;
   keyboard::KeyboardUI* CreateKeyboardUI() override;
   void OpenUrlFromArc(const GURL& url) override;
-  app_list::AppListPresenter* GetAppListPresenter() override;
   ShelfDelegate* CreateShelfDelegate(ShelfModel* model) override;
   SystemTrayDelegate* CreateSystemTrayDelegate() override;
   std::unique_ptr<WallpaperDelegate> CreateWallpaperDelegate() override;
@@ -65,10 +59,6 @@
 
   int num_exit_requests() const { return num_exit_requests_; }
 
-  app_list::AppListPresenterImpl* app_list_presenter() {
-    return app_list_presenter_.get();
-  }
-
   void SetForceMaximizeOnFirstRun(bool maximize) {
     force_maximize_on_first_run_ = maximize;
   }
@@ -79,10 +69,6 @@
   bool force_maximize_on_first_run_;
   bool touchscreen_enabled_in_local_pref_;
 
-  std::unique_ptr<app_list::AppListPresenterDelegateFactory>
-      app_list_presenter_delegate_factory_;
-  std::unique_ptr<app_list::AppListPresenterImpl> app_list_presenter_;
-
   DISALLOW_COPY_AND_ASSIGN(TestShellDelegate);
 };
 
diff --git a/ash/wm/overview/window_selector_unittest.cc b/ash/wm/overview/window_selector_unittest.cc
index 4be3a09..915d253 100644
--- a/ash/wm/overview/window_selector_unittest.cc
+++ b/ash/wm/overview/window_selector_unittest.cc
@@ -36,6 +36,7 @@
 #include "ash/test/ash_test_base.h"
 #include "ash/test/shelf_view_test_api.h"
 #include "ash/test/shell_test_api.h"
+#include "ash/test/test_app_list_view_presenter_impl.h"
 #include "ash/wm/window_state_aura.h"
 #include "ash/wm/window_util.h"
 #include "base/command_line.h"
@@ -1207,10 +1208,15 @@
   gfx::Rect bounds(0, 0, 400, 400);
   std::unique_ptr<aura::Window> window1(CreateWindow(bounds));
   std::unique_ptr<aura::Window> window2(CreateWindow(bounds));
-  WmShell::Get()->ShowAppList();
-  EXPECT_TRUE(WmShell::Get()->GetAppListTargetVisibility());
+
+  // The tested behavior relies on the app list presenter delegate.
+  test::TestAppListViewPresenterImpl app_list_presenter_impl;
+
+  app_list_presenter_impl.Show(display_manager()->first_display_id());
+  EXPECT_TRUE(app_list_presenter_impl.IsVisible());
+
   ToggleOverview();
-  EXPECT_FALSE(WmShell::Get()->GetAppListTargetVisibility());
+  EXPECT_FALSE(app_list_presenter_impl.IsVisible());
   ToggleOverview();
 }
 
diff --git a/ash/wm/window_cycle_controller_unittest.cc b/ash/wm/window_cycle_controller_unittest.cc
index 2223ec5e..ed72e1c 100644
--- a/ash/wm/window_cycle_controller_unittest.cc
+++ b/ash/wm/window_cycle_controller_unittest.cc
@@ -22,6 +22,7 @@
 #include "ash/shell.h"
 #include "ash/test/ash_test_base.h"
 #include "ash/test/shelf_view_test_api.h"
+#include "ash/test/test_app_list_view_presenter_impl.h"
 #include "ash/test/test_shell_delegate.h"
 #include "ash/wm/window_state_aura.h"
 #include "ash/wm/window_util.h"
@@ -31,6 +32,7 @@
 #include "ui/aura/test/test_windows.h"
 #include "ui/aura/window.h"
 #include "ui/aura/window_event_dispatcher.h"
+#include "ui/display/manager/display_manager.h"
 #include "ui/events/event_handler.h"
 #include "ui/events/test/event_generator.h"
 #include "ui/gfx/geometry/rect.h"
@@ -473,14 +475,17 @@
 
 // Tests that beginning window selection hides the app list.
 TEST_F(WindowCycleControllerTest, SelectingHidesAppList) {
+  // The tested behavior relies on the app list presenter implementation.
+  test::TestAppListViewPresenterImpl app_list_presenter_impl;
+
   WindowCycleController* controller = WmShell::Get()->window_cycle_controller();
 
   std::unique_ptr<aura::Window> window0(CreateTestWindowInShellWithId(0));
   std::unique_ptr<aura::Window> window1(CreateTestWindowInShellWithId(1));
-  WmShell::Get()->ShowAppList();
-  EXPECT_TRUE(WmShell::Get()->GetAppListTargetVisibility());
+  app_list_presenter_impl.Show(display_manager()->first_display_id());
+  EXPECT_TRUE(app_list_presenter_impl.IsVisible());
   controller->HandleCycleWindow(WindowCycleController::FORWARD);
-  EXPECT_FALSE(WmShell::Get()->GetAppListTargetVisibility());
+  EXPECT_FALSE(app_list_presenter_impl.IsVisible());
 
   // Make sure that dismissing the app list this way doesn't pass activation
   // to a different window.
diff --git a/base/trace_event/heap_profiler_allocation_register.cc b/base/trace_event/heap_profiler_allocation_register.cc
index 2c2cd378..63d4061 100644
--- a/base/trace_event/heap_profiler_allocation_register.cc
+++ b/base/trace_event/heap_profiler_allocation_register.cc
@@ -60,12 +60,12 @@
   // The multiplicative hashing scheme from [Knuth 1998]. The value of |a| has
   // been chosen carefully based on measurements with real-word data (addresses
   // recorded from a Chrome trace run). It is the first prime after 2^17. For
-  // |shift|, 13, 14 and 15 yield good results. These values are tuned to 2^18
-  // buckets. Microbenchmarks show that this simple scheme outperforms fancy
-  // hashes like Murmur3 by 20 to 40 percent.
+  // |shift|, 15 yield good results for both 2^18 and 2^19 bucket sizes.
+  // Microbenchmarks show that this simple scheme outperforms fancy hashes like
+  // Murmur3 by 20 to 40 percent.
   const uintptr_t key = reinterpret_cast<uintptr_t>(address);
   const uintptr_t a = 131101;
-  const uintptr_t shift = 14;
+  const uintptr_t shift = 15;
   const uintptr_t h = (key * a) >> shift;
   return h;
 }
diff --git a/base/trace_event/heap_profiler_allocation_register.h b/base/trace_event/heap_profiler_allocation_register.h
index 873aebfc..d6a02fae 100644
--- a/base/trace_event/heap_profiler_allocation_register.h
+++ b/base/trace_event/heap_profiler_allocation_register.h
@@ -16,6 +16,7 @@
 #include "base/process/process_metrics.h"
 #include "base/template_util.h"
 #include "base/trace_event/heap_profiler_allocation_context.h"
+#include "build/build_config.h"
 
 namespace base {
 namespace trace_event {
@@ -198,7 +199,9 @@
     // the simplest solution is to just allocate a humongous chunk of address
     // space.
 
-    DCHECK_LT(next_unused_cell_, num_cells_ + 1);
+    CHECK_LT(next_unused_cell_, num_cells_ + 1)
+        << "Allocation Register hash table has too little capacity. Increase "
+           "the capacity to run heap profiler in large sessions.";
 
     return &cells_[idx];
   }
@@ -299,10 +302,16 @@
  private:
   friend AllocationRegisterTest;
 
-  // Expect max 1.5M allocations. Number of buckets is 2^18 for optimal
-  // hashing and should be changed together with AddressHasher.
+// Expect lower number of allocations from mobile platforms. Load factor
+// (capacity / bucket count) is kept less than 10 for optimal hashing. The
+// number of buckets should be changed together with AddressHasher.
+#if defined(OS_ANDROID) || defined(OS_IOS)
   static const size_t kAllocationBuckets = 1 << 18;
   static const size_t kAllocationCapacity = 1500000;
+#else
+  static const size_t kAllocationBuckets = 1 << 19;
+  static const size_t kAllocationCapacity = 5000000;
+#endif
 
   // 2^16 works well with BacktraceHasher. When increasing this number make
   // sure BacktraceHasher still produces low number of collisions.
diff --git a/blimp/engine/BUILD.gn b/blimp/engine/BUILD.gn
index 6007542..f321db4 100644
--- a/blimp/engine/BUILD.gn
+++ b/blimp/engine/BUILD.gn
@@ -93,7 +93,7 @@
     "//base",
     "//blimp/common",
     "//blimp/common/proto",
-    "//blimp/engine:blob_channel_mojo_cpp_sources",
+    "//blimp/engine:blob_channel_mojo",
     "//blimp/net",
     "//components/crash/content/app:app_breakpad_mac_win_to_be_deleted",
     "//components/crash/content/app:lib",
@@ -588,7 +588,7 @@
     "//base",
     "//blimp/common",
     "//blimp/common:test_support",
-    "//blimp/engine:blob_channel_mojo_cpp_sources",
+    "//blimp/engine:blob_channel_mojo",
     "//blimp/engine:blob_channel_service",
     "//blimp/net:test_support",
     "//blimp/test:support",
diff --git a/build/install-build-deps-android.sh b/build/install-build-deps-android.sh
index 0261b37..37f4244 100755
--- a/build/install-build-deps-android.sh
+++ b/build/install-build-deps-android.sh
@@ -22,7 +22,7 @@
 lsb_release=$(lsb_release --codename --short)
 
 case $lsb_release in
-  xenial)
+  xenial|yakkety)
     java_alternative="java-1.8.0-openjdk-amd64"
     java_pkgs="openjdk-8-jre openjdk-8-jdk"
   ;;
diff --git a/build/install-build-deps.sh b/build/install-build-deps.sh
index dcc4108..a7651bf 100755
--- a/build/install-build-deps.sh
+++ b/build/install-build-deps.sh
@@ -111,12 +111,12 @@
 fi
 
 lsb_release=$(lsb_release --codename --short)
-supported_releases="(precise|trusty|utopic|vivid|wily|xenial|jessie)"
+supported_releases="(precise|trusty|utopic|vivid|wily|xenial|yakkety|jessie)"
 if [ 0 -eq "${do_unsupported-0}" ] && [ 0 -eq "${do_quick_check-0}" ] ; then
   if [[ ! $lsb_release =~ $supported_releases ]]; then
     echo "ERROR: Only Ubuntu 12.04 (precise), 14.04 (trusty), " \
-      "14.10 (utopic), 15.04 (vivid), 15.10 (wily) and 16.04 (xenial), " \
-      "and Debian 8 (jessie) are currently supported" >&2
+      "14.10 (utopic), 15.04 (vivid), 15.10 (wily), 16.04 (xenial), " \
+      "16.10 (yakkety) and Debian 8 (jessie) are currently supported" >&2
     exit 1
   fi
 
@@ -163,7 +163,7 @@
 lib_list="libatk1.0-0 libc6 libasound2 libcairo2 libcap2 libcups2 libexpat1
           libffi6 libfontconfig1 libfreetype6 libglib2.0-0 libgnome-keyring0
           libgtk2.0-0 libpam0g libpango1.0-0 libpci3 libpcre3 libpixman-1-0
-          libpng12-0 libspeechd2 libstdc++6 libsqlite3-0 libx11-6 libx11-xcb1
+          libspeechd2 libstdc++6 libsqlite3-0 libx11-6 libx11-xcb1
           libxau6 libxcb1 libxcomposite1 libxcursor1 libxdamage1 libxdmcp6
           libxext6 libxfixes3 libxi6 libxinerama1 libxrandr2 libxrender1
           libxtst6 zlib1g $chromeos_lib_list"
@@ -236,12 +236,7 @@
     arm_list+=" g++-4.8-multilib-arm-linux-gnueabihf
                 gcc-4.8-multilib-arm-linux-gnueabihf"
     ;;
-  wily)
-    arm_list+=" g++-5-multilib-arm-linux-gnueabihf
-                gcc-5-multilib-arm-linux-gnueabihf
-                gcc-arm-linux-gnueabihf"
-    ;;
-  xenial)
+  wily|xenial|yakkety)
     arm_list+=" g++-5-multilib-arm-linux-gnueabihf
                 gcc-5-multilib-arm-linux-gnueabihf
                 gcc-arm-linux-gnueabihf"
@@ -284,6 +279,11 @@
 nacl_list="${nacl_list} libgl1-mesa-glx${mesa_variant}:i386"
 
 # Some package names have changed over time
+if package_exists libpng12-0; then
+  lib_list="${lib_list} libpng12-0"
+else
+  lib_list="${lib_list} libpng16-16"
+fi
 if package_exists libnspr4-dbg; then
   dbg_list="${dbg_list} libnspr4-dbg libnss3-dbg"
   lib_list="${lib_list} libnspr4 libnss3"
diff --git a/cc/output/shader.cc b/cc/output/shader.cc
index ee0e6a7..ea5288b 100644
--- a/cc/output/shader.cc
+++ b/cc/output/shader.cc
@@ -29,7 +29,6 @@
 // Shaders are passed in with lambda syntax, which tricks clang-format into
 // handling them correctly. StipLambda removes this.
 #define SHADER0(Src) StripLambda(#Src)
-#define VERTEX_SHADER(Head, Body) SetVertexShaderDefines(Head + Body)
 
 using gpu::gles2::GLES2Interface;
 
@@ -76,18 +75,6 @@
   return shader_string;
 }
 
-static std::string SetVertexShaderDefines(const std::string& shader_string) {
-  // We unconditionally use highp in the vertex shader since
-  // we are unlikely to be vertex shader bound when drawing large quads.
-  // Also, some vertex shaders mutate the texture coordinate in such a
-  // way that the effective precision might be lower than expected.
-  return base::StringPrintf(
-             "#define TexCoordPrecision highp\n"
-             "#define NUM_STATIC_QUADS %d\n",
-             StaticGeometryBinding::NUM_QUADS) +
-         shader_string;
-}
-
 TexCoordPrecision TexCoordPrecisionRequired(GLES2Interface* context,
                                             int* highp_threshold_cache,
                                             int highp_threshold_min,
@@ -173,41 +160,93 @@
                                    max_size.height());
 }
 
-VertexShaderPosTex::VertexShaderPosTex() : matrix_location_(-1) {
+VertexShaderBase::VertexShaderBase() {}
+
+void VertexShaderBase::Init(GLES2Interface* context,
+                            unsigned program,
+                            int* base_uniform_index) {
+  std::vector<const char*> uniforms;
+  std::vector<int> locations;
+
+  if (has_tex_transform_)
+    uniforms.push_back("texTransform");
+  if (has_vertex_tex_transform_)
+    uniforms.push_back("vertexTexTransform");
+  if (has_tex_matrix_)
+    uniforms.push_back("texMatrix");
+  if (has_ya_uv_tex_scale_offset_) {
+    uniforms.push_back("yaTexScale");
+    uniforms.push_back("yaTexOffset");
+    uniforms.push_back("uvTexScale");
+    uniforms.push_back("uvTexOffset");
+  }
+  if (has_matrix_)
+    uniforms.push_back("matrix");
+  if (has_vertex_opacity_)
+    uniforms.push_back("opacity");
+  if (has_aa_) {
+    uniforms.push_back("viewport");
+    uniforms.push_back("edge");
+  }
+  if (has_quad_)
+    uniforms.push_back("quad");
+
+  locations.resize(uniforms.size());
+
+  GetProgramUniformLocations(context, program, uniforms.size(), uniforms.data(),
+                             locations.data(), base_uniform_index);
+
+  size_t index = 0;
+  if (has_tex_transform_)
+    tex_transform_location_ = locations[index++];
+  if (has_vertex_tex_transform_)
+    vertex_tex_transform_location_ = locations[index++];
+  if (has_tex_matrix_)
+    tex_matrix_location_ = locations[index++];
+  if (has_ya_uv_tex_scale_offset_) {
+    ya_tex_scale_location_ = locations[index++];
+    ya_tex_offset_location_ = locations[index++];
+    uv_tex_scale_location_ = locations[index++];
+    uv_tex_offset_location_ = locations[index++];
+  }
+  if (has_matrix_)
+    matrix_location_ = locations[index++];
+  if (has_vertex_opacity_)
+    vertex_opacity_location_ = locations[index++];
+  if (has_aa_) {
+    viewport_location_ = locations[index++];
+    edge_location_ = locations[index++];
+  }
+  if (has_quad_)
+    quad_location_ = locations[index++];
 }
 
-void VertexShaderPosTex::Init(GLES2Interface* context,
-                              unsigned program,
-                              int* base_uniform_index) {
-  static const char* uniforms[] = {
-      "matrix",
-  };
-  int locations[arraysize(uniforms)];
-
-  GetProgramUniformLocations(context,
-                             program,
-                             arraysize(uniforms),
-                             uniforms,
-                             locations,
-                             base_uniform_index);
-  matrix_location_ = locations[0];
+void VertexShaderBase::FillLocations(ShaderLocations* locations) const {
+  locations->quad = quad_location();
+  locations->edge = edge_location();
+  locations->viewport = viewport_location();
+  locations->matrix = matrix_location();
+  locations->tex_transform = tex_transform_location();
 }
 
-std::string VertexShaderPosTex::GetShaderString() const {
-  return VERTEX_SHADER(GetShaderHead(), GetShaderBody());
+std::string VertexShaderBase::GetShaderString() const {
+  // We unconditionally use highp in the vertex shader since
+  // we are unlikely to be vertex shader bound when drawing large quads.
+  // Also, some vertex shaders mutate the texture coordinate in such a
+  // way that the effective precision might be lower than expected.
+  return base::StringPrintf(
+             "#define TexCoordPrecision highp\n"
+             "#define NUM_STATIC_QUADS %d\n",
+             StaticGeometryBinding::NUM_QUADS) +
+         GetShaderSource();
 }
 
-std::string VertexShaderPosTex::GetShaderHead() {
+std::string VertexShaderPosTex::GetShaderSource() const {
   return SHADER0([]() {
     attribute vec4 a_position;
     attribute TexCoordPrecision vec2 a_texCoord;
     uniform mat4 matrix;
     varying TexCoordPrecision vec2 v_texCoord;
-  });
-}
-
-std::string VertexShaderPosTex::GetShaderBody() {
-  return SHADER0([]() {
     void main() {
       gl_Position = matrix * a_position;
       v_texCoord = a_texCoord;
@@ -215,40 +254,7 @@
   });
 }
 
-VertexShaderPosTexYUVStretchOffset::VertexShaderPosTexYUVStretchOffset()
-    : matrix_location_(-1),
-      ya_tex_scale_location_(-1),
-      ya_tex_offset_location_(-1),
-      uv_tex_scale_location_(-1),
-      uv_tex_offset_location_(-1) {
-}
-
-void VertexShaderPosTexYUVStretchOffset::Init(GLES2Interface* context,
-                                              unsigned program,
-                                              int* base_uniform_index) {
-  static const char* uniforms[] = {
-      "matrix", "yaTexScale", "yaTexOffset", "uvTexScale", "uvTexOffset",
-  };
-  int locations[arraysize(uniforms)];
-
-  GetProgramUniformLocations(context,
-                             program,
-                             arraysize(uniforms),
-                             uniforms,
-                             locations,
-                             base_uniform_index);
-  matrix_location_ = locations[0];
-  ya_tex_scale_location_ = locations[1];
-  ya_tex_offset_location_ = locations[2];
-  uv_tex_scale_location_ = locations[3];
-  uv_tex_offset_location_ = locations[4];
-}
-
-std::string VertexShaderPosTexYUVStretchOffset::GetShaderString() const {
-  return VERTEX_SHADER(GetShaderHead(), GetShaderBody());
-}
-
-std::string VertexShaderPosTexYUVStretchOffset::GetShaderHead() {
+std::string VertexShaderPosTexYUVStretchOffset::GetShaderSource() const {
   return SHADER0([]() {
     precision mediump float;
     attribute vec4 a_position;
@@ -260,11 +266,6 @@
     uniform TexCoordPrecision vec2 yaTexOffset;
     uniform TexCoordPrecision vec2 uvTexScale;
     uniform TexCoordPrecision vec2 uvTexOffset;
-  });
-}
-
-std::string VertexShaderPosTexYUVStretchOffset::GetShaderBody() {
-  return SHADER0([]() {
     void main() {
       gl_Position = matrix * a_position;
       v_yaTexCoord = a_texCoord * yaTexScale + yaTexOffset;
@@ -273,73 +274,15 @@
   });
 }
 
-VertexShaderPos::VertexShaderPos() : matrix_location_(-1) {
-}
-
-void VertexShaderPos::Init(GLES2Interface* context,
-                           unsigned program,
-                           int* base_uniform_index) {
-  static const char* uniforms[] = {
-      "matrix",
-  };
-  int locations[arraysize(uniforms)];
-
-  GetProgramUniformLocations(context,
-                             program,
-                             arraysize(uniforms),
-                             uniforms,
-                             locations,
-                             base_uniform_index);
-  matrix_location_ = locations[0];
-}
-
-std::string VertexShaderPos::GetShaderString() const {
-  return VERTEX_SHADER(GetShaderHead(), GetShaderBody());
-}
-
-std::string VertexShaderPos::GetShaderHead() {
+std::string VertexShaderPos::GetShaderSource() const {
   return SHADER0([]() {
     attribute vec4 a_position;
     uniform mat4 matrix;
-  });
-}
-
-std::string VertexShaderPos::GetShaderBody() {
-  return SHADER0([]() {
     void main() { gl_Position = matrix * a_position; }
   });
 }
 
-VertexShaderPosTexTransform::VertexShaderPosTexTransform()
-    : matrix_location_(-1),
-      tex_transform_location_(-1),
-      vertex_opacity_location_(-1) {
-}
-
-void VertexShaderPosTexTransform::Init(GLES2Interface* context,
-                                       unsigned program,
-                                       int* base_uniform_index) {
-  static const char* uniforms[] = {
-      "matrix", "texTransform", "opacity",
-  };
-  int locations[arraysize(uniforms)];
-
-  GetProgramUniformLocations(context,
-                             program,
-                             arraysize(uniforms),
-                             uniforms,
-                             locations,
-                             base_uniform_index);
-  matrix_location_ = locations[0];
-  tex_transform_location_ = locations[1];
-  vertex_opacity_location_ = locations[2];
-}
-
-std::string VertexShaderPosTexTransform::GetShaderString() const {
-  return VERTEX_SHADER(GetShaderHead(), GetShaderBody());
-}
-
-std::string VertexShaderPosTexTransform::GetShaderHead() {
+std::string VertexShaderPosTexTransform::GetShaderSource() const {
   return SHADER0([]() {
     attribute vec4 a_position;
     attribute TexCoordPrecision vec2 a_texCoord;
@@ -349,11 +292,6 @@
     uniform float opacity[NUM_STATIC_QUADS * 4];
     varying TexCoordPrecision vec2 v_texCoord;
     varying float v_alpha;
-  });
-}
-
-std::string VertexShaderPosTexTransform::GetShaderBody() {
-  return SHADER0([]() {
     void main() {
       int quad_index = int(a_index * 0.25);  // NOLINT
       gl_Position = matrix[quad_index] * a_position;
@@ -364,25 +302,10 @@
   });
 }
 
-void VertexShaderPosTexTransform::FillLocations(
-    ShaderLocations* locations) const {
-  locations->matrix = matrix_location();
-  locations->tex_transform = tex_transform_location();
-}
-
-std::string VertexShaderPosTexIdentity::GetShaderString() const {
-  return VERTEX_SHADER(GetShaderHead(), GetShaderBody());
-}
-
-std::string VertexShaderPosTexIdentity::GetShaderHead() {
+std::string VertexShaderPosTexIdentity::GetShaderSource() const {
   return SHADER0([]() {
     attribute vec4 a_position;
     varying TexCoordPrecision vec2 v_texCoord;
-  });
-}
-
-std::string VertexShaderPosTexIdentity::GetShaderBody() {
-  return SHADER0([]() {
     void main() {
       gl_Position = a_position;
       v_texCoord = (a_position.xy + vec2(1.0)) * 0.5;
@@ -390,33 +313,7 @@
   });
 }
 
-VertexShaderQuad::VertexShaderQuad()
-    : matrix_location_(-1), quad_location_(-1) {
-}
-
-void VertexShaderQuad::Init(GLES2Interface* context,
-                            unsigned program,
-                            int* base_uniform_index) {
-  static const char* uniforms[] = {
-      "matrix", "quad",
-  };
-  int locations[arraysize(uniforms)];
-
-  GetProgramUniformLocations(context,
-                             program,
-                             arraysize(uniforms),
-                             uniforms,
-                             locations,
-                             base_uniform_index);
-  matrix_location_ = locations[0];
-  quad_location_ = locations[1];
-}
-
-std::string VertexShaderQuad::GetShaderString() const {
-  return VERTEX_SHADER(GetShaderHead(), GetShaderBody());
-}
-
-std::string VertexShaderQuad::GetShaderHead() {
+std::string VertexShaderQuad::GetShaderSource() const {
 #if defined(OS_ANDROID)
   // TODO(epenner): Find the cause of this 'quad' uniform
   // being missing if we don't add dummy variables.
@@ -428,20 +325,6 @@
     uniform TexCoordPrecision vec2 quad[4];
     uniform TexCoordPrecision vec2 dummy_uniform;
     varying TexCoordPrecision vec2 dummy_varying;
-  });
-#else
-  return SHADER0([]() {
-    attribute TexCoordPrecision vec4 a_position;
-    attribute float a_index;
-    uniform mat4 matrix;
-    uniform TexCoordPrecision vec2 quad[4];
-  });
-#endif
-}
-
-std::string VertexShaderQuad::GetShaderBody() {
-#if defined(OS_ANDROID)
-  return SHADER0([]() {
     void main() {
       vec2 pos = quad[int(a_index)];  // NOLINT
       gl_Position = matrix * vec4(pos, a_position.z, a_position.w);
@@ -450,6 +333,10 @@
   });
 #else
   return SHADER0([]() {
+    attribute TexCoordPrecision vec4 a_position;
+    attribute float a_index;
+    uniform mat4 matrix;
+    uniform TexCoordPrecision vec2 quad[4];
     void main() {
       vec2 pos = quad[int(a_index)];  // NOLINT
       gl_Position = matrix * vec4(pos, a_position.z, a_position.w);
@@ -458,38 +345,7 @@
 #endif
 }
 
-VertexShaderQuadAA::VertexShaderQuadAA()
-    : matrix_location_(-1),
-      viewport_location_(-1),
-      quad_location_(-1),
-      edge_location_(-1) {
-}
-
-void VertexShaderQuadAA::Init(GLES2Interface* context,
-                              unsigned program,
-                              int* base_uniform_index) {
-  static const char* uniforms[] = {
-      "matrix", "viewport", "quad", "edge",
-  };
-  int locations[arraysize(uniforms)];
-
-  GetProgramUniformLocations(context,
-                             program,
-                             arraysize(uniforms),
-                             uniforms,
-                             locations,
-                             base_uniform_index);
-  matrix_location_ = locations[0];
-  viewport_location_ = locations[1];
-  quad_location_ = locations[2];
-  edge_location_ = locations[3];
-}
-
-std::string VertexShaderQuadAA::GetShaderString() const {
-  return VERTEX_SHADER(GetShaderHead(), GetShaderBody());
-}
-
-std::string VertexShaderQuadAA::GetShaderHead() {
+std::string VertexShaderQuadAA::GetShaderSource() const {
   return SHADER0([]() {
     attribute TexCoordPrecision vec4 a_position;
     attribute float a_index;
@@ -498,11 +354,6 @@
     uniform TexCoordPrecision vec2 quad[4];
     uniform TexCoordPrecision vec3 edge[8];
     varying TexCoordPrecision vec4 edge_dist[2];  // 8 edge distances.
-  });
-}
-
-std::string VertexShaderQuadAA::GetShaderBody() {
-  return SHADER0([]() {
     void main() {
       vec2 pos = quad[int(a_index)];  // NOLINT
       gl_Position = matrix * vec4(pos, a_position.z, a_position.w);
@@ -518,40 +369,7 @@
   });
 }
 
-VertexShaderQuadTexTransformAA::VertexShaderQuadTexTransformAA()
-    : matrix_location_(-1),
-      viewport_location_(-1),
-      quad_location_(-1),
-      edge_location_(-1),
-      tex_transform_location_(-1) {
-}
-
-void VertexShaderQuadTexTransformAA::Init(GLES2Interface* context,
-                                          unsigned program,
-                                          int* base_uniform_index) {
-  static const char* uniforms[] = {
-      "matrix", "viewport", "quad", "edge", "texTrans",
-  };
-  int locations[arraysize(uniforms)];
-
-  GetProgramUniformLocations(context,
-                             program,
-                             arraysize(uniforms),
-                             uniforms,
-                             locations,
-                             base_uniform_index);
-  matrix_location_ = locations[0];
-  viewport_location_ = locations[1];
-  quad_location_ = locations[2];
-  edge_location_ = locations[3];
-  tex_transform_location_ = locations[4];
-}
-
-std::string VertexShaderQuadTexTransformAA::GetShaderString() const {
-  return VERTEX_SHADER(GetShaderHead(), GetShaderBody());
-}
-
-std::string VertexShaderQuadTexTransformAA::GetShaderHead() {
+std::string VertexShaderQuadTexTransformAA::GetShaderSource() const {
   return SHADER0([]() {
     attribute TexCoordPrecision vec4 a_position;
     attribute float a_index;
@@ -559,14 +377,9 @@
     uniform vec4 viewport;
     uniform TexCoordPrecision vec2 quad[4];
     uniform TexCoordPrecision vec3 edge[8];
-    uniform TexCoordPrecision vec4 texTrans;
+    uniform TexCoordPrecision vec4 texTransform;
     varying TexCoordPrecision vec2 v_texCoord;
     varying TexCoordPrecision vec4 edge_dist[2];  // 8 edge distances.
-  });
-}
-
-std::string VertexShaderQuadTexTransformAA::GetShaderBody() {
-  return SHADER0([]() {
     void main() {
       vec2 pos = quad[int(a_index)];  // NOLINT
       gl_Position = matrix * vec4(pos, a_position.z, a_position.w);
@@ -578,51 +391,12 @@
       edge_dist[1] = vec4(dot(edge[4], screen_pos), dot(edge[5], screen_pos),
                           dot(edge[6], screen_pos), dot(edge[7], screen_pos)) *
                      gl_Position.w;
-      v_texCoord = (pos.xy + vec2(0.5)) * texTrans.zw + texTrans.xy;
+      v_texCoord = (pos.xy + vec2(0.5)) * texTransform.zw + texTransform.xy;
     }
   });
 }
 
-void VertexShaderQuadTexTransformAA::FillLocations(
-    ShaderLocations* locations) const {
-  locations->quad = quad_location();
-  locations->edge = edge_location();
-  locations->viewport = viewport_location();
-  locations->matrix = matrix_location();
-  locations->tex_transform = tex_transform_location();
-}
-
-
-VertexShaderTile::VertexShaderTile()
-    : matrix_location_(-1),
-      quad_location_(-1),
-      vertex_tex_transform_location_(-1) {
-}
-
-void VertexShaderTile::Init(GLES2Interface* context,
-                            unsigned program,
-                            int* base_uniform_index) {
-  static const char* uniforms[] = {
-      "matrix", "quad", "vertexTexTransform",
-  };
-  int locations[arraysize(uniforms)];
-
-  GetProgramUniformLocations(context,
-                             program,
-                             arraysize(uniforms),
-                             uniforms,
-                             locations,
-                             base_uniform_index);
-  matrix_location_ = locations[0];
-  quad_location_ = locations[1];
-  vertex_tex_transform_location_ = locations[2];
-}
-
-std::string VertexShaderTile::GetShaderString() const {
-  return VERTEX_SHADER(GetShaderHead(), GetShaderBody());
-}
-
-std::string VertexShaderTile::GetShaderHead() {
+std::string VertexShaderTile::GetShaderSource() const {
   return SHADER0([]() {
     attribute TexCoordPrecision vec4 a_position;
     attribute TexCoordPrecision vec2 a_texCoord;
@@ -631,11 +405,6 @@
     uniform TexCoordPrecision vec2 quad[4];
     uniform TexCoordPrecision vec4 vertexTexTransform;
     varying TexCoordPrecision vec2 v_texCoord;
-  });
-}
-
-std::string VertexShaderTile::GetShaderBody() {
-  return SHADER0([]() {
     void main() {
       vec2 pos = quad[int(a_index)];  // NOLINT
       gl_Position = matrix * vec4(pos, a_position.z, a_position.w);
@@ -644,40 +413,7 @@
   });
 }
 
-VertexShaderTileAA::VertexShaderTileAA()
-    : matrix_location_(-1),
-      viewport_location_(-1),
-      quad_location_(-1),
-      edge_location_(-1),
-      vertex_tex_transform_location_(-1) {
-}
-
-void VertexShaderTileAA::Init(GLES2Interface* context,
-                              unsigned program,
-                              int* base_uniform_index) {
-  static const char* uniforms[] = {
-      "matrix", "viewport", "quad", "edge", "vertexTexTransform",
-  };
-  int locations[arraysize(uniforms)];
-
-  GetProgramUniformLocations(context,
-                             program,
-                             arraysize(uniforms),
-                             uniforms,
-                             locations,
-                             base_uniform_index);
-  matrix_location_ = locations[0];
-  viewport_location_ = locations[1];
-  quad_location_ = locations[2];
-  edge_location_ = locations[3];
-  vertex_tex_transform_location_ = locations[4];
-}
-
-std::string VertexShaderTileAA::GetShaderString() const {
-  return VERTEX_SHADER(GetShaderHead(), GetShaderBody());
-}
-
-std::string VertexShaderTileAA::GetShaderHead() {
+std::string VertexShaderTileAA::GetShaderSource() const {
   return SHADER0([]() {
     attribute TexCoordPrecision vec4 a_position;
     attribute float a_index;
@@ -688,11 +424,6 @@
     uniform TexCoordPrecision vec4 vertexTexTransform;
     varying TexCoordPrecision vec2 v_texCoord;
     varying TexCoordPrecision vec4 edge_dist[2];  // 8 edge distances.
-  });
-}
-
-std::string VertexShaderTileAA::GetShaderBody() {
-  return SHADER0([]() {
     void main() {
       vec2 pos = quad[int(a_index)];  // NOLINT
       gl_Position = matrix * vec4(pos, a_position.z, a_position.w);
@@ -709,44 +440,13 @@
   });
 }
 
-VertexShaderVideoTransform::VertexShaderVideoTransform()
-    : matrix_location_(-1), tex_matrix_location_(-1) {
-}
-
-void VertexShaderVideoTransform::Init(GLES2Interface* context,
-                                      unsigned program,
-                                      int* base_uniform_index) {
-  static const char* uniforms[] = {
-      "matrix", "texMatrix",
-  };
-  int locations[arraysize(uniforms)];
-
-  GetProgramUniformLocations(context,
-                             program,
-                             arraysize(uniforms),
-                             uniforms,
-                             locations,
-                             base_uniform_index);
-  matrix_location_ = locations[0];
-  tex_matrix_location_ = locations[1];
-}
-
-std::string VertexShaderVideoTransform::GetShaderString() const {
-  return VERTEX_SHADER(GetShaderHead(), GetShaderBody());
-}
-
-std::string VertexShaderVideoTransform::GetShaderHead() {
+std::string VertexShaderVideoTransform::GetShaderSource() const {
   return SHADER0([]() {
     attribute vec4 a_position;
     attribute TexCoordPrecision vec2 a_texCoord;
     uniform mat4 matrix;
     uniform TexCoordPrecision mat4 texMatrix;
     varying TexCoordPrecision vec2 v_texCoord;
-  });
-}
-
-std::string VertexShaderVideoTransform::GetShaderBody() {
-  return SHADER0([]() {
     void main() {
       gl_Position = matrix * a_position;
       v_texCoord = (texMatrix * vec4(a_texCoord.xy, 0.0, 1.0)).xy;
@@ -1319,20 +1019,7 @@
   return header + source;
 }
 
-FragmentShaderYUVVideo::FragmentShaderYUVVideo()
-    : y_texture_location_(-1),
-      u_texture_location_(-1),
-      v_texture_location_(-1),
-      uv_texture_location_(-1),
-      a_texture_location_(-1),
-      lut_texture_location_(-1),
-      alpha_location_(-1),
-      yuv_matrix_location_(-1),
-      yuv_adj_location_(-1),
-      ya_clamp_rect_location_(-1),
-      uv_clamp_rect_location_(-1),
-      resource_multiplier_location_(-1),
-      resource_offset_location_(-1) {}
+FragmentShaderYUVVideo::FragmentShaderYUVVideo() {}
 
 void FragmentShaderYUVVideo::SetFeatures(bool use_alpha_texture,
                                          bool use_nv12,
diff --git a/cc/output/shader.h b/cc/output/shader.h
index c0cb758..6afd22b 100644
--- a/cc/output/shader.h
+++ b/cc/output/shader.h
@@ -113,257 +113,163 @@
     int highp_threshold_min,
     const gfx::Size& max_size);
 
-class VertexShaderPosTex {
+class VertexShaderBase {
  public:
-  VertexShaderPosTex();
-
+  VertexShaderBase();
   void Init(gpu::gles2::GLES2Interface* context,
             unsigned program,
             int* base_uniform_index);
   std::string GetShaderString() const;
-  static std::string GetShaderHead();
-  static std::string GetShaderBody();
+  void FillLocations(ShaderLocations* locations) const;
 
-  int matrix_location() const { return matrix_location_; }
+  int tex_transform_location() const { return tex_transform_location_; }
 
- private:
-  int matrix_location_;
+  int vertex_tex_transform_location() const {
+    return vertex_tex_transform_location_;
+  }
 
-  DISALLOW_COPY_AND_ASSIGN(VertexShaderPosTex);
-};
+  int tex_matrix_location() const { return tex_matrix_location_; }
 
-class VertexShaderPosTexYUVStretchOffset {
- public:
-  VertexShaderPosTexYUVStretchOffset();
-
-  void Init(gpu::gles2::GLES2Interface* context,
-            unsigned program,
-            int* base_uniform_index);
-  std::string GetShaderString() const;
-  static std::string GetShaderHead();
-  static std::string GetShaderBody();
-
-  int matrix_location() const { return matrix_location_; }
   int ya_tex_scale_location() const { return ya_tex_scale_location_; }
   int ya_tex_offset_location() const { return ya_tex_offset_location_; }
   int uv_tex_scale_location() const { return uv_tex_scale_location_; }
   int uv_tex_offset_location() const { return uv_tex_offset_location_; }
 
- private:
-  int matrix_location_;
-  int ya_tex_scale_location_;
-  int ya_tex_offset_location_;
-  int uv_tex_scale_location_;
-  int uv_tex_offset_location_;
-
-  DISALLOW_COPY_AND_ASSIGN(VertexShaderPosTexYUVStretchOffset);
-};
-
-class VertexShaderPos {
- public:
-  VertexShaderPos();
-
-  void Init(gpu::gles2::GLES2Interface* context,
-            unsigned program,
-            int* base_uniform_index);
-  std::string GetShaderString() const;
-  static std::string GetShaderHead();
-  static std::string GetShaderBody();
-
   int matrix_location() const { return matrix_location_; }
 
- private:
-  int matrix_location_;
-
-  DISALLOW_COPY_AND_ASSIGN(VertexShaderPos);
-};
-
-class VertexShaderPosTexIdentity {
- public:
-  void Init(gpu::gles2::GLES2Interface* context,
-            unsigned program,
-            int* base_uniform_index) {}
-  std::string GetShaderString() const;
-  static std::string GetShaderHead();
-  static std::string GetShaderBody();
-};
-
-class VertexShaderPosTexTransform {
- public:
-  VertexShaderPosTexTransform();
-
-  void Init(gpu::gles2::GLES2Interface* context,
-            unsigned program,
-            int* base_uniform_index);
-  std::string GetShaderString() const;
-  static std::string GetShaderHead();
-  static std::string GetShaderBody();
-  void FillLocations(ShaderLocations* locations) const;
-
-  int matrix_location() const { return matrix_location_; }
-  int tex_transform_location() const { return tex_transform_location_; }
   int vertex_opacity_location() const { return vertex_opacity_location_; }
 
- private:
-  int matrix_location_;
-  int tex_transform_location_;
-  int vertex_opacity_location_;
-
-  DISALLOW_COPY_AND_ASSIGN(VertexShaderPosTexTransform);
-};
-
-class VertexShaderQuad {
- public:
-  VertexShaderQuad();
-
-  void Init(gpu::gles2::GLES2Interface* context,
-           unsigned program,
-           int* base_uniform_index);
-  std::string GetShaderString() const;
-  static std::string GetShaderHead();
-  static std::string GetShaderBody();
-
-  int matrix_location() const { return matrix_location_; }
-  int viewport_location() const { return -1; }
-  int quad_location() const { return quad_location_; }
-  int edge_location() const { return -1; }
-
- private:
-  int matrix_location_;
-  int quad_location_;
-
-  DISALLOW_COPY_AND_ASSIGN(VertexShaderQuad);
-};
-
-class VertexShaderQuadAA {
- public:
-  VertexShaderQuadAA();
-
-  void Init(gpu::gles2::GLES2Interface* context,
-           unsigned program,
-           int* base_uniform_index);
-  std::string GetShaderString() const;
-  static std::string GetShaderHead();
-  static std::string GetShaderBody();
-
-  int matrix_location() const { return matrix_location_; }
   int viewport_location() const { return viewport_location_; }
-  int quad_location() const { return quad_location_; }
   int edge_location() const { return edge_location_; }
 
- private:
-  int matrix_location_;
-  int viewport_location_;
-  int quad_location_;
-  int edge_location_;
+  int quad_location() const { return quad_location_; }
 
-  DISALLOW_COPY_AND_ASSIGN(VertexShaderQuadAA);
+ protected:
+  virtual std::string GetShaderSource() const = 0;
+
+  bool has_tex_transform_ = false;
+  int tex_transform_location_ = -1;
+
+  bool has_vertex_tex_transform_ = false;
+  int vertex_tex_transform_location_ = -1;
+
+  bool has_tex_matrix_ = false;
+  int tex_matrix_location_ = -1;
+
+  bool has_ya_uv_tex_scale_offset_ = false;
+  int ya_tex_scale_location_ = -1;
+  int ya_tex_offset_location_ = -1;
+  int uv_tex_scale_location_ = -1;
+  int uv_tex_offset_location_ = -1;
+
+  bool has_matrix_ = false;
+  int matrix_location_ = -1;
+
+  bool has_vertex_opacity_ = false;
+  int vertex_opacity_location_ = -1;
+
+  bool has_aa_ = false;
+  int viewport_location_ = -1;
+  int edge_location_ = -1;
+
+  bool has_quad_ = false;
+  int quad_location_ = -1;
 };
 
-
-class VertexShaderQuadTexTransformAA {
+class VertexShaderPosTex : public VertexShaderBase {
  public:
-  VertexShaderQuadTexTransformAA();
-
-  void Init(gpu::gles2::GLES2Interface* context,
-           unsigned program,
-           int* base_uniform_index);
-  std::string GetShaderString() const;
-  static std::string GetShaderHead();
-  static std::string GetShaderBody();
-  void FillLocations(ShaderLocations* locations) const;
-
-  int matrix_location() const { return matrix_location_; }
-  int viewport_location() const { return viewport_location_; }
-  int quad_location() const { return quad_location_; }
-  int edge_location() const { return edge_location_; }
-  int tex_transform_location() const { return tex_transform_location_; }
-
- private:
-  int matrix_location_;
-  int viewport_location_;
-  int quad_location_;
-  int edge_location_;
-  int tex_transform_location_;
-
-  DISALLOW_COPY_AND_ASSIGN(VertexShaderQuadTexTransformAA);
+  VertexShaderPosTex() { has_matrix_ = true; }
+  std::string GetShaderSource() const override;
 };
 
-class VertexShaderTile {
+class VertexShaderPosTexYUVStretchOffset : public VertexShaderBase {
  public:
-  VertexShaderTile();
-
-  void Init(gpu::gles2::GLES2Interface* context,
-            unsigned program,
-            int* base_uniform_index);
-  std::string GetShaderString() const;
-  static std::string GetShaderHead();
-  static std::string GetShaderBody();
-
-  int matrix_location() const { return matrix_location_; }
-  int viewport_location() const { return -1; }
-  int quad_location() const { return quad_location_; }
-  int edge_location() const { return -1; }
-  int vertex_tex_transform_location() const {
-    return vertex_tex_transform_location_;
+  VertexShaderPosTexYUVStretchOffset() {
+    has_matrix_ = true;
+    has_ya_uv_tex_scale_offset_ = true;
   }
-
- private:
-  int matrix_location_;
-  int quad_location_;
-  int vertex_tex_transform_location_;
-
-  DISALLOW_COPY_AND_ASSIGN(VertexShaderTile);
+  std::string GetShaderSource() const override;
 };
 
-class VertexShaderTileAA {
+class VertexShaderPos : public VertexShaderBase {
  public:
-  VertexShaderTileAA();
+  VertexShaderPos() { has_matrix_ = true; }
+  std::string GetShaderSource() const override;
+};
 
-  void Init(gpu::gles2::GLES2Interface* context,
-            unsigned program,
-            int* base_uniform_index);
-  std::string GetShaderString() const;
-  static std::string GetShaderHead();
-  static std::string GetShaderBody();
+class VertexShaderPosTexIdentity : public VertexShaderBase {
+ public:
+  std::string GetShaderSource() const override;
+};
 
-  int matrix_location() const { return matrix_location_; }
-  int viewport_location() const { return viewport_location_; }
-  int quad_location() const { return quad_location_; }
-  int edge_location() const { return edge_location_; }
-  int vertex_tex_transform_location() const {
-    return vertex_tex_transform_location_;
+class VertexShaderPosTexTransform : public VertexShaderBase {
+ public:
+  VertexShaderPosTexTransform() {
+    has_matrix_ = true;
+    has_tex_transform_ = true;
+    has_vertex_opacity_ = true;
   }
-
- private:
-  int matrix_location_;
-  int viewport_location_;
-  int quad_location_;
-  int edge_location_;
-  int vertex_tex_transform_location_;
-
-  DISALLOW_COPY_AND_ASSIGN(VertexShaderTileAA);
+  std::string GetShaderSource() const override;
 };
 
-class VertexShaderVideoTransform {
+class VertexShaderQuad : public VertexShaderBase {
  public:
-  VertexShaderVideoTransform();
+  VertexShaderQuad() {
+    has_matrix_ = true;
+    has_quad_ = true;
+  }
+  std::string GetShaderSource() const override;
+};
 
-  void Init(gpu::gles2::GLES2Interface* context,
-            unsigned program,
-            int* base_uniform_index);
-  std::string GetShaderString() const;
-  static std::string GetShaderHead();
-  static std::string GetShaderBody();
+class VertexShaderQuadAA : public VertexShaderBase {
+ public:
+  VertexShaderQuadAA() {
+    has_matrix_ = true;
+    has_aa_ = true;
+    has_quad_ = true;
+  }
+  std::string GetShaderSource() const override;
+};
 
-  int matrix_location() const { return matrix_location_; }
-  int tex_matrix_location() const { return tex_matrix_location_; }
+class VertexShaderQuadTexTransformAA : public VertexShaderBase {
+ public:
+  VertexShaderQuadTexTransformAA() {
+    has_matrix_ = true;
+    has_aa_ = true;
+    has_quad_ = true;
+    has_tex_transform_ = true;
+  }
+  std::string GetShaderSource() const override;
+};
 
- private:
-  int matrix_location_;
-  int tex_matrix_location_;
+class VertexShaderTile : public VertexShaderBase {
+ public:
+  VertexShaderTile() {
+    has_matrix_ = true;
+    has_quad_ = true;
+    has_vertex_tex_transform_ = true;
+  }
+  std::string GetShaderSource() const override;
+};
 
-  DISALLOW_COPY_AND_ASSIGN(VertexShaderVideoTransform);
+class VertexShaderTileAA : public VertexShaderBase {
+ public:
+  VertexShaderTileAA() {
+    has_matrix_ = true;
+    has_quad_ = true;
+    has_vertex_tex_transform_ = true;
+    has_aa_ = true;
+  }
+  std::string GetShaderSource() const override;
+};
+
+class VertexShaderVideoTransform : public VertexShaderBase {
+ public:
+  VertexShaderVideoTransform() {
+    has_matrix_ = true;
+    has_tex_matrix_ = true;
+  }
+  std::string GetShaderSource() const override;
 };
 
 class FragmentShaderBase {
diff --git a/chrome/android/java/res/layout/multiline_spinner_item.xml b/chrome/android/java/res/layout/multiline_spinner_item.xml
index e83c6d8..1fecc30 100644
--- a/chrome/android/java/res/layout/multiline_spinner_item.xml
+++ b/chrome/android/java/res/layout/multiline_spinner_item.xml
@@ -5,8 +5,10 @@
 -->
 
 <TextView xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/spinner_item"
     style="?android:attr/spinnerItemStyle"
     android:singleLine="false"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
+    android:paddingEnd="24dp"
     android:textAlignment="inherit" />
diff --git a/chrome/android/java/res/layout/payment_request_dropdown_item.xml b/chrome/android/java/res/layout/payment_request_dropdown_item.xml
new file mode 100644
index 0000000..67be520
--- /dev/null
+++ b/chrome/android/java/res/layout/payment_request_dropdown_item.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+	android:layout_width="match_parent"
+	android:layout_height="wrap_content">
+
+	<TextView
+		android:id="@+id/spinner_item"
+		android:layout_width="match_parent"
+		android:layout_height="wrap_content"
+		android:paddingStart="24dp"
+		android:paddingEnd="24dp"
+		android:minHeight="48dp"
+		style="?android:attr/spinnerDropDownItemStyle" />
+
+</FrameLayout>
\ No newline at end of file
diff --git a/chrome/android/java/res/layout/payment_request_editor_dropdown.xml b/chrome/android/java/res/layout/payment_request_editor_dropdown.xml
index 0464f5d..c5d86f8 100644
--- a/chrome/android/java/res/layout/payment_request_editor_dropdown.xml
+++ b/chrome/android/java/res/layout/payment_request_editor_dropdown.xml
@@ -21,9 +21,11 @@
 
     <android.support.v7.widget.AppCompatSpinner
             android:id="@+id/spinner"
-            android:layout_width="fill_parent"
+            android:layout_width="match_parent"
             android:layout_height="wrap_content"
-            android:layout_marginTop="@dimen/pref_autofill_field_top_margin" />
+            android:layout_marginTop="@dimen/pref_autofill_field_top_margin"
+            android:padding="0dp"
+            android:dropDownWidth="match_parent" />
 
     <View style="@style/PreferenceSpinnerUnderlineView" />
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
index 4701a6b..22bf1655 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
@@ -114,6 +114,7 @@
 import org.chromium.content.browser.crypto.CipherFactory;
 import org.chromium.content.common.ContentSwitches;
 import org.chromium.content_public.browser.LoadUrlParams;
+import org.chromium.content_public.common.Referrer;
 import org.chromium.ui.base.DeviceFormFactor;
 import org.chromium.ui.base.PageTransition;
 import org.chromium.ui.base.WindowAndroid;
@@ -821,6 +822,10 @@
                         loadUrlParams.setHasUserGesture(hasUserGesture);
                         loadUrlParams.setTransitionType(IntentHandler.getTransitionTypeFromIntent(
                                 getApplicationContext(), intent, transitionType));
+                        if (referer != null) {
+                            loadUrlParams.setReferrer(
+                                    new Referrer(referer, Referrer.REFERRER_POLICY_DEFAULT));
+                        }
                         currentTab.loadUrl(loadUrlParams);
                         RecordUserAction.record("MobileTabClobbered");
                     } else {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/history/BrowsingHistoryBridge.java b/chrome/android/java/src/org/chromium/chrome/browser/history/BrowsingHistoryBridge.java
index c9f0e1d..e2cd124 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/history/BrowsingHistoryBridge.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/history/BrowsingHistoryBridge.java
@@ -11,50 +11,22 @@
 import java.util.List;
 
 /** The JNI bridge for Android to fetch and manipulate browsing history. */
-public class BrowsingHistoryBridge {
-
-    /**
-     * Observer to be notified of browsing history events.
-     */
-    public interface BrowsingHistoryObserver {
-        /**
-         * Called after {@link BrowsingHistoryBridge#queryHistory(String, long)} is complete.
-         * @param items The items that matched the #queryHistory() parameters.
-         * @param hasMorePotentialMatches Whether there are more items that match the query text.
-         *                                This will be false once the entire local history database
-         *                                has been searched.
-         */
-        public void onQueryHistoryComplete(List<HistoryItem> items,
-                boolean hasMorePotentialMatches);
-
-        /**
-         * Called when history has been deleted through something other than a call to
-         * BrowsingHistoryBridge#removeItems(). For example, if two instances of the history page
-         * are open and the user removes items in one instance, the other instance will be notified
-         * via this method.
-         */
-        public void onHistoryDeleted();
-
-        /**
-         * Called after querying history to indicate whether other forms of browsing history were
-         * found.
-         * @param hasOtherForms Whether other forms of browsing history were found.
-         * @param hasSyncedResults Whether synced results were found.
-         */
-        public void hasOtherFormsOfBrowsingData(boolean hasOtherForms, boolean hasSyncedResults);
-    }
-
-    private final BrowsingHistoryObserver mObserver;
-
+public class BrowsingHistoryBridge implements HistoryProvider {
+    private BrowsingHistoryObserver mObserver;
     private long mNativeHistoryBridge;
     private boolean mRemovingItems;
     private boolean mHasPendingRemoveRequest;
 
-    public BrowsingHistoryBridge(BrowsingHistoryObserver observer) {
+    public BrowsingHistoryBridge() {
         mNativeHistoryBridge = nativeInit(Profile.getLastUsedProfile());
+    }
+
+    @Override
+    public void setObserver(BrowsingHistoryObserver observer) {
         mObserver = observer;
     }
 
+    @Override
     public void destroy() {
         if (mNativeHistoryBridge != 0) {
             nativeDestroy(mNativeHistoryBridge);
@@ -62,29 +34,17 @@
         }
     }
 
-    /**
-     * Query browsing history. Only one query may be in-flight at any time. See
-     * BrowsingHistoryService::QueryHistory.
-     * @param query The query search text. May be empty.
-     * @param endQueryTime The end of the time range to search. A value of 0 indicates that there
-     *                     is no limit on the end time. See the native QueryOptions.
-     */
+    @Override
     public void queryHistory(String query, long endQueryTime) {
         nativeQueryHistory(mNativeHistoryBridge, new ArrayList<HistoryItem>(), query, endQueryTime);
     }
 
-    /**
-     * Adds the HistoryItem to the list of items being removed. The removal will not be committed
-     * until {@link #removeItems()} is called.
-     * @param item The item to mark for removal.
-     */
+    @Override
     public void markItemForRemoval(HistoryItem item) {
         nativeMarkItemForRemoval(mNativeHistoryBridge, item.getUrl(), item.getTimestamps());
     }
 
-    /**
-     * Removes all items that have been marked for removal through #markItemForRemoval().
-     */
+    @Override
     public void removeItems() {
         // Only one remove request may be in-flight at any given time. If items are currently being
         // removed, queue the new request and return early.
@@ -105,7 +65,7 @@
 
     @CalledByNative
     public void onQueryHistoryComplete(List<HistoryItem> items, boolean hasMorePotentialMatches) {
-        mObserver.onQueryHistoryComplete(items, hasMorePotentialMatches);
+        if (mObserver != null) mObserver.onQueryHistoryComplete(items, hasMorePotentialMatches);
     }
 
     @CalledByNative
@@ -123,12 +83,14 @@
 
     @CalledByNative
     public void onHistoryDeleted() {
-        mObserver.onHistoryDeleted();
+        if (mObserver != null) mObserver.onHistoryDeleted();
     }
 
     @CalledByNative
     public void hasOtherFormsOfBrowsingData(boolean hasOtherForms, boolean hasSyncedResults) {
-        mObserver.hasOtherFormsOfBrowsingData(hasOtherForms, hasSyncedResults);
+        if (mObserver != null) {
+            mObserver.hasOtherFormsOfBrowsingData(hasOtherForms, hasSyncedResults);
+        }
     }
 
     private native long nativeInit(Profile profile);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/history/HistoryActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/history/HistoryActivity.java
index e2f71f3..5d181ac 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/history/HistoryActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/history/HistoryActivity.java
@@ -6,6 +6,7 @@
 
 import android.os.Bundle;
 
+import org.chromium.base.VisibleForTesting;
 import org.chromium.chrome.browser.SynchronousInitializationActivity;
 
 /**
@@ -28,4 +29,9 @@
         mHistoryManager = null;
         super.onDestroy();
     }
+
+    @VisibleForTesting
+    HistoryManager getHistoryManagerForTests() {
+        return mHistoryManager;
+    }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/history/HistoryAdapter.java b/chrome/android/java/src/org/chromium/chrome/browser/history/HistoryAdapter.java
index 657a02f..10862e3c 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/history/HistoryAdapter.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/history/HistoryAdapter.java
@@ -15,7 +15,7 @@
 
 import org.chromium.base.ContextUtils;
 import org.chromium.chrome.R;
-import org.chromium.chrome.browser.history.BrowsingHistoryBridge.BrowsingHistoryObserver;
+import org.chromium.chrome.browser.history.HistoryProvider.BrowsingHistoryObserver;
 import org.chromium.chrome.browser.widget.DateDividedAdapter;
 import org.chromium.chrome.browser.widget.selection.SelectableItemViewHolder;
 import org.chromium.chrome.browser.widget.selection.SelectionDelegate;
@@ -37,7 +37,7 @@
     private static final String GOOGLE_HISTORY_LINK = "history.google.com";
 
     private final SelectionDelegate<HistoryItem> mSelectionDelegate;
-    private final BrowsingHistoryBridge mBridge;
+    private final HistoryProvider mHistoryProvider;
     private final HistoryManager mManager;
 
     private TextView mSignedInNotSyncedTextView;
@@ -56,10 +56,12 @@
     private long mNextQueryEndTime;
     private String mQueryText = EMPTY_QUERY;
 
-    public HistoryAdapter(SelectionDelegate<HistoryItem> delegate, HistoryManager manager) {
+    public HistoryAdapter(SelectionDelegate<HistoryItem> delegate, HistoryManager manager,
+            HistoryProvider provider) {
         setHasStableIds(true);
         mSelectionDelegate = delegate;
-        mBridge = new BrowsingHistoryBridge(this);
+        mHistoryProvider = provider;
+        mHistoryProvider.setObserver(this);
         mManager = manager;
     }
 
@@ -67,7 +69,7 @@
      * Called when the activity/native page is destroyed.
      */
     public void onDestroyed() {
-        mBridge.destroy();
+        mHistoryProvider.destroy();
         mIsDestroyed = true;
     }
 
@@ -79,7 +81,7 @@
         mIsLoadingItems = true;
         mNextQueryEndTime = 0;
         mClearOnNextQueryComplete = true;
-        mBridge.queryHistory(mQueryText, mNextQueryEndTime);
+        mHistoryProvider.queryHistory(mQueryText, mNextQueryEndTime);
     }
 
     /**
@@ -92,7 +94,7 @@
         mIsLoadingItems = true;
         addFooter();
         notifyDataSetChanged();
-        mBridge.queryHistory(mQueryText, mNextQueryEndTime);
+        mHistoryProvider.queryHistory(mQueryText, mNextQueryEndTime);
     }
 
     /**
@@ -111,7 +113,7 @@
         mNextQueryEndTime = 0;
         mIsSearching = true;
         mClearOnNextQueryComplete = true;
-        mBridge.queryHistory(mQueryText, mNextQueryEndTime);
+        mHistoryProvider.queryHistory(mQueryText, mNextQueryEndTime);
     }
 
     /**
@@ -132,14 +134,19 @@
      */
     public void markItemForRemoval(HistoryItem item) {
         removeItem(item);
-        mBridge.markItemForRemoval(item);
+        mHistoryProvider.markItemForRemoval(item);
+
+        // If there is only one item left, remove the header so the empty view will be displayed.
+        if (getItemCount() == 1) {
+            removeHeader();
+        }
     }
 
     /**
      * Removes all items that have been marked for removal through #markItemForRemoval().
      */
     public void removeItems() {
-        mBridge.removeItems();
+        mHistoryProvider.removeItems();
     }
 
     @Override
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/history/HistoryItem.java b/chrome/android/java/src/org/chromium/chrome/browser/history/HistoryItem.java
index 55263e1e..a89dd9c1 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/history/HistoryItem.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/history/HistoryItem.java
@@ -85,13 +85,19 @@
      * Navigates a tab to this item's URL.
      */
     public void open() {
-        if (mManager != null) mManager.openUrl(mUrl, null, false);
+        if (mManager != null) {
+            mManager.recordUserActionWithOptionalSearch("OpenItem");
+            mManager.openUrl(mUrl, null, false);
+        }
     }
 
     /**
      * Removes this item.
      */
     public void remove() {
-        if (mManager != null) mManager.removeItem(this);
+        if (mManager != null) {
+            mManager.recordUserActionWithOptionalSearch("RemoveItem");
+            mManager.removeItem(this);
+        }
     }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/history/HistoryManager.java b/chrome/android/java/src/org/chromium/chrome/browser/history/HistoryManager.java
index 51ae37a5..8964db77 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/history/HistoryManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/history/HistoryManager.java
@@ -11,6 +11,7 @@
 import android.content.Intent;
 import android.net.Uri;
 import android.provider.Browser;
+import android.support.annotation.VisibleForTesting;
 import android.support.v7.widget.LinearLayoutManager;
 import android.support.v7.widget.RecyclerView;
 import android.support.v7.widget.RecyclerView.OnScrollListener;
@@ -18,8 +19,11 @@
 import android.view.LayoutInflater;
 import android.view.MenuItem;
 import android.view.ViewGroup;
+import android.widget.TextView;
 
 import org.chromium.base.ContextUtils;
+import org.chromium.base.metrics.RecordHistogram;
+import org.chromium.base.metrics.RecordUserAction;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.IntentHandler;
 import org.chromium.chrome.browser.document.ChromeLauncherActivity;
@@ -41,14 +45,20 @@
 public class HistoryManager implements OnMenuItemClickListener {
     private static final int FAVICON_MAX_CACHE_SIZE_BYTES = 10 * 1024 * 1024; // 10MB
     private static final int MEGABYTES_TO_BYTES =  1024 * 1024;
+    private static final String METRICS_PREFIX = "Android.HistoryPage.";
+
+    private static HistoryProvider sProviderForTests;
 
     private final Activity mActivity;
     private final SelectableListLayout<HistoryItem> mSelectableListLayout;
     private final HistoryAdapter mHistoryAdapter;
     private final SelectionDelegate<HistoryItem> mSelectionDelegate;
     private final HistoryManagerToolbar mToolbar;
+    private final TextView mEmptyView;
     private LargeIconBridge mLargeIconBridge;
 
+    private boolean mIsSearching;
+
     /**
      * Creates a new HistoryManager.
      * @param activity The Activity associated with the HistoryManager.
@@ -57,7 +67,8 @@
         mActivity = activity;
 
         mSelectionDelegate = new SelectionDelegate<>();
-        mHistoryAdapter = new HistoryAdapter(mSelectionDelegate, this);
+        mHistoryAdapter = new HistoryAdapter(mSelectionDelegate, this,
+                sProviderForTests != null ? sProviderForTests : new BrowsingHistoryBridge());
 
         mSelectableListLayout =
                 (SelectableListLayout<HistoryItem>) LayoutInflater.from(activity).inflate(
@@ -67,7 +78,7 @@
                 R.layout.history_toolbar, mSelectionDelegate, R.string.menu_history, null,
                 R.id.normal_menu_group, R.id.selection_mode_menu_group, this);
         mToolbar.setManager(this);
-        mSelectableListLayout.initializeEmptyView(
+        mEmptyView = mSelectableListLayout.initializeEmptyView(
                 TintedDrawable.constructTintedDrawable(mActivity.getResources(),
                         R.drawable.history_large),
                 R.string.history_manager_empty);
@@ -85,6 +96,7 @@
                 if (layoutManager.findLastVisibleItemPosition()
                         > (mHistoryAdapter.getItemCount() - 25)) {
                     mHistoryAdapter.loadMoreItems();
+                    recordUserActionWithOptionalSearch("LoadMoreOnScroll");
                 }
             }});
 
@@ -94,6 +106,8 @@
         int maxSize = Math.min((activityManager.getMemoryClass() / 4) * MEGABYTES_TO_BYTES,
                 FAVICON_MAX_CACHE_SIZE_BYTES);
         mLargeIconBridge.createCache(maxSize);
+
+        recordUserAction("Show");
     }
 
     @Override
@@ -110,6 +124,9 @@
             mSelectionDelegate.clearSelection();
             return true;
         } else if (item.getItemId() == R.id.selection_mode_delete_menu_id) {
+            recordSelectionCountHistorgram("Remove");
+            recordUserActionWithOptionalSearch("RemoveSelected");
+
             for (HistoryItem historyItem : mSelectionDelegate.getSelectedItems()) {
                 mHistoryAdapter.markItemForRemoval(historyItem);
             }
@@ -119,6 +136,9 @@
         } else if (item.getItemId() == R.id.search_menu_id) {
             mToolbar.showSearchView();
             mSelectableListLayout.setEmptyViewText(R.string.history_manager_no_results);
+            recordUserAction("Search");
+            mIsSearching = true;
+            return true;
         }
         return false;
     }
@@ -194,6 +214,7 @@
      * Opens the clear browsing data preference.
      */
     public void openClearBrowsingDataPreference() {
+        recordUserAction("ClearBrowsingData");
         Intent intent = PreferencesLauncher.createIntentForSettingsPage(mActivity,
                 ClearBrowsingDataPreferences.class.getName());
         IntentUtils.safeStartActivity(mActivity, intent);
@@ -213,6 +234,7 @@
     public void onEndSearch() {
         mHistoryAdapter.onEndSearch();
         mSelectableListLayout.setEmptyViewText(R.string.history_manager_empty);
+        mIsSearching = false;
     }
 
     /**
@@ -223,8 +245,64 @@
     }
 
     private void openItemsInNewTabs(List<HistoryItem> items, boolean isIncognito) {
+        recordSelectionCountHistorgram("Open");
+        recordUserActionWithOptionalSearch("OpenSelected" + (isIncognito ? "Incognito" : ""));
+
         for (HistoryItem item : items) {
             openUrl(item.getUrl(), isIncognito, true);
         }
     }
+
+    /**
+     * Sets a {@link HistoryProvider} that is used in place of a real one.
+     */
+    @VisibleForTesting
+    public static void setProviderForTests(HistoryProvider provider) {
+        sProviderForTests = provider;
+    }
+
+    @VisibleForTesting
+    SelectionDelegate<HistoryItem> getSelectionDelegateForTests() {
+        return mSelectionDelegate;
+    }
+
+    @VisibleForTesting
+    HistoryManagerToolbar getToolbarForTests() {
+        return mToolbar;
+    }
+
+    @VisibleForTesting
+    TextView getEmptyViewForTests() {
+        return mEmptyView;
+    }
+
+    @VisibleForTesting
+    HistoryAdapter getAdapterForTests() {
+        return mHistoryAdapter;
+    }
+
+    /**
+     * @param action The user action string to record.
+     */
+    static void recordUserAction(String action) {
+        RecordUserAction.record(METRICS_PREFIX + action);
+    }
+
+    /**
+     * Records the user action with "Search" prepended if the user is currently searching.
+     * @param action The user action string to record.
+     */
+    void recordUserActionWithOptionalSearch(String action) {
+        recordUserAction((mIsSearching ? "Search." : "") + action);
+    }
+
+    /**
+     * Records the number of selected items when a multi-select action is performed.
+     * @param action The multi-select action that was performed.
+     */
+    private void recordSelectionCountHistorgram(String action) {
+        List<HistoryItem> selectedItems = mSelectionDelegate.getSelectedItems();
+        RecordHistogram.recordCount100Histogram(
+                METRICS_PREFIX + action + "Selected", selectedItems.size());
+    }
 }
\ No newline at end of file
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/history/HistoryManagerToolbar.java b/chrome/android/java/src/org/chromium/chrome/browser/history/HistoryManagerToolbar.java
index b04194c0..03df352 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/history/HistoryManagerToolbar.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/history/HistoryManagerToolbar.java
@@ -117,7 +117,13 @@
 
     @Override
     public void onSelectionStateChange(List<HistoryItem> selectedItems) {
+        boolean wasSelectionEnabled = mIsSelectionEnabled;
         super.onSelectionStateChange(selectedItems);
+
+        if (!wasSelectionEnabled && mIsSelectionEnabled) {
+            mManager.recordUserActionWithOptionalSearch("SelectionEstablished");
+        }
+
         if (!mIsSearching) return;
 
         if (mIsSelectionEnabled) {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/history/HistoryProvider.java b/chrome/android/java/src/org/chromium/chrome/browser/history/HistoryProvider.java
new file mode 100644
index 0000000..b90a9d6e
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/history/HistoryProvider.java
@@ -0,0 +1,74 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.history;
+
+import java.util.List;
+
+/**
+ * Provides methods needed for querying and managing browsing history.
+ */
+public interface HistoryProvider {
+    /**
+     * Observer to be notified of browsing history events.
+     */
+    public interface BrowsingHistoryObserver {
+        /**
+         * Called after {@link BrowsingHistoryBridge#queryHistory(String, long)} is complete.
+         * @param items The items that matched the #queryHistory() parameters.
+         * @param hasMorePotentialMatches Whether there are more items that match the query text.
+         *                                This will be false once the entire local history database
+         *                                has been searched.
+         */
+        void onQueryHistoryComplete(List<HistoryItem> items,
+                boolean hasMorePotentialMatches);
+
+        /**
+         * Called when history has been deleted through something other than a call to
+         * BrowsingHistoryBridge#removeItems(). For example, if two instances of the history page
+         * are open and the user removes items in one instance, the other instance will be notified
+         * via this method.
+         */
+        void onHistoryDeleted();
+
+        /**
+         * Called after querying history to indicate whether other forms of browsing history were
+         * found.
+         * @param hasOtherForms Whether other forms of browsing history were found.
+         * @param hasSyncedResults Whether synced results were found.
+         */
+        void hasOtherFormsOfBrowsingData(boolean hasOtherForms, boolean hasSyncedResults);
+    }
+
+    /**
+     * Sets the {@link BrowsingHistoryObserver} to be notified of browsing history events.
+     */
+    void setObserver(BrowsingHistoryObserver observer);
+
+    /**
+     * Query browsing history. Only one query may be in-flight at any time. See
+     * BrowsingHistoryService::QueryHistory.
+     * @param query The query search text. May be empty.
+     * @param endQueryTime The end of the time range to search. A value of 0 indicates that there
+     *                     is no limit on the end time. See the native QueryOptions.
+     */
+    void queryHistory(String query, long endQueryTime);
+
+    /**
+     * Adds the HistoryItem to the list of items being removed. The removal will not be committed
+     * until {@link #removeItems()} is called.
+     * @param item The item to mark for removal.
+     */
+    void markItemForRemoval(HistoryItem item);
+
+    /**
+     * Removes all items that have been marked for removal through #markItemForRemoval().
+     */
+    void removeItems();
+
+    /**
+     * Destroys the HistoryProvider.
+     */
+    void destroy();
+}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/UrlBar.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/UrlBar.java
index d97f1c4..0479f00 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/UrlBar.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/UrlBar.java
@@ -32,6 +32,7 @@
 import android.view.inputmethod.InputConnectionWrapper;
 
 import org.chromium.base.ApiCompatibilityUtils;
+import org.chromium.base.Log;
 import org.chromium.base.SysUtils;
 import org.chromium.base.VisibleForTesting;
 import org.chromium.chrome.R;
@@ -52,7 +53,9 @@
  * The URL text entry view for the Omnibox.
  */
 public class UrlBar extends VerticallyFixedEditText {
-    private static final String TAG = "UrlBar";
+    private static final String TAG = "cr_UrlBar";
+
+    private static final boolean DEBUG = false;
 
     // TextView becomes very slow on long strings, so we limit maximum length
     // of what is displayed to the user, see limitDisplayableLength().
@@ -349,6 +352,7 @@
 
     @Override
     public void onBeginBatchEdit() {
+        if (DEBUG) Log.i(TAG, "onBeginBatchEdit");
         mBeforeBatchEditAutocompleteIndex = getText().getSpanStart(mAutocompleteSpan);
         mBeforeBatchEditFullText = getText().toString();
 
@@ -359,6 +363,7 @@
 
     @Override
     public void onEndBatchEdit() {
+        if (DEBUG) Log.i(TAG, "onEndBatchEdit");
         super.onEndBatchEdit();
         mInBatchEditMode = false;
         limitDisplayableLength();
@@ -391,6 +396,7 @@
 
     @Override
     protected void onSelectionChanged(int selStart, int selEnd) {
+        if (DEBUG) Log.i(TAG, "onSelectionChanged -- selStart: %d, selEnd: %d", selStart, selEnd);
         if (!mInBatchEditMode) {
             int beforeTextLength = getText().length();
             if (validateSelection(selStart, selEnd)) {
@@ -414,7 +420,15 @@
     private boolean validateSelection(int selStart, int selEnd) {
         int spanStart = getText().getSpanStart(mAutocompleteSpan);
         int spanEnd = getText().getSpanEnd(mAutocompleteSpan);
+
+        if (DEBUG) {
+            Log.i(TAG, "validateSelection -- selStart: %d, selEnd: %d, spanStart: %d, spanEnd: %d",
+                    selStart, selEnd, spanStart, spanEnd);
+        }
+
         if (spanStart >= 0 && (spanStart != selStart || spanEnd != selEnd)) {
+            CharSequence previousAutocompleteText = mAutocompleteSpan.mAutocompleteText;
+
             // On selection changes, the autocomplete text has been accepted by the user or needs
             // to be deleted below.
             mAutocompleteSpan.clearSpan();
@@ -426,7 +440,10 @@
             // character appears, Chrome may decide to append some autocomplete, but the keyboard
             // will then remove this temporary character only while leaving the autocomplete text
             // alone.  See crbug/273763 for more details.
-            if (selEnd <= spanStart) getText().delete(spanStart, getText().length());
+            if (selEnd <= spanStart && TextUtils.equals(previousAutocompleteText,
+                    getText().subSequence(spanStart, getText().length()))) {
+                getText().delete(spanStart, getText().length());
+            }
             return true;
         }
         return false;
@@ -758,6 +775,10 @@
      * @param inlineAutocompleteText The suggested autocompletion for the user's text.
      */
     public void setAutocompleteText(CharSequence userText, CharSequence inlineAutocompleteText) {
+        if (DEBUG) {
+            Log.i(TAG, "setAutocompleteText -- userText: %s, inlineAutocompleteText: %s",
+                    userText, inlineAutocompleteText);
+        }
         boolean emptyAutocomplete = TextUtils.isEmpty(inlineAutocompleteText);
 
         if (!emptyAutocomplete) mDisableTextScrollingFromAutocomplete = true;
@@ -842,6 +863,11 @@
 
     @Override
     protected void onTextChanged(CharSequence text, int start, int lengthBefore, int lengthAfter) {
+        if (DEBUG) {
+            Log.i(TAG, "onTextChanged -- text: %s, start: %d, lengthBefore: %d, lengthAfter: %d",
+                    text, start, lengthBefore, lengthAfter);
+        }
+
         super.onTextChanged(text, start, lengthBefore, lengthAfter);
         if (!mInBatchEditMode) {
             limitDisplayableLength();
@@ -854,6 +880,8 @@
 
     @Override
     public void setText(CharSequence text, BufferType type) {
+        if (DEBUG) Log.i(TAG, "setText -- text: %s", text);
+
         mDisableTextScrollingFromAutocomplete = false;
 
         // Avoid setting the same text to the URL bar as it will mess up the scroll/cursor
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/payments/ui/BillingAddressAdapter.java b/chrome/android/java/src/org/chromium/chrome/browser/payments/ui/BillingAddressAdapter.java
index d765174..084e197 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/payments/ui/BillingAddressAdapter.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/payments/ui/BillingAddressAdapter.java
@@ -38,27 +38,32 @@
  *                                                         ..............
  */
 public class BillingAddressAdapter<T> extends ArrayAdapter<T> {
+    private final int mTextViewResourceId;
 
     /**
      * Creates an array adapter for which the last element is a hint that is not shown in the
      * expanded view and where the last shown element has a "+" icon on its left and has a blue
      * tint.
      *
-     * @param context  The current context.
-     * @param resource The resource ID for a layout file containing a layout to use when
-     *                 instantiating views.
-     * @param objects  The objects to represent in the ListView, the last of which will have a "+"
-     *                 icon on its left and will have a blue tint.
-     * @param hint     The element to be used as a hint when no element is selected. It is not taken
-     *                 into account in the count function and thus will not be displayed when in the
-     *                 expanded dropdown view.
+     * @param context            The current context.
+     * @param resource           The resource ID for a layout file containing a layout to use when
+     *                           instantiating views.
+     * @param textViewResourceId The id of the TextView within the layout resource to be populated.
+     * @param objects            The objects to represent in the ListView, the last of which will
+     *                           have a "+" icon on its left and will have a blue tint.
+     * @param hint               The element to be used as a hint when no element is selected. It is
+     *                           not taken into account in the count function and thus will not be
+     *                           displayed when in the expanded dropdown view.
      */
-    public BillingAddressAdapter(Context context, int resource, List<T> objects, T hint) {
+    public BillingAddressAdapter(
+            Context context, int resource, int textViewResourceId, List<T> objects, T hint) {
         // Make a copy of objects so the hint is not added to the original list.
-        super(context, resource, new ArrayList<T>(objects));
+        super(context, resource, textViewResourceId, new ArrayList<T>(objects));
         // The hint is added as the last element. It will not be shown when the dropdown is
         // expanded and not be taken into account in the getCount function.
         add(hint);
+
+        mTextViewResourceId = textViewResourceId;
     }
 
     @Override
@@ -70,32 +75,58 @@
 
     @Override
     public View getDropDownView(int position, View convertView, ViewGroup parent) {
-        View view;
+        TextView textView = convertView == null
+                ? null
+                : (TextView) convertView.findViewById(mTextViewResourceId);
+        if (textView != null) {
+            // Clear the possible changes for the first and last view.
+            ApiCompatibilityUtils.setPaddingRelative(convertView,
+                    ApiCompatibilityUtils.getPaddingStart(convertView), 0,
+                    ApiCompatibilityUtils.getPaddingEnd(convertView), 0);
+            textView.setCompoundDrawablesWithIntrinsicBounds(null, null, null, null);
+            ApiCompatibilityUtils.setTextAppearance(
+                    textView, android.R.style.TextAppearance_Widget_DropDownItem);
+        }
+        convertView = super.getDropDownView(position, convertView, parent);
 
-        // Add a "+" icon and a blue tint to the last element.
-        if (position == getCount() - 1) {
-            view = super.getDropDownView(position, convertView, parent);
-            TextView tv = (TextView) view;
+        if (position == 0) {
+            // Padding at the top of the dropdown.
+            ApiCompatibilityUtils.setPaddingRelative(convertView,
+                    ApiCompatibilityUtils.getPaddingStart(convertView),
+                    getContext().getResources().getDimensionPixelSize(
+                            R.dimen.payments_section_small_spacing),
+                    ApiCompatibilityUtils.getPaddingEnd(convertView),
+                    convertView.getPaddingBottom());
+        } else if (position == getCount() - 1) {
+            // Add a "+" icon and a blue tint to the last element.
             Resources resources = getContext().getResources();
+            if (textView == null) {
+                textView = (TextView) convertView.findViewById(mTextViewResourceId);
+            }
 
             // Create the "+" icon, put it left of the text and add appropriate padding.
-            tv.setCompoundDrawablesWithIntrinsicBounds(
+            textView.setCompoundDrawablesWithIntrinsicBounds(
                     TintedDrawable.constructTintedDrawable(
-                        resources, R.drawable.plus, R.color.light_active_color),
+                            resources, R.drawable.plus, R.color.light_active_color),
                     null, null, null);
-            tv.setCompoundDrawablePadding(
+            textView.setCompoundDrawablePadding(
                     resources.getDimensionPixelSize(R.dimen.payments_section_large_spacing));
 
             // Set the correct appearance, face and style for the text.
-            ApiCompatibilityUtils.setTextAppearance(tv, R.style.PaymentsUiSectionAddButtonLabel);
-            tv.setTypeface(Typeface.create(
-                    resources.getString(R.string.roboto_medium_typeface),
-                    R.integer.roboto_medium_textstyle));
-        } else {
-            // Don't use the recycled convertView, as it may have the style of the last element.
-            view = super.getDropDownView(position, null, parent);
+            ApiCompatibilityUtils.setTextAppearance(
+                    textView, R.style.PaymentsUiSectionAddButtonLabel);
+            textView.setTypeface(
+                    Typeface.create(resources.getString(R.string.roboto_medium_typeface),
+                            R.integer.roboto_medium_textstyle));
+
+            // Padding at the bottom of the dropdown.
+            ApiCompatibilityUtils.setPaddingRelative(convertView,
+                    ApiCompatibilityUtils.getPaddingStart(convertView), convertView.getPaddingTop(),
+                    ApiCompatibilityUtils.getPaddingEnd(convertView),
+                    getContext().getResources().getDimensionPixelSize(
+                            R.dimen.payments_section_small_spacing));
         }
 
-        return view;
+        return convertView;
     }
 }
\ No newline at end of file
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/payments/ui/EditorDropdownField.java b/chrome/android/java/src/org/chromium/chrome/browser/payments/ui/EditorDropdownField.java
index fcbed1f..52a78e5 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/payments/ui/EditorDropdownField.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/payments/ui/EditorDropdownField.java
@@ -59,9 +59,15 @@
         ArrayAdapter<DropdownKeyValue> adapter;
         if (mFieldModel.getHint() != null) {
             // Use the BillingAddressAdapter and pass it a hint to be displayed as default.
-            adapter = new BillingAddressAdapter<DropdownKeyValue>(
-                    context, R.layout.multiline_spinner_item, dropdownKeyValues,
+            adapter = new BillingAddressAdapter<DropdownKeyValue>(context,
+                    R.layout.multiline_spinner_item, R.id.spinner_item, dropdownKeyValues,
                     new DropdownKeyValue("", mFieldModel.getHint().toString()));
+            // Wrap the TextView in the dropdown popup around with a FrameLayout to display the text
+            // in multiple lines.
+            // Note that the TextView in the dropdown popup is displayed in a DropDownListView for
+            // the dropdown style Spinner and the DropDownListView sets to display TextView instance
+            // in a single line.
+            adapter.setDropDownViewResource(R.layout.payment_request_dropdown_item);
 
             // If no value is selected, select the hint entry which is the last item in the adapter.
             // Using getCount will not result in an out of bounds index because the hint value is
@@ -70,8 +76,8 @@
         } else {
             adapter = new ArrayAdapter<DropdownKeyValue>(
                     context, R.layout.multiline_spinner_item, dropdownKeyValues);
+            adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
         }
-        adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
 
         mDropdown = (Spinner) mLayout.findViewById(R.id.spinner);
         mDropdown.setTag(this);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/widget/DateDividedAdapter.java b/chrome/android/java/src/org/chromium/chrome/browser/widget/DateDividedAdapter.java
index ad4c281..ee707031 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/widget/DateDividedAdapter.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/widget/DateDividedAdapter.java
@@ -288,15 +288,20 @@
             }
         }
 
-        // Tell each group where they start in the list.
+        setGroupPositions();
+        notifyDataSetChanged();
+    }
+
+    /**
+     * Tells each group where they start in the list.
+     */
+    private void setGroupPositions() {
         int startIndex = 0;
         for (ItemGroup group : mGroups) {
             group.resetPosition();
             group.setPosition(startIndex);
             startIndex += group.size();
         }
-
-        notifyDataSetChanged();
     }
 
     /**
@@ -314,6 +319,27 @@
     }
 
     /**
+     * Removes the list headrer.
+     */
+    public void removeHeader() {
+        if (!mHasListHeader) return;
+
+        mGroups.remove(mGroups.first());
+        mSize--;
+        mHasListHeader = false;
+
+        setGroupPositions();
+        notifyDataSetChanged();
+    }
+
+    /**
+     * Whether the adapter has a list header.
+     */
+    public boolean hasListHeader() {
+        return mHasListHeader;
+    }
+
+    /**
      * Adds a footer as the last group in this adapter.
      */
     public void addFooter() {
@@ -456,6 +482,7 @@
             mSize--;
         }
 
+        setGroupPositions();
         notifyDataSetChanged();
     }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/widget/selection/SelectableItemViewHolder.java b/chrome/android/java/src/org/chromium/chrome/browser/widget/selection/SelectableItemViewHolder.java
index e80af00..0a202d1 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/widget/selection/SelectableItemViewHolder.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/widget/selection/SelectableItemViewHolder.java
@@ -4,19 +4,19 @@
 
 package org.chromium.chrome.browser.widget.selection;
 
-import android.support.v7.widget.RecyclerView;
+import android.support.v7.widget.RecyclerView.ViewHolder;
 import android.view.View;
 
 /**
  * An ViewHolder for a {@link SelectableItemView}.
- * @param <E> The type of the item associated with the SelectableItemView.
+ * @param <E> The type of the item associated with the {@link SelectableItemView}.
  */
-public class SelectableItemViewHolder<E> extends RecyclerView.ViewHolder {
+public class SelectableItemViewHolder<E> extends ViewHolder {
     private SelectableItemView<E> mItemView;
 
     /**
-     * @param itemView The SelectableItemView to be held by this ViewHolder.
-     * @param delegate The SelectionDelegate for the itemView.
+     * @param itemView The {@link SelectableItemView} to be held by this ViewHolder.
+     * @param delegate The {@link SelectionDelegate} for the itemView.
      */
     @SuppressWarnings("unchecked")
     public SelectableItemViewHolder(View itemView, SelectionDelegate<E> delegate) {
@@ -26,9 +26,17 @@
     }
 
     /**
-     * @param item The item to display in the SelectableItemView held by this ViewHolder.
+     * @param item The item to display in the {@link SelectableItemView} held by this
+     *             {@link ViewHolder}.
      */
     public void displayItem(E item) {
         mItemView.setItem(item);
     }
+
+    /**
+     * @return The {@link SelectableItemView} held by this ViewHolder.
+     */
+    public SelectableItemView<E> getItemView() {
+        return mItemView;
+    }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/widget/selection/SelectableListLayout.java b/chrome/android/java/src/org/chromium/chrome/browser/widget/selection/SelectableListLayout.java
index 464b5b5..3594397 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/widget/selection/SelectableListLayout.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/widget/selection/SelectableListLayout.java
@@ -139,10 +139,12 @@
      *
      * @param emptyDrawable The Drawable to show when the selectable list is empty.
      * @param emptyStringResId The string to show when the selectable list is empty.
+     * @return The {@link TextView} displayed when the list is empty.
      */
-    public void initializeEmptyView(Drawable emptyDrawable, int emptyStringResId) {
+    public TextView initializeEmptyView(Drawable emptyDrawable, int emptyStringResId) {
         mEmptyView.setCompoundDrawablesWithIntrinsicBounds(null, emptyDrawable, null, null);
         mEmptyView.setText(emptyStringResId);
+        return mEmptyView;
     }
 
     /**
diff --git a/chrome/android/java_sources.gni b/chrome/android/java_sources.gni
index d9e46e1..f91a05b 100644
--- a/chrome/android/java_sources.gni
+++ b/chrome/android/java_sources.gni
@@ -410,6 +410,7 @@
   "java/src/org/chromium/chrome/browser/history/HistoryManager.java",
   "java/src/org/chromium/chrome/browser/history/HistoryManagerToolbar.java",
   "java/src/org/chromium/chrome/browser/history/HistoryManagerUtils.java",
+  "java/src/org/chromium/chrome/browser/history/HistoryProvider.java",
   "java/src/org/chromium/chrome/browser/historyreport/DeltaFileEntry.java",
   "java/src/org/chromium/chrome/browser/historyreport/HistoryReportJniBridge.java",
   "java/src/org/chromium/chrome/browser/historyreport/SearchJniBridge.java",
@@ -1267,6 +1268,9 @@
   "javatests/src/org/chromium/chrome/browser/hardware_acceleration/ToastHWATest.java",
   "javatests/src/org/chromium/chrome/browser/hardware_acceleration/Utils.java",
   "javatests/src/org/chromium/chrome/browser/hardware_acceleration/WebappActivityHWATest.java",
+  "javatests/src/org/chromium/chrome/browser/history/HistoryActivityTest.java",
+  "javatests/src/org/chromium/chrome/browser/history/HistoryAdapterTest.java",
+  "javatests/src/org/chromium/chrome/browser/history/StubbedHistoryProvider.java",
   "javatests/src/org/chromium/chrome/browser/identity/SettingsSecureBasedIdentificationGeneratorTest.java",
   "javatests/src/org/chromium/chrome/browser/identity/UniqueIdentificationGeneratorFactoryTest.java",
   "javatests/src/org/chromium/chrome/browser/identity/UuidBasedUniqueIdentificationGeneratorTest.java",
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/SelectFileDialogTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/SelectFileDialogTest.java
index 0ae91db..add8ecc 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/SelectFileDialogTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/SelectFileDialogTest.java
@@ -32,6 +32,8 @@
             + "content=\"width=device-width, initial-scale=2.0, maximum-scale=2.0\" /></head>"
             + "<body><form action=\"about:blank\">"
             + "<input id=\"input_file\" type=\"file\" /><br/>"
+            + "<input id=\"input_text\" type=\"file\" accept=\"text/plain\" /><br/>"
+            + "<input id=\"input_any\" type=\"file\" accept=\"*/*\" /><br/>"
             + "<input id=\"input_file_multiple\" type=\"file\" multiple /><br />"
             + "<input id=\"input_image\" type=\"file\" accept=\"image/*\" capture /><br/>"
             + "<input id=\"input_audio\" type=\"file\" accept=\"audio/*\" capture />"
@@ -105,21 +107,60 @@
     @Feature({"TextInput", "Main"})
     @RetryOnFailure
     public void testSelectFileAndCancelRequest() throws Throwable {
-        DOMUtils.clickNode(this, mContentViewCore, "input_file");
-        CriteriaHelper.pollInstrumentationThread(new IntentSentCriteria());
-        assertEquals(Intent.ACTION_CHOOSER, mActivityWindowAndroidForTest.lastIntent.getAction());
-        resetActivityWindowAndroidForTest();
-
-        DOMUtils.clickNode(this, mContentViewCore, "input_file_multiple");
-        CriteriaHelper.pollInstrumentationThread(new IntentSentCriteria());
-        assertEquals(Intent.ACTION_CHOOSER, mActivityWindowAndroidForTest.lastIntent.getAction());
-        Intent contentIntent = (Intent)
-                mActivityWindowAndroidForTest.lastIntent.getParcelableExtra(Intent.EXTRA_INTENT);
-        assertNotNull(contentIntent);
-        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
-            assertTrue(contentIntent.hasExtra(Intent.EXTRA_ALLOW_MULTIPLE));
+        {
+            DOMUtils.clickNode(this, mContentViewCore, "input_file");
+            CriteriaHelper.pollInstrumentationThread(new IntentSentCriteria());
+            assertEquals(
+                    Intent.ACTION_CHOOSER, mActivityWindowAndroidForTest.lastIntent.getAction());
+            Intent contentIntent =
+                    (Intent) mActivityWindowAndroidForTest.lastIntent.getParcelableExtra(
+                            Intent.EXTRA_INTENT);
+            assertNotNull(contentIntent);
+            assertFalse(contentIntent.hasCategory(Intent.CATEGORY_OPENABLE));
+            resetActivityWindowAndroidForTest();
         }
-        resetActivityWindowAndroidForTest();
+
+        {
+            DOMUtils.clickNode(this, mContentViewCore, "input_text");
+            CriteriaHelper.pollInstrumentationThread(new IntentSentCriteria());
+            assertEquals(
+                    Intent.ACTION_CHOOSER, mActivityWindowAndroidForTest.lastIntent.getAction());
+            Intent contentIntent =
+                    (Intent) mActivityWindowAndroidForTest.lastIntent.getParcelableExtra(
+                            Intent.EXTRA_INTENT);
+            assertNotNull(contentIntent);
+            assertTrue(contentIntent.hasCategory(Intent.CATEGORY_OPENABLE));
+            resetActivityWindowAndroidForTest();
+        }
+
+        {
+            DOMUtils.clickNode(this, mContentViewCore, "input_any");
+            CriteriaHelper.pollInstrumentationThread(new IntentSentCriteria());
+            assertEquals(
+                    Intent.ACTION_CHOOSER, mActivityWindowAndroidForTest.lastIntent.getAction());
+            Intent contentIntent =
+                    (Intent) mActivityWindowAndroidForTest.lastIntent.getParcelableExtra(
+                            Intent.EXTRA_INTENT);
+            assertNotNull(contentIntent);
+            assertFalse(contentIntent.hasCategory(Intent.CATEGORY_OPENABLE));
+            resetActivityWindowAndroidForTest();
+        }
+
+        {
+            DOMUtils.clickNode(this, mContentViewCore, "input_file_multiple");
+            CriteriaHelper.pollInstrumentationThread(new IntentSentCriteria());
+            assertEquals(
+                    Intent.ACTION_CHOOSER, mActivityWindowAndroidForTest.lastIntent.getAction());
+            Intent contentIntent =
+                    (Intent) mActivityWindowAndroidForTest.lastIntent.getParcelableExtra(
+                            Intent.EXTRA_INTENT);
+            assertNotNull(contentIntent);
+            assertFalse(contentIntent.hasCategory(Intent.CATEGORY_OPENABLE));
+            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
+                assertTrue(contentIntent.hasExtra(Intent.EXTRA_ALLOW_MULTIPLE));
+            }
+            resetActivityWindowAndroidForTest();
+        }
 
         DOMUtils.clickNode(this, mContentViewCore, "input_image");
         CriteriaHelper.pollInstrumentationThread(new IntentSentCriteria());
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/history/HistoryActivityTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/history/HistoryActivityTest.java
new file mode 100644
index 0000000..b555b60af
--- /dev/null
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/history/HistoryActivityTest.java
@@ -0,0 +1,171 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.history;
+
+import android.os.Handler;
+import android.os.Looper;
+import android.support.test.filters.SmallTest;
+import android.support.v7.widget.RecyclerView;
+import android.support.v7.widget.RecyclerView.ViewHolder;
+import android.view.View;
+
+import org.chromium.base.ThreadUtils;
+import org.chromium.base.test.BaseActivityInstrumentationTestCase;
+import org.chromium.base.test.util.CallbackHelper;
+import org.chromium.base.test.util.Restriction;
+import org.chromium.chrome.R;
+import org.chromium.chrome.browser.widget.TintedImageButton;
+import org.chromium.chrome.browser.widget.selection.SelectableItemView;
+import org.chromium.chrome.browser.widget.selection.SelectableItemViewHolder;
+import org.chromium.chrome.browser.widget.selection.SelectionDelegate.SelectionObserver;
+import org.chromium.chrome.test.util.ChromeRestriction;
+
+import java.util.Date;
+import java.util.List;
+
+/**
+ * Tests the {@link HistoryActivity}.
+ */
+@Restriction(ChromeRestriction.RESTRICTION_TYPE_PHONE)
+public class HistoryActivityTest extends BaseActivityInstrumentationTestCase<HistoryActivity> {
+    private static class TestObserver extends RecyclerView.AdapterDataObserver
+            implements SelectionObserver<HistoryItem> {
+        public final CallbackHelper onChangedCallback = new CallbackHelper();
+        public final CallbackHelper onSelectionCallback = new CallbackHelper();
+
+        private Handler mHandler;
+
+        public TestObserver() {
+            mHandler = new Handler(Looper.getMainLooper());
+        }
+
+        @Override
+        public void onChanged() {
+            // To guarantee that all real Observers have had a chance to react to the event, post
+            // the CallbackHelper.notifyCalled() call.
+            mHandler.post(new Runnable() {
+                @Override
+                public void run() {
+                    onChangedCallback.notifyCalled();
+                }
+            });
+        }
+
+        @Override
+        public void onSelectionStateChange(List<HistoryItem> selectedItems) {
+            mHandler.post(new Runnable() {
+                @Override
+                public void run() {
+                    onSelectionCallback.notifyCalled();
+                }
+            });
+        }
+    }
+
+    private StubbedHistoryProvider mHistoryProvider;
+    private HistoryAdapter mAdapter;
+    private HistoryManager mHistoryManager;
+    private RecyclerView mRecyclerView;
+    private TestObserver mTestObserver;
+
+    private HistoryItem mItem1;
+    private HistoryItem mItem2;
+
+    public HistoryActivityTest() {
+        super(HistoryActivity.class);
+    }
+
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+
+        mHistoryProvider = new StubbedHistoryProvider();
+
+        Date today = new Date();
+        long[] timestamps = {today.getTime()};
+        mItem1 = StubbedHistoryProvider.createHistoryItem(0, timestamps);
+        mItem2 = StubbedHistoryProvider.createHistoryItem(1, timestamps);
+        mHistoryProvider.addItem(mItem1);
+        mHistoryProvider.addItem(mItem2);
+
+        HistoryManager.setProviderForTests(mHistoryProvider);
+
+        final HistoryActivity activity = getActivity();
+        mHistoryManager = activity.getHistoryManagerForTests();
+        mAdapter = mHistoryManager.getAdapterForTests();
+        mTestObserver = new TestObserver();
+        mHistoryManager.getSelectionDelegateForTests().addObserver(mTestObserver);
+        mAdapter.registerAdapterDataObserver(mTestObserver);
+        mRecyclerView = ((RecyclerView) activity.findViewById(R.id.recycler_view));
+
+        assertEquals(4, mAdapter.getItemCount());
+    }
+
+    @SmallTest
+    public void testRemove_SingleItem() throws Exception {
+        int callCount = mTestObserver.onChangedCallback.getCallCount();
+        final SelectableItemView<HistoryItem> itemView = getItemView(2);
+
+        ThreadUtils.runOnUiThreadBlocking(new Runnable() {
+            @Override
+            public void run() {
+                ((TintedImageButton) itemView.findViewById(R.id.remove)).performClick();
+            }
+        });
+
+        // Check that one item was removed.
+        mTestObserver.onChangedCallback.waitForCallback(callCount, 1);
+        assertEquals(1, mHistoryProvider.markItemForRemovalCallback.getCallCount());
+        assertEquals(1, mHistoryProvider.removeItemsCallback.getCallCount());
+        assertEquals(3, mAdapter.getItemCount());
+        assertEquals(View.VISIBLE, mRecyclerView.getVisibility());
+        assertEquals(View.GONE, mHistoryManager.getEmptyViewForTests().getVisibility());
+    }
+
+    @SmallTest
+    public void testRemove_AllItems() throws Exception {
+        toggleItemSelection(2);
+        toggleItemSelection(3);
+
+        int callCount = mTestObserver.onChangedCallback.getCallCount();
+
+        ThreadUtils.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                assertTrue(mHistoryManager.getToolbarForTests().getMenu()
+                        .performIdentifierAction(R.id.selection_mode_delete_menu_id, 0));
+            }
+        });
+
+        // Check that all items were removed. The onChangedCallback should be called three times -
+        // once for each item that is being removed and once for the removal of the header.
+        mTestObserver.onChangedCallback.waitForCallback(callCount, 3);
+        assertEquals(0, mAdapter.getItemCount());
+        assertEquals(2, mHistoryProvider.markItemForRemovalCallback.getCallCount());
+        assertEquals(1, mHistoryProvider.removeItemsCallback.getCallCount());
+        assertFalse(mHistoryManager.getSelectionDelegateForTests().isSelectionEnabled());
+        assertEquals(View.GONE, mRecyclerView.getVisibility());
+        assertEquals(View.VISIBLE, mHistoryManager.getEmptyViewForTests().getVisibility());
+    }
+
+    private void toggleItemSelection(int position) throws Exception {
+        int callCount = mTestObserver.onSelectionCallback.getCallCount();
+        final SelectableItemView<HistoryItem> itemView = getItemView(position);
+        ThreadUtils.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                itemView.performLongClick();
+            }
+        });
+        mTestObserver.onSelectionCallback.waitForCallback(callCount, 1);
+    }
+
+    @SuppressWarnings("unchecked")
+    private SelectableItemView<HistoryItem> getItemView(int position) {
+        ViewHolder mostRecentHolder = mRecyclerView.findViewHolderForAdapterPosition(position);
+        assertTrue(mostRecentHolder instanceof SelectableItemViewHolder);
+        return ((SelectableItemViewHolder<HistoryItem>) mostRecentHolder).getItemView();
+    }
+}
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/history/HistoryAdapterTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/history/HistoryAdapterTest.java
new file mode 100644
index 0000000..1f773ce
--- /dev/null
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/history/HistoryAdapterTest.java
@@ -0,0 +1,149 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.history;
+
+import static org.chromium.chrome.browser.widget.DateDividedAdapter.TYPE_DATE;
+import static org.chromium.chrome.browser.widget.DateDividedAdapter.TYPE_HEADER;
+import static org.chromium.chrome.browser.widget.DateDividedAdapter.TYPE_NORMAL;
+
+import android.support.test.filters.SmallTest;
+import android.test.InstrumentationTestCase;
+
+import org.chromium.base.ThreadUtils;
+import org.chromium.chrome.browser.widget.selection.SelectionDelegate;
+
+import java.util.Date;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Tests for the {@link HistoryAdapter}.
+ */
+public class HistoryAdapterTest extends InstrumentationTestCase {
+    private StubbedHistoryProvider mHistoryProvider;
+    private HistoryAdapter mAdapter;
+
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+        mHistoryProvider = new StubbedHistoryProvider();
+        mAdapter = new HistoryAdapter(new SelectionDelegate<HistoryItem>(), null, mHistoryProvider);
+    }
+
+    private void initializeAdapter() {
+        ThreadUtils.runOnUiThreadBlocking(new Runnable(){
+            @Override
+            public void run() {
+                mAdapter.initialize();
+            }
+        });
+    }
+
+    @SmallTest
+    public void testInitialize_Empty() {
+        initializeAdapter();
+        checkAdapterContents(false);
+    }
+
+    @SmallTest
+    public void testInitialize_SingleItem() {
+        Date today = new Date();
+        long[] timestamps = {today.getTime(), today.getTime() - 100};
+        HistoryItem item1 = StubbedHistoryProvider.createHistoryItem(0, timestamps);
+        mHistoryProvider.addItem(item1);
+
+        initializeAdapter();
+
+        // There should be three items - the header, a date and the history item.
+        checkAdapterContents(true, null, null, item1);
+    }
+
+    @SmallTest
+    public void testRemove_TwoItemsOneDate() {
+        Date today = new Date();
+        long[] timestamps = {today.getTime()};
+        HistoryItem item1 = StubbedHistoryProvider.createHistoryItem(0, timestamps);
+        mHistoryProvider.addItem(item1);
+
+        HistoryItem item2 = StubbedHistoryProvider.createHistoryItem(1, timestamps);
+        mHistoryProvider.addItem(item2);
+
+        initializeAdapter();
+
+        // There should be four items - the list header, a date header and two history items.
+        checkAdapterContents(true, null, null, item1, item2);
+
+        mAdapter.markItemForRemoval(item1);
+
+        // Check that one item was removed.
+        checkAdapterContents(true, null, null, item2);
+        assertEquals(1, mHistoryProvider.markItemForRemovalCallback.getCallCount());
+        assertEquals(0, mHistoryProvider.removeItemsCallback.getCallCount());
+
+        mAdapter.markItemForRemoval(item2);
+
+        // There should no longer be any items in the adapter.
+        checkAdapterContents(false);
+        assertEquals(2, mHistoryProvider.markItemForRemovalCallback.getCallCount());
+        assertEquals(0, mHistoryProvider.removeItemsCallback.getCallCount());
+
+        mAdapter.removeItems();
+        assertEquals(1, mHistoryProvider.removeItemsCallback.getCallCount());
+    }
+
+    @SmallTest
+    public void testRemove_TwoItemsTwoDates() {
+        Date today = new Date();
+        long[] timestamps = {today.getTime()};
+        HistoryItem item1 = StubbedHistoryProvider.createHistoryItem(0, timestamps);
+        mHistoryProvider.addItem(item1);
+
+        long[] timestamps2 = {today.getTime() - TimeUnit.DAYS.toMillis(3)};
+        HistoryItem item2 = StubbedHistoryProvider.createHistoryItem(1, timestamps2);
+        mHistoryProvider.addItem(item2);
+
+        initializeAdapter();
+
+        // There should be five items - the list header, a date header, a history item, another
+        // date header and another history item.
+        checkAdapterContents(true, null, null, item1, null, item2);
+
+        mAdapter.markItemForRemoval(item1);
+
+        // Check that the first item and date header were removed.
+        checkAdapterContents(true, null, null, item2);
+        assertEquals(1, mHistoryProvider.markItemForRemovalCallback.getCallCount());
+        assertEquals(0, mHistoryProvider.removeItemsCallback.getCallCount());
+
+        mAdapter.markItemForRemoval(item2);
+
+        // There should no longer be any items in the adapter.
+        checkAdapterContents(false);
+        assertEquals(2, mHistoryProvider.markItemForRemovalCallback.getCallCount());
+        assertEquals(0, mHistoryProvider.removeItemsCallback.getCallCount());
+
+        mAdapter.removeItems();
+        assertEquals(1, mHistoryProvider.removeItemsCallback.getCallCount());
+    }
+
+    private void checkAdapterContents(boolean hasHeader, Object... expectedItems) {
+        assertEquals(expectedItems.length, mAdapter.getItemCount());
+        assertEquals(hasHeader, mAdapter.hasListHeader());
+
+        for (int i = 0; i < expectedItems.length; i++) {
+            if (i == 0 && hasHeader) {
+                assertEquals(TYPE_HEADER, mAdapter.getItemViewType(i));
+                continue;
+            }
+
+            if (expectedItems[i] == null) {
+                // TODO(twellington): Check what date header is showing.
+                assertEquals(TYPE_DATE, mAdapter.getItemViewType(i));
+            } else {
+                assertEquals(TYPE_NORMAL, mAdapter.getItemViewType(i));
+                assertEquals(expectedItems[i], mAdapter.getItemAt(i).second);
+            }
+        }
+    }
+}
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/history/OWNERS b/chrome/android/javatests/src/org/chromium/chrome/browser/history/OWNERS
new file mode 100644
index 0000000..0fa757c
--- /dev/null
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/history/OWNERS
@@ -0,0 +1 @@
+twellington@chromium.org
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/history/StubbedHistoryProvider.java b/chrome/android/javatests/src/org/chromium/chrome/browser/history/StubbedHistoryProvider.java
new file mode 100644
index 0000000..84091e7
--- /dev/null
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/history/StubbedHistoryProvider.java
@@ -0,0 +1,65 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.history;
+
+import org.chromium.base.test.util.CallbackHelper;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Stubs out the backends used by the native Android browsing history manager.
+ */
+public class StubbedHistoryProvider implements HistoryProvider {
+    public final CallbackHelper markItemForRemovalCallback = new CallbackHelper();
+    public final CallbackHelper removeItemsCallback = new CallbackHelper();
+
+    private BrowsingHistoryObserver mObserver;
+    private List<HistoryItem> mItems = new ArrayList<>();
+    private List<HistoryItem> mRemovedItems = new ArrayList<>();
+
+    @Override
+    public void setObserver(BrowsingHistoryObserver observer) {
+        mObserver = observer;
+    }
+
+    @Override
+    public void queryHistory(String query, long endQueryTime) {
+        mObserver.onQueryHistoryComplete(mItems, false);
+    }
+
+    @Override
+    public void markItemForRemoval(HistoryItem item) {
+        mRemovedItems.add(item);
+        markItemForRemovalCallback.notifyCalled();
+    }
+
+    @Override
+    public void removeItems() {
+        for (HistoryItem item : mRemovedItems) {
+            mItems.remove(item);
+        }
+        mRemovedItems.clear();
+        removeItemsCallback.notifyCalled();
+    }
+
+    @Override
+    public void destroy() {}
+
+    public void addItem(HistoryItem item) {
+        mItems.add(item);
+    }
+
+    public static HistoryItem createHistoryItem(int which, long[] timestamps) {
+        if (which == 0) {
+            return new HistoryItem("http://google.com/", "www.google.com", "Google", timestamps);
+        } else if (which == 1) {
+            return new HistoryItem("http://foo.com/", "www.foo.com", "Foo", timestamps);
+        } else {
+            return null;
+        }
+    }
+
+}
diff --git a/chrome/browser/chromeos/login/screens/hid_detection_screen.cc b/chrome/browser/chromeos/login/screens/hid_detection_screen.cc
index 763e851..af9da95 100644
--- a/chrome/browser/chromeos/login/screens/hid_detection_screen.cc
+++ b/chrome/browser/chromeos/login/screens/hid_detection_screen.cc
@@ -53,17 +53,10 @@
                                        HIDDetectionView* view)
     : HIDDetectionModel(base_screen_delegate),
       view_(view),
-      mouse_is_pairing_(false),
-      pointing_device_connect_type_(InputDeviceInfo::TYPE_UNKNOWN),
-      keyboard_is_pairing_(false),
-      keyboard_device_connect_type_(InputDeviceInfo::TYPE_UNKNOWN),
-      switch_on_adapter_when_ready_(false),
-      showing_(false),
       weak_ptr_factory_(this) {
   DCHECK(view_);
   if (view_)
     view_->Bind(*this);
-
 }
 
 HIDDetectionScreen::~HIDDetectionScreen() {
@@ -126,19 +119,13 @@
 
   // Switch off BT adapter if it was off before the screen and no BT device
   // connected.
-  bool adapter_is_powered =
+  const bool adapter_is_powered =
       adapter_.get() && adapter_->IsPresent() && adapter_->IsPowered();
-  bool use_bluetooth =
-      pointing_device_connect_type_ == InputDeviceInfo::TYPE_BLUETOOTH ||
-      keyboard_device_connect_type_ == InputDeviceInfo::TYPE_BLUETOOTH;
-  bool need_switching_off = adapter_initially_powered_ &&
-                            !(*adapter_initially_powered_);
-  if (adapter_is_powered && !use_bluetooth && need_switching_off) {
-    VLOG(1) << "Switching off BT adapter after HID OOBE screen as unused.";
-    adapter_->SetPowered(
-        false,
-        base::Bind(&base::DoNothing),
-        base::Bind(&HIDDetectionScreen::SetPoweredOffError,
+  const bool need_switching_off =
+      adapter_initially_powered_ && !(*adapter_initially_powered_);
+  if (adapter_is_powered && need_switching_off) {
+    input_service_proxy_.GetDevices(
+        base::Bind(&HIDDetectionScreen::OnGetInputDevicesForPowerOff,
                    weak_ptr_factory_.GetWeakPtr()));
   }
 
@@ -532,6 +519,23 @@
   UpdateDevices();
 }
 
+void HIDDetectionScreen::OnGetInputDevicesForPowerOff(
+    const std::vector<InputDeviceInfo>& devices) {
+  bool use_bluetooth = false;
+  for (const auto& device : devices) {
+    if (device.type == InputDeviceInfo::TYPE_BLUETOOTH) {
+      use_bluetooth = true;
+      break;
+    }
+  }
+  if (!use_bluetooth) {
+    VLOG(1) << "Switching off BT adapter after HID OOBE screen as unused.";
+    adapter_->SetPowered(false, base::Bind(&base::DoNothing),
+                         base::Bind(&HIDDetectionScreen::SetPoweredOffError,
+                                    weak_ptr_factory_.GetWeakPtr()));
+  }
+}
+
 void HIDDetectionScreen::SetPoweredError() {
   LOG(ERROR) << "Failed to power BT adapter";
 }
@@ -544,5 +548,13 @@
   VLOG(1) << "Failed to start Bluetooth discovery.";
 }
 
+scoped_refptr<device::BluetoothAdapter>
+HIDDetectionScreen::GetAdapterForTesting() {
+  return adapter_;
+}
+
+void HIDDetectionScreen::SetAdapterInitialPoweredForTesting(bool powered) {
+  adapter_initially_powered_.reset(new bool(powered));
+}
 
 }  // namespace chromeos
diff --git a/chrome/browser/chromeos/login/screens/hid_detection_screen.h b/chrome/browser/chromeos/login/screens/hid_detection_screen.h
index 66cb6ba..de920f8 100644
--- a/chrome/browser/chromeos/login/screens/hid_detection_screen.h
+++ b/chrome/browser/chromeos/login/screens/hid_detection_screen.h
@@ -140,6 +140,11 @@
   // initiate a discovery session.
   void FindDevicesError();
 
+  // Check the input devices returned by InputServiceProxy one by one and power
+  // off the BT adapter if there is no bluetooth device.
+  void OnGetInputDevicesForPowerOff(
+      const std::vector<InputDeviceInfo>& devices);
+
   // Called by device::BluetoothAdapter in response to a failure to
   // power BT adapter.
   void SetPoweredError();
@@ -179,6 +184,9 @@
   // Helper method. Sets device name or placeholder if the name is empty.
   void SetKeyboardDeviceName_(const std::string& name);
 
+  scoped_refptr<device::BluetoothAdapter> GetAdapterForTesting();
+  void SetAdapterInitialPoweredForTesting(bool powered);
+
   HIDDetectionView* view_;
 
   // Default bluetooth adapter, used for all operations.
@@ -193,21 +201,23 @@
 
   // Current pointing device, if any. Device name is kept in screen context.
   std::string pointing_device_id_;
-  bool mouse_is_pairing_;
-  InputDeviceInfo::Type pointing_device_connect_type_;
+  bool mouse_is_pairing_ = false;
+  InputDeviceInfo::Type pointing_device_connect_type_ =
+      InputDeviceInfo::TYPE_UNKNOWN;
 
   // Current keyboard device, if any. Device name is kept in screen context.
   std::string keyboard_device_id_;
-  bool keyboard_is_pairing_;
-  InputDeviceInfo::Type keyboard_device_connect_type_;
+  bool keyboard_is_pairing_ = false;
+  InputDeviceInfo::Type keyboard_device_connect_type_ =
+      InputDeviceInfo::TYPE_UNKNOWN;
   std::string keyboard_device_name_;
 
   // State of BT adapter before screen-initiated changes.
   std::unique_ptr<bool> adapter_initially_powered_;
 
-  bool switch_on_adapter_when_ready_;
+  bool switch_on_adapter_when_ready_ = false;
 
-  bool showing_;
+  bool showing_ = false;
 
   base::WeakPtrFactory<HIDDetectionScreen> weak_ptr_factory_;
 
diff --git a/chrome/browser/chromeos/login/screens/hid_detection_screen_browsertest.cc b/chrome/browser/chromeos/login/screens/hid_detection_screen_browsertest.cc
index 1b9539b..3401ce1 100644
--- a/chrome/browser/chromeos/login/screens/hid_detection_screen_browsertest.cc
+++ b/chrome/browser/chromeos/login/screens/hid_detection_screen_browsertest.cc
@@ -6,6 +6,7 @@
 #include "chrome/browser/chromeos/device/input_service_test_helper.h"
 #include "chrome/browser/chromeos/login/screens/base_screen.h"
 #include "chrome/browser/chromeos/login/screens/hid_detection_screen.h"
+#include "chrome/browser/chromeos/login/test/oobe_screen_waiter.h"
 #include "chrome/browser/chromeos/login/test/wizard_in_process_browser_test.h"
 #include "chrome/browser/chromeos/login/wizard_controller.h"
 
@@ -30,6 +31,7 @@
               hid_detection_screen_);
     ASSERT_TRUE(hid_detection_screen_->view_);
 
+    hid_detection_screen()->SetAdapterInitialPoweredForTesting(false);
     helper_->SetProxy(&hid_detection_screen_->input_service_proxy_);
   }
 
@@ -40,6 +42,12 @@
 
   InputServiceTestHelper* helper() { return helper_.get(); }
 
+  HIDDetectionScreen* hid_detection_screen() { return hid_detection_screen_; }
+
+  scoped_refptr<device::BluetoothAdapter> adapter() {
+    return hid_detection_screen_->GetAdapterForTesting();
+  }
+
   const ::login::ScreenContext* context() const {
     return &hid_detection_screen_->context_;
   }
@@ -106,4 +114,44 @@
       HIDDetectionModel::kContextKeyContinueButtonEnabled));
 }
 
+// Test that if there is any Bluetooth device connected on HID screen, the
+// Bluetooth adapter should not be disabled after advancing to the next screen.
+IN_PROC_BROWSER_TEST_F(HIDDetectionScreenTest, BluetoothDeviceConnected) {
+  OobeScreenWaiter(OobeScreen::SCREEN_OOBE_HID_DETECTION).Wait();
+  EXPECT_TRUE(adapter()->IsPowered());
+
+  // Add a pair of USB mouse/keyboard so that |pointing_device_connect_type_|
+  // and |keyboard_device_connect_type_| are InputDeviceInfo::TYPE_USB.
+  helper()->AddDeviceToService(true, InputDeviceInfo::TYPE_USB);
+  helper()->AddDeviceToService(false, InputDeviceInfo::TYPE_USB);
+
+  // Add another pair of Bluetooth mouse/keyboard.
+  helper()->AddDeviceToService(true, InputDeviceInfo::TYPE_BLUETOOTH);
+  helper()->AddDeviceToService(false, InputDeviceInfo::TYPE_BLUETOOTH);
+
+  // Simulate the user's click on "Continue" button.
+  hid_detection_screen()->OnContinueButtonClicked();
+  OobeScreenWaiter(OobeScreen::SCREEN_OOBE_NETWORK).Wait();
+
+  // The adapter should not be powered off at this moment.
+  EXPECT_TRUE(adapter()->IsPowered());
+}
+
+// Test that if there is no Bluetooth device connected on HID screen, the
+// Bluetooth adapter should be disabled after advancing to the next screen.
+IN_PROC_BROWSER_TEST_F(HIDDetectionScreenTest, NoBluetoothDeviceConnected) {
+  OobeScreenWaiter(OobeScreen::SCREEN_OOBE_HID_DETECTION).Wait();
+  EXPECT_TRUE(adapter()->IsPowered());
+
+  helper()->AddDeviceToService(true, InputDeviceInfo::TYPE_USB);
+  helper()->AddDeviceToService(false, InputDeviceInfo::TYPE_USB);
+
+  // Simulate the user's click on "Continue" button.
+  hid_detection_screen()->OnContinueButtonClicked();
+  OobeScreenWaiter(OobeScreen::SCREEN_OOBE_NETWORK).Wait();
+
+  // The adapter should be powered off at this moment.
+  EXPECT_FALSE(adapter()->IsPowered());
+}
+
 }  // namespace chromeos
diff --git a/chrome/browser/extensions/api/dial/OWNERS b/chrome/browser/extensions/api/dial/OWNERS
index 68dd516..8224bbf7 100644
--- a/chrome/browser/extensions/api/dial/OWNERS
+++ b/chrome/browser/extensions/api/dial/OWNERS
@@ -1,3 +1,3 @@
 mfoltz@chromium.org
 imcheng@chromium.org
-
+wez@chromium.org
diff --git a/chrome/browser/resources/settings/downloads_page/downloads_page.html b/chrome/browser/resources/settings/downloads_page/downloads_page.html
index 4271241..ea7b85f 100644
--- a/chrome/browser/resources/settings/downloads_page/downloads_page.html
+++ b/chrome/browser/resources/settings/downloads_page/downloads_page.html
@@ -11,7 +11,12 @@
       <div class="start">
         <div>$i18n{downloadLocation}</div>
         <div class="secondary">
+<if expr="not chromeos">
+          [[prefs.download.default_directory.value]]
+</if>
+<if expr="chromeos">
           [[getDownloadLocation_(prefs.download.default_directory.value)]]
+</if>
         </div>
       </div>
       <div class="secondary-action">
diff --git a/chrome/browser/resources/settings/downloads_page/downloads_page.js b/chrome/browser/resources/settings/downloads_page/downloads_page.js
index 7603dad..004e99c 100644
--- a/chrome/browser/resources/settings/downloads_page/downloads_page.js
+++ b/chrome/browser/resources/settings/downloads_page/downloads_page.js
@@ -39,6 +39,7 @@
     chrome.send('selectDownloadLocation');
   },
 
+<if expr="chromeos">
   /**
    * @param {string} path
    * @return {string} The download location string that is suitable to display
@@ -55,4 +56,5 @@
     path = path.replace(/\//g, ' \u203a ');
     return path;
   },
+</if>
 });
diff --git a/chrome/browser/resources/settings/settings_resources.grd b/chrome/browser/resources/settings/settings_resources.grd
index bcd304f8..ddd8539 100644
--- a/chrome/browser/resources/settings/settings_resources.grd
+++ b/chrome/browser/resources/settings/settings_resources.grd
@@ -524,7 +524,8 @@
                  allowexternalscript="true" />
       <structure name="IDR_SETTINGS_DOWNLOADS_PAGE_JS"
                  file="downloads_page/downloads_page.js"
-                 type="chrome_html" />
+                 type="chrome_html"
+                 preprocess="true" />
       <structure name="IDR_SETTINGS_I18n_SETUP_HTML"
                  file="i18n_setup.html"
                  type="chrome_html" />
diff --git a/chrome/browser/ui/app_list/app_list_service_interactive_uitest.cc b/chrome/browser/ui/app_list/app_list_service_interactive_uitest.cc
index 0ca371c3..163a9af3 100644
--- a/chrome/browser/ui/app_list/app_list_service_interactive_uitest.cc
+++ b/chrome/browser/ui/app_list/app_list_service_interactive_uitest.cc
@@ -57,13 +57,11 @@
 // ChromeOS does not support ShowForProfile(), or profile switching within the
 // app list. Profile switching on CrOS goes through a different code path.
 #if defined(OS_CHROMEOS)
-#define MAYBE_ShowAndDismiss DISABLED_ShowAndDismiss
 #define MAYBE_SwitchAppListProfiles DISABLED_SwitchAppListProfiles
 #define MAYBE_SwitchAppListLockedProfile DISABLED_SwitchAppListLockedProfile
 #define MAYBE_SwitchAppListProfilesDuringSearch \
     DISABLED_SwitchAppListProfilesDuringSearch
 #else
-#define MAYBE_ShowAndDismiss ShowAndDismiss
 #define MAYBE_SwitchAppListProfiles SwitchAppListProfiles
 #define MAYBE_SwitchAppListLockedProfile SwitchAppListLockedProfile
 #define MAYBE_SwitchAppListProfilesDuringSearch \
@@ -71,7 +69,7 @@
 #endif
 
 // Show the app list, then dismiss it.
-IN_PROC_BROWSER_TEST_F(AppListServiceInteractiveTest, MAYBE_ShowAndDismiss) {
+IN_PROC_BROWSER_TEST_F(AppListServiceInteractiveTest, ShowAndDismiss) {
   AppListService* service = AppListService::Get();
   ASSERT_FALSE(service->IsAppListVisible());
   service->ShowForProfile(browser()->profile());
diff --git a/chrome/browser/ui/app_list/app_list_syncable_service.cc b/chrome/browser/ui/app_list/app_list_syncable_service.cc
index 555bfbd..aa44f70 100644
--- a/chrome/browser/ui/app_list/app_list_syncable_service.cc
+++ b/chrome/browser/ui/app_list/app_list_syncable_service.cc
@@ -567,6 +567,7 @@
     return NULL;
   VLOG(2) << this << " CreateSyncItemFromAppItem:" << app_item->ToDebugString();
   SyncItem* sync_item = CreateSyncItem(app_item->id(), type);
+  DCHECK(app_item->position().IsValid());
   UpdateSyncItemFromAppItem(app_item, sync_item);
   UpdateSyncItemInLocalStorage(profile_, sync_item);
   SendSyncChange(sync_item, SyncChange::ACTION_ADD);
@@ -584,6 +585,8 @@
 void AppListSyncableService::SetPinPosition(
     const std::string& app_id,
     const syncer::StringOrdinal& item_pin_ordinal) {
+  // Pin position can be set only after model is built.
+  DCHECK(IsInitialized());
   SyncItem* sync_item = FindSyncItem(app_id);
   SyncChange::SyncChangeType sync_change_type;
   if (sync_item) {
@@ -604,9 +607,15 @@
   if (app_item->id() == kOemFolderId)
     return;
 
+  DCHECK(app_item->position().IsValid());
+
   SyncItem* sync_item = FindSyncItem(app_item->id());
   if (sync_item) {
     UpdateAppItemFromSyncItem(sync_item, app_item);
+    if (!sync_item->item_ordinal.IsValid()) {
+      UpdateSyncItem(app_item);
+      VLOG(2) << "Flushing position to sync item " << sync_item;
+    }
     return;
   }
   CreateSyncItemFromAppItem(app_item);
@@ -840,6 +849,33 @@
     change_list.push_back(SyncChange(FROM_HERE,  SyncChange::ACTION_ADD,
                                      GetSyncDataFromSyncItem(sync_item)));
   }
+
+  // Fix items that do not contain valid app list position, required for
+  // builds prior to M53 (crbug.com/677647).
+  for (const auto& sync_pair : sync_items_) {
+    SyncItem* sync_item = sync_pair.second.get();
+    if (sync_item->item_type != sync_pb::AppListSpecifics::TYPE_APP ||
+        sync_item->item_ordinal.IsValid()) {
+      continue;
+    }
+    const AppListItem* app_item = model_->FindItem(sync_item->item_id);
+    if (app_item) {
+      if (UpdateSyncItemFromAppItem(app_item, sync_item)) {
+        VLOG(1) << "Fixing sync item from existing app: " << sync_item;
+      } else {
+        sync_item->item_ordinal = syncer::StringOrdinal::CreateInitialOrdinal();
+        VLOG(1) << "Failed to fix sync item from existing app. "
+                << "Generating new position ordinal: " << sync_item;
+      }
+    } else {
+      sync_item->item_ordinal = syncer::StringOrdinal::CreateInitialOrdinal();
+      VLOG(1) << "Fixing sync item by generating new position ordinal: "
+              << sync_item;
+    }
+    change_list.push_back(SyncChange(FROM_HERE,  SyncChange::ACTION_UPDATE,
+                                     GetSyncDataFromSyncItem(sync_item)));
+  }
+
   sync_processor_->ProcessSyncChanges(FROM_HERE, change_list);
 
   HandleUpdateFinished();
diff --git a/chrome/browser/ui/app_list/chrome_app_list_item.cc b/chrome/browser/ui/app_list/chrome_app_list_item.cc
index 934638f..ca281a1 100644
--- a/chrome/browser/ui/app_list/chrome_app_list_item.cc
+++ b/chrome/browser/ui/app_list/chrome_app_list_item.cc
@@ -62,10 +62,16 @@
 void ChromeAppListItem::SetDefaultPositionIfApplicable() {
   syncer::StringOrdinal page_ordinal;
   syncer::StringOrdinal launch_ordinal;
-  if (GetAppSorting()->GetDefaultOrdinals(id(), &page_ordinal,
-                                          &launch_ordinal) &&
-      page_ordinal.IsValid() && launch_ordinal.IsValid()) {
-    set_position(syncer::StringOrdinal(page_ordinal.ToInternalValue() +
-                                       launch_ordinal.ToInternalValue()));
+  extensions::AppSorting* app_sorting = GetAppSorting();
+  if (!app_sorting->GetDefaultOrdinals(id(), &page_ordinal,
+                                       &launch_ordinal) ||
+      !page_ordinal.IsValid() || !launch_ordinal.IsValid()) {
+    app_sorting->EnsureValidOrdinals(id(), syncer::StringOrdinal());
+    page_ordinal = app_sorting->GetPageOrdinal(id());
+    launch_ordinal = app_sorting->GetAppLaunchOrdinal(id());
   }
+  DCHECK(page_ordinal.IsValid());
+  DCHECK(launch_ordinal.IsValid());
+  set_position(syncer::StringOrdinal(page_ordinal.ToInternalValue() +
+                                     launch_ordinal.ToInternalValue()));
 }
diff --git a/chrome/browser/ui/app_list/extension_app_model_builder_unittest.cc b/chrome/browser/ui/app_list/extension_app_model_builder_unittest.cc
index 71711bb..d21cfebd5 100644
--- a/chrome/browser/ui/app_list/extension_app_model_builder_unittest.cc
+++ b/chrome/browser/ui/app_list/extension_app_model_builder_unittest.cc
@@ -72,7 +72,7 @@
   return app;
 }
 
-const char kDefaultApps[] = "Hosted App,Packaged App 1,Packaged App 2";
+const char kDefaultApps[] = "Packaged App 1,Packaged App 2,Hosted App";
 const size_t kDefaultAppCount = 3u;
 
 }  // namespace
@@ -183,7 +183,7 @@
                                extensions::UNINSTALL_REASON_FOR_TESTING,
                                base::Bind(&base::DoNothing),
                                NULL);
-  EXPECT_EQ(std::string("Hosted App,Packaged App 1"),
+  EXPECT_EQ(std::string("Packaged App 1,Hosted App"),
             GetModelContent(model_.get()));
 
   base::RunLoop().RunUntilIdle();
@@ -201,7 +201,7 @@
                                extensions::UNINSTALL_REASON_FOR_TESTING,
                                base::Bind(&base::DoNothing),
                                NULL);
-  EXPECT_EQ(std::string("Hosted App,Packaged App 1"),
+  EXPECT_EQ(std::string("Packaged App 1,Hosted App"),
             GetModelContent(model_.get()));
 
   base::RunLoop().RunUntilIdle();
diff --git a/chrome/browser/ui/ash/app_list/app_list_interactive_uitest.cc b/chrome/browser/ui/ash/app_list/app_list_interactive_uitest.cc
new file mode 100644
index 0000000..a4386b1f
--- /dev/null
+++ b/chrome/browser/ui/ash/app_list/app_list_interactive_uitest.cc
@@ -0,0 +1,50 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <memory>
+
+#include "ash/common/shelf/app_list_button.h"
+#include "ash/common/shelf/shelf_widget.h"
+#include "ash/common/shelf/wm_shelf.h"
+#include "ash/common/wm_shell.h"
+#include "ash/common/wm_window.h"
+#include "ash/public/cpp/shell_window_ids.h"
+#include "base/run_loop.h"
+#include "chrome/test/base/in_process_browser_test.h"
+#include "ui/app_list/presenter/app_list.h"
+#include "ui/events/test/event_generator.h"
+
+using AppListTest = InProcessBrowserTest;
+
+// An integration test to toggle the app list by pressing the shelf button.
+IN_PROC_BROWSER_TEST_F(AppListTest, PressAppListButtonToShowAndDismiss) {
+  ash::WmShell* shell = ash::WmShell::Get();
+  ash::WmShelf* shelf = ash::WmShelf::ForWindow(shell->GetActiveWindow());
+  ash::ShelfWidget* shelf_widget = shelf->shelf_widget();
+  ash::AppListButton* app_list_button = shelf_widget->GetAppListButton();
+
+  ash::WmWindow* root_window = shell->GetActiveWindow()->GetRootWindow();
+  ash::WmWindow* app_list_container = root_window->GetChildByShellWindowId(
+      ash::kShellWindowId_AppListContainer);
+  ui::test::EventGenerator generator(shelf_widget->GetNativeWindow());
+
+  // Click the app list button to show the app list.
+  EXPECT_FALSE(shell->app_list()->GetTargetVisibility());
+  EXPECT_EQ(0u, app_list_container->GetChildren().size());
+  EXPECT_FALSE(app_list_button->is_showing_app_list());
+  generator.set_current_location(
+      app_list_button->GetBoundsInScreen().CenterPoint());
+  generator.ClickLeftButton();
+  base::RunLoop().RunUntilIdle();
+  EXPECT_TRUE(shell->app_list()->GetTargetVisibility());
+  EXPECT_EQ(1u, app_list_container->GetChildren().size());
+  EXPECT_TRUE(app_list_button->is_showing_app_list());
+
+  // Click the button again to dismiss the app list; the window animates closed.
+  generator.ClickLeftButton();
+  base::RunLoop().RunUntilIdle();
+  EXPECT_FALSE(shell->app_list()->GetTargetVisibility());
+  EXPECT_EQ(1u, app_list_container->GetChildren().size());
+  EXPECT_FALSE(app_list_button->is_showing_app_list());
+}
diff --git a/chrome/browser/ui/ash/app_list/app_list_presenter_delegate_mus.cc b/chrome/browser/ui/ash/app_list/app_list_presenter_delegate_mus.cc
index fb7e062..1ca9e18 100644
--- a/chrome/browser/ui/ash/app_list/app_list_presenter_delegate_mus.cc
+++ b/chrome/browser/ui/ash/app_list/app_list_presenter_delegate_mus.cc
@@ -4,7 +4,7 @@
 
 #include "chrome/browser/ui/ash/app_list/app_list_presenter_delegate_mus.h"
 
-#include "ui/app_list/presenter/app_list_presenter.h"
+#include "ui/app_list/presenter/app_list_presenter_impl.h"
 #include "ui/app_list/presenter/app_list_view_delegate_factory.h"
 #include "ui/app_list/views/app_list_view.h"
 #include "ui/display/display.h"
@@ -39,7 +39,7 @@
 }  // namespace
 
 AppListPresenterDelegateMus::AppListPresenterDelegateMus(
-    app_list::AppListPresenter* presenter,
+    app_list::AppListPresenterImpl* presenter,
     app_list::AppListViewDelegateFactory* view_delegate_factory)
     : presenter_(presenter), view_delegate_factory_(view_delegate_factory) {}
 
diff --git a/chrome/browser/ui/ash/app_list/app_list_presenter_delegate_mus.h b/chrome/browser/ui/ash/app_list/app_list_presenter_delegate_mus.h
index 87e9c2e..81ab187 100644
--- a/chrome/browser/ui/ash/app_list/app_list_presenter_delegate_mus.h
+++ b/chrome/browser/ui/ash/app_list/app_list_presenter_delegate_mus.h
@@ -10,7 +10,7 @@
 #include "ui/views/pointer_watcher.h"
 
 namespace app_list {
-class AppListPresenter;
+class AppListPresenterImpl;
 class AppListView;
 class AppListViewDelegateFactory;
 }  // namespace app_list
@@ -23,7 +23,7 @@
                                     public views::PointerWatcher {
  public:
   AppListPresenterDelegateMus(
-      app_list::AppListPresenter* presenter,
+      app_list::AppListPresenterImpl* presenter,
       app_list::AppListViewDelegateFactory* view_delegate_factory);
   ~AppListPresenterDelegateMus() override;
 
@@ -45,7 +45,7 @@
                               views::Widget* target) override;
 
   // Not owned. Pointer is guaranteed to be valid while this object is alive.
-  app_list::AppListPresenter* presenter_;
+  app_list::AppListPresenterImpl* presenter_;
 
   // Not owned. Pointer is guaranteed to be valid while this object is alive.
   app_list::AppListViewDelegateFactory* view_delegate_factory_;
diff --git a/chrome/browser/ui/ash/app_list/app_list_presenter_service.cc b/chrome/browser/ui/ash/app_list/app_list_presenter_service.cc
index 2770d89..aed941a 100644
--- a/chrome/browser/ui/ash/app_list/app_list_presenter_service.cc
+++ b/chrome/browser/ui/ash/app_list/app_list_presenter_service.cc
@@ -8,7 +8,7 @@
 #include "chrome/browser/ui/ash/ash_util.h"
 #include "content/public/common/service_manager_connection.h"
 #include "services/service_manager/public/cpp/connector.h"
-#include "ui/app_list/presenter/app_list_presenter.h"
+#include "ui/app_list/presenter/app_list_presenter_impl.h"
 
 AppListPresenterService::AppListPresenterService() : binding_(this) {
   content::ServiceManagerConnection* connection =
@@ -20,6 +20,8 @@
         ash_util::GetAshServiceName(), &app_list_ptr);
     // Register this object as the app list presenter.
     app_list_ptr->SetAppListPresenter(binding_.CreateInterfacePtrAndBind());
+    // Pass the interface pointer to the presenter to report visibility changes.
+    GetPresenter()->SetAppList(std::move(app_list_ptr));
   }
 }
 
@@ -37,6 +39,6 @@
   GetPresenter()->ToggleAppList(display_id);
 }
 
-app_list::AppListPresenter* AppListPresenterService::GetPresenter() {
+app_list::AppListPresenterImpl* AppListPresenterService::GetPresenter() {
   return AppListServiceAsh::GetInstance()->GetAppListPresenter();
 }
diff --git a/chrome/browser/ui/ash/app_list/app_list_presenter_service.h b/chrome/browser/ui/ash/app_list/app_list_presenter_service.h
index 969cc8e..7ca5d255 100644
--- a/chrome/browser/ui/ash/app_list/app_list_presenter_service.h
+++ b/chrome/browser/ui/ash/app_list/app_list_presenter_service.h
@@ -10,7 +10,7 @@
 #include "ui/app_list/presenter/app_list_presenter.mojom.h"
 
 namespace app_list {
-class AppListPresenter;
+class AppListPresenterImpl;
 }
 
 // A service providing the Mojo interface to manipulate the App List.
@@ -25,7 +25,7 @@
   void ToggleAppList(int64_t display_id) override;
 
  private:
-  app_list::AppListPresenter* GetPresenter();
+  app_list::AppListPresenterImpl* GetPresenter();
 
   mojo::Binding<app_list::mojom::AppListPresenter> binding_;
 
diff --git a/chrome/browser/ui/ash/app_list/app_list_service_ash.cc b/chrome/browser/ui/ash/app_list/app_list_service_ash.cc
index ca2480e..e3fc90a 100644
--- a/chrome/browser/ui/ash/app_list/app_list_service_ash.cc
+++ b/chrome/browser/ui/ash/app_list/app_list_service_ash.cc
@@ -16,6 +16,7 @@
 #include "chrome/browser/ui/app_list/start_page_service.h"
 #include "chrome/browser/ui/ash/app_list/app_list_controller_ash.h"
 #include "chrome/browser/ui/ash/app_list/app_list_presenter_delegate_mus.h"
+#include "chrome/browser/ui/ash/app_list/app_list_presenter_service.h"
 #include "chrome/browser/ui/ash/ash_util.h"
 #include "chrome/browser/ui/ash/launcher/chrome_launcher_controller.h"
 #include "chrome/browser/ui/ash/session_util.h"
@@ -58,7 +59,7 @@
   ~AppListPresenterDelegateFactoryMus() override {}
 
   std::unique_ptr<app_list::AppListPresenterDelegate> GetDelegate(
-      app_list::AppListPresenter* presenter) override {
+      app_list::AppListPresenterImpl* presenter) override {
     return base::MakeUnique<AppListPresenterDelegateMus>(
         presenter, view_delegate_factory_.get());
   }
@@ -78,27 +79,32 @@
 }
 
 AppListServiceAsh::AppListServiceAsh() {
+  std::unique_ptr<app_list::AppListPresenterDelegateFactory> factory;
   if (chrome::IsRunningInMash()) {
-    presenter_delegate_factory_.reset(new AppListPresenterDelegateFactoryMus(
-        base::MakeUnique<ViewDelegateFactoryImpl>(this)));
+    factory = base::MakeUnique<AppListPresenterDelegateFactoryMus>(
+        base::MakeUnique<ViewDelegateFactoryImpl>(this));
   } else {
-    presenter_delegate_factory_.reset(new ash::AppListPresenterDelegateFactory(
-        base::MakeUnique<ViewDelegateFactoryImpl>(this)));
+    factory = base::MakeUnique<ash::AppListPresenterDelegateFactory>(
+        base::MakeUnique<ViewDelegateFactoryImpl>(this));
   }
-  app_list_presenter_.reset(
-      new app_list::AppListPresenterImpl(presenter_delegate_factory_.get()));
-  controller_delegate_.reset(
-      new AppListControllerDelegateAsh(app_list_presenter_.get()));
+  app_list_presenter_ =
+      base::MakeUnique<app_list::AppListPresenterImpl>(std::move(factory));
+  controller_delegate_ =
+      base::MakeUnique<AppListControllerDelegateAsh>(app_list_presenter_.get());
 }
 
-AppListServiceAsh::~AppListServiceAsh() {
-}
+AppListServiceAsh::~AppListServiceAsh() {}
 
-app_list::AppListPresenter* AppListServiceAsh::GetAppListPresenter() {
+app_list::AppListPresenterImpl* AppListServiceAsh::GetAppListPresenter() {
   return app_list_presenter_.get();
 }
 
 void AppListServiceAsh::Init(Profile* initial_profile) {
+  // The AppListPresenterService ctor calls AppListServiceAsh::GetInstance(),
+  // which isn't available in the AppListServiceAsh constructor, so init here.
+  // This establishes the mojo connections between the app list and presenter.
+  app_list_presenter_service_ = base::MakeUnique<AppListPresenterService>();
+
   // Ensure the StartPageService is created here. This early initialization is
   // necessary to allow the WebContents to load before the app list is shown.
   app_list::StartPageService* service =
diff --git a/chrome/browser/ui/ash/app_list/app_list_service_ash.h b/chrome/browser/ui/ash/app_list/app_list_service_ash.h
index b3f3763..1175466 100644
--- a/chrome/browser/ui/ash/app_list/app_list_service_ash.h
+++ b/chrome/browser/ui/ash/app_list/app_list_service_ash.h
@@ -9,13 +9,12 @@
 
 #include "base/macros.h"
 #include "chrome/browser/ui/app_list/app_list_service_impl.h"
-#include "chrome/browser/ui/ash/app_list/app_list_presenter_service.h"
 #include "ui/app_list/app_list_model.h"
 
+class AppListPresenterService;
+
 namespace app_list {
-class AppListPresenter;
 class AppListPresenterImpl;
-class AppListPresenterDelegateFactory;
 }
 
 namespace base {
@@ -30,7 +29,7 @@
  public:
   static AppListServiceAsh* GetInstance();
 
-  app_list::AppListPresenter* GetAppListPresenter();
+  app_list::AppListPresenterImpl* GetAppListPresenter();
 
   // AppListService overrides:
   void Init(Profile* initial_profile) override;
@@ -72,12 +71,9 @@
   void CreateForProfile(Profile* default_profile) override;
   void DestroyAppList() override;
 
-  std::unique_ptr<app_list::AppListPresenterDelegateFactory>
-      presenter_delegate_factory_;
   std::unique_ptr<app_list::AppListPresenterImpl> app_list_presenter_;
   std::unique_ptr<AppListControllerDelegateAsh> controller_delegate_;
-
-  AppListPresenterService app_list_presenter_service_;
+  std::unique_ptr<AppListPresenterService> app_list_presenter_service_;
 
   DISALLOW_COPY_AND_ASSIGN(AppListServiceAsh);
 };
diff --git a/chrome/browser/ui/ash/chrome_shell_delegate.cc b/chrome/browser/ui/ash/chrome_shell_delegate.cc
index 6bd85d99..383874b 100644
--- a/chrome/browser/ui/ash/chrome_shell_delegate.cc
+++ b/chrome/browser/ui/ash/chrome_shell_delegate.cc
@@ -44,8 +44,6 @@
 #include "chrome/browser/signin/signin_error_notifier_factory_ash.h"
 #include "chrome/browser/speech/tts_controller.h"
 #include "chrome/browser/sync/sync_error_notifier_factory_ash.h"
-#include "chrome/browser/ui/app_list/app_list_view_delegate.h"
-#include "chrome/browser/ui/ash/app_list/app_list_service_ash.h"
 #include "chrome/browser/ui/ash/chrome_keyboard_ui.h"
 #include "chrome/browser/ui/ash/launcher/chrome_launcher_controller_impl.h"
 #include "chrome/browser/ui/ash/launcher/launcher_context_menu.h"
@@ -74,7 +72,6 @@
 #include "content/public/browser/notification_service.h"
 #include "content/public/browser/user_metrics.h"
 #include "content/public/common/service_manager_connection.h"
-#include "ui/app_list/presenter/app_list_presenter.h"
 #include "ui/aura/window.h"
 #include "ui/base/ime/chromeos/input_method_manager.h"
 #include "ui/base/l10n/l10n_util.h"
@@ -453,11 +450,6 @@
       displayer.browser()->window()->GetNativeWindow());
 }
 
-app_list::AppListPresenter* ChromeShellDelegate::GetAppListPresenter() {
-  DCHECK(ash::Shell::HasInstance());
-  return AppListServiceAsh::GetInstance()->GetAppListPresenter();
-}
-
 ash::ShelfDelegate* ChromeShellDelegate::CreateShelfDelegate(
     ash::ShelfModel* model) {
   if (!shelf_delegate_) {
diff --git a/chrome/browser/ui/ash/chrome_shell_delegate.h b/chrome/browser/ui/ash/chrome_shell_delegate.h
index f5c0e77..bebc20d 100644
--- a/chrome/browser/ui/ash/chrome_shell_delegate.h
+++ b/chrome/browser/ui/ash/chrome_shell_delegate.h
@@ -42,7 +42,6 @@
   void Exit() override;
   keyboard::KeyboardUI* CreateKeyboardUI() override;
   void OpenUrlFromArc(const GURL& url) override;
-  app_list::AppListPresenter* GetAppListPresenter() override;
   ash::ShelfDelegate* CreateShelfDelegate(ash::ShelfModel* model) override;
   ash::SystemTrayDelegate* CreateSystemTrayDelegate() override;
   std::unique_ptr<ash::WallpaperDelegate> CreateWallpaperDelegate() override;
diff --git a/chrome/browser/ui/libgtkui/gtk_ui.cc b/chrome/browser/ui/libgtkui/gtk_ui.cc
index 36aa340..07fb169 100644
--- a/chrome/browser/ui/libgtkui/gtk_ui.cc
+++ b/chrome/browser/ui/libgtkui/gtk_ui.cc
@@ -242,11 +242,9 @@
 //
 // Default tints.
 const color_utils::HSL kDefaultTintFrameIncognito = {-1, 0.2f, 0.35f};
+#if GTK_MAJOR_VERSION == 2
 const color_utils::HSL kDefaultTintFrameIncognitoInactive = {-1, 0.3f, 0.6f};
-
-#if GTK_MAJOR_VERSION == 3
-const color_utils::HSL kDefaultTintFrameInactive = {-1, -1, 0.75f};
-#endif  // GTK_MAJOR_VERSION == 3
+#endif
 
 // Picks a button tint from a set of background colors. While
 // |accent_color| will usually be the same color through a theme, this
@@ -879,6 +877,12 @@
   colors_[ThemeProperties::COLOR_TAB_THROBBER_WAITING] =
       native_theme_->GetSystemColor(
           ui::NativeTheme::kColorId_ThrobberWaitingColor);
+
+#if GTK_MAJOR_VERSION > 2
+  colors_[ThemeProperties::COLOR_TOOLBAR_BOTTOM_SEPARATOR] =
+      colors_[ThemeProperties::COLOR_DETACHED_BOOKMARK_BAR_SEPARATOR] =
+          GetBorderColor("GtkToolbar.primary-toolbar.toolbar");
+#endif
 }
 
 void Gtk2UI::LoadCursorTheme() {
@@ -921,58 +925,16 @@
   GetChromeStyleColor("incognito-inactive-frame-color", &temp_color);
   colors_[ThemeProperties::COLOR_FRAME_INCOGNITO_INACTIVE] = temp_color;
 #else
-  auto set_frame_color = [this](int color_id) {
-    // Render a GtkHeaderBar as our title bar, cropping out any curved edges
-    // on the left and right sides. Also remove the bottom border for good
-    // measure.
-    SkBitmap bitmap;
-    bitmap.allocN32Pixels(1, 1);
-    bitmap.eraseColor(0);
-
-    static GtkWidget* menu = nullptr;
-    if (!menu) {
-      menu = gtk_menu_bar_new();
-      gtk_widget_set_size_request(menu, 1, 1);
-
-      GtkWidget* window = gtk_offscreen_window_new();
-      gtk_container_add(GTK_CONTAINER(window), menu);
-
-      gtk_widget_show_all(window);
-    }
-
-    cairo_surface_t* surface = cairo_image_surface_create_for_data(
-        static_cast<unsigned char*>(bitmap.getAddr(0, 0)), CAIRO_FORMAT_ARGB32,
-        bitmap.width(), bitmap.height(),
-        cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, 1));
-    cairo_t* cr = cairo_create(surface);
-    gtk_widget_draw(menu, cr);
-    cairo_destroy(cr);
-    cairo_surface_destroy(surface);
-
-    switch (color_id) {
-      case ThemeProperties::COLOR_FRAME_INACTIVE:
-        bitmap = SkBitmapOperations::CreateHSLShiftedBitmap(
-            bitmap, kDefaultTintFrameInactive);
-        break;
-      case ThemeProperties::COLOR_FRAME_INCOGNITO:
-        bitmap = SkBitmapOperations::CreateHSLShiftedBitmap(
-            bitmap, kDefaultTintFrameIncognito);
-        break;
-      case ThemeProperties::COLOR_FRAME_INCOGNITO_INACTIVE:
-        bitmap = SkBitmapOperations::CreateHSLShiftedBitmap(
-            bitmap, kDefaultTintFrameIncognitoInactive);
-        break;
-    }
-
-    bitmap.lockPixels();
-    colors_[color_id] = bitmap.getColor(0, 0);
-    bitmap.unlockPixels();
-  };
-
-  set_frame_color(ThemeProperties::COLOR_FRAME);
-  set_frame_color(ThemeProperties::COLOR_FRAME_INACTIVE);
-  set_frame_color(ThemeProperties::COLOR_FRAME_INCOGNITO);
-  set_frame_color(ThemeProperties::COLOR_FRAME_INCOGNITO_INACTIVE);
+  // TODO(thomasanderson): Render a GtkHeaderBar directly.
+  SkColor color_frame = GetBGColor(".headerbar.header-bar.titlebar");
+  SkColor color_frame_inactive =
+      GetBGColor(".headerbar.header-bar.titlebar:backdrop");
+  colors_[ThemeProperties::COLOR_FRAME] = color_frame;
+  colors_[ThemeProperties::COLOR_FRAME_INACTIVE] = color_frame_inactive;
+  colors_[ThemeProperties::COLOR_FRAME_INCOGNITO] =
+      color_utils::HSLShift(color_frame, kDefaultTintFrameIncognito);
+  colors_[ThemeProperties::COLOR_FRAME_INCOGNITO_INACTIVE] =
+      color_utils::HSLShift(color_frame_inactive, kDefaultTintFrameIncognito);
 #endif
 }
 
diff --git a/chrome/browser/ui/libgtkui/gtk_util.cc b/chrome/browser/ui/libgtkui/gtk_util.cc
index aab9717..caed023 100644
--- a/chrome/browser/ui/libgtkui/gtk_util.cc
+++ b/chrome/browser/ui/libgtkui/gtk_util.cc
@@ -14,6 +14,9 @@
 #include "base/command_line.h"
 #include "base/debug/leak_annotations.h"
 #include "base/environment.h"
+#include "base/strings/string_split.h"
+#include "base/strings/string_tokenizer.h"
+#include "base/strings/string_util.h"
 #include "ui/aura/window.h"
 #include "ui/aura/window_tree_host.h"
 #include "ui/base/accelerators/accelerator.h"
@@ -188,4 +191,218 @@
   g_object_set_data(G_OBJECT(dialog), kAuraTransientParent, NULL);
 }
 
+#if GTK_MAJOR_VERSION > 2
+ScopedStyleContext AppendNode(GtkStyleContext* context,
+                              const std::string& css_node) {
+  GtkWidgetPath* path =
+      context ? gtk_widget_path_copy(gtk_style_context_get_path(context))
+              : gtk_widget_path_new();
+
+  enum {
+    // TODO(thomasanderson): Add CSS_NAME here to handle the Gtk3.20 case.
+    CSS_TYPE,
+    CSS_CLASS,
+    CSS_PSEUDOCLASS,
+  } part_type = CSS_TYPE;
+  static const struct {
+    const char* name;
+    GtkStateFlags state_flag;
+  } pseudo_classes[] = {
+      {"active", GTK_STATE_FLAG_ACTIVE},
+      {"hover", GTK_STATE_FLAG_PRELIGHT},
+      {"selected", GTK_STATE_FLAG_SELECTED},
+      {"disabled", GTK_STATE_FLAG_INSENSITIVE},
+      {"indeterminate", GTK_STATE_FLAG_INCONSISTENT},
+      {"focus", GTK_STATE_FLAG_FOCUSED},
+      {"backdrop", GTK_STATE_FLAG_BACKDROP},
+      // TODO(thomasanderson): These state flags are only available in
+      // GTK 3.10 or later, which is unavailable in the wheezy
+      // sysroot.  Add them once the sysroot is updated to jessie.
+      // { "link",          GTK_STATE_FLAG_LINK },
+      // { "visited",       GTK_STATE_FLAG_VISITED },
+      // { "checked",       GTK_STATE_FLAG_CHECKED },
+  };
+  GtkStateFlags state =
+      context ? gtk_style_context_get_state(context) : GTK_STATE_FLAG_NORMAL;
+  base::StringTokenizer t(css_node, ".:");
+  t.set_options(base::StringTokenizer::RETURN_DELIMS);
+  while (t.GetNext()) {
+    if (t.token_is_delim()) {
+      if (t.token_begin() == css_node.begin()) {
+        // Special case for the first token.
+        gtk_widget_path_append_type(path, G_TYPE_NONE);
+      }
+      switch (*t.token_begin()) {
+        case '.':
+          part_type = CSS_CLASS;
+          break;
+        case ':':
+          part_type = CSS_PSEUDOCLASS;
+          break;
+        default:
+          NOTREACHED();
+      }
+    } else {
+      switch (part_type) {
+        case CSS_TYPE: {
+          GType type = g_type_from_name(t.token().c_str());
+          DCHECK(type);
+          gtk_widget_path_append_type(path, type);
+          break;
+        }
+        case CSS_CLASS: {
+          gtk_widget_path_iter_add_class(path, -1, t.token().c_str());
+          break;
+        }
+        case CSS_PSEUDOCLASS: {
+          GtkStateFlags state_flag = GTK_STATE_FLAG_NORMAL;
+          for (const auto& pseudo_class_entry : pseudo_classes) {
+            if (strcmp(pseudo_class_entry.name, t.token().c_str()) == 0) {
+              state_flag = pseudo_class_entry.state_flag;
+              break;
+            }
+          }
+          state = static_cast<GtkStateFlags>(state | state_flag);
+          break;
+        }
+      }
+    }
+  }
+  auto child_context = ScopedStyleContext(gtk_style_context_new());
+  gtk_style_context_set_path(child_context, path);
+  gtk_style_context_set_state(child_context, state);
+  gtk_style_context_set_parent(child_context, context);
+  gtk_widget_path_unref(path);
+  return child_context;
+}
+
+ScopedStyleContext GetStyleContextFromCss(const char* css_selector) {
+  // Prepend "GtkWindow.background" to the selector since all widgets must live
+  // in a window, but we don't want to specify that every time.
+  auto context = AppendNode(nullptr, "GtkWindow.background");
+
+  for (const auto& widget_type :
+       base::SplitString(css_selector, base::kWhitespaceASCII,
+                         base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY)) {
+    context = AppendNode(context, widget_type);
+  }
+  return context;
+}
+
+SkColor GdkRgbaToSkColor(const GdkRGBA& color) {
+  return SkColorSetARGB(color.alpha * 255, color.red * 255, color.green * 255,
+                        color.blue * 255);
+}
+
+SkColor GetFGColor(const char* css_selector) {
+  auto context = GetStyleContextFromCss(css_selector);
+  GdkRGBA color;
+  gtk_style_context_get_color(context, gtk_style_context_get_state(context),
+                              &color);
+  return GdkRgbaToSkColor(color);
+}
+
+GtkCssProvider* GetCssProvider(const char* css) {
+  GtkCssProvider* provider = gtk_css_provider_new();
+  GError* error = nullptr;
+  gtk_css_provider_load_from_data(provider, css, -1, &error);
+  DCHECK(!error);
+  return provider;
+}
+
+void ApplyCssToContext(GtkStyleContext* context, GtkCssProvider* provider) {
+  while (context) {
+    gtk_style_context_add_provider(context, GTK_STYLE_PROVIDER(provider),
+                                   G_MAXUINT);
+    context = gtk_style_context_get_parent(context);
+  }
+}
+
+void RemoveBorders(GtkStyleContext* context) {
+  static GtkCssProvider* provider = GetCssProvider(
+      "* {"
+      "border-style: none;"
+      "border-radius: 0px;"
+      "border-width: 0px;"
+      "border-image-width: 0px;"
+      "padding: 0px;"
+      "margin: 0px;"
+      "}");
+  ApplyCssToContext(context, provider);
+}
+
+void AddBorders(GtkStyleContext* context) {
+  static GtkCssProvider* provider = GetCssProvider(
+      "* {"
+      "border-style: solid;"
+      "border-radius: 0px;"
+      "border-width: 1px;"
+      "padding: 0px;"
+      "margin: 0px;"
+      "}");
+  ApplyCssToContext(context, provider);
+}
+
+// A 1x1 cairo surface that GTK can render into.
+class PixelSurface {
+ public:
+  PixelSurface()
+      : surface_(cairo_image_surface_create(CAIRO_FORMAT_ARGB32, 1, 1)),
+        cairo_(cairo_create(surface_)) {}
+
+  ~PixelSurface() {
+    cairo_destroy(cairo_);
+    cairo_surface_destroy(surface_);
+  }
+
+  // Get the drawing context for GTK to use.
+  cairo_t* cairo() { return cairo_; }
+
+  // Get the color value of the single pixel.
+  SkColor GetPixelValue() {
+    return *reinterpret_cast<SkColor*>(cairo_image_surface_get_data(surface_));
+  }
+
+ private:
+  cairo_surface_t* surface_;
+  cairo_t* cairo_;
+};
+
+SkColor GetBGColor(const char* css_selector) {
+  // Backgrounds are more general than solid colors (eg. gradients),
+  // but chromium requires us to boil this down to one color.  We
+  // cannot use the background-color here because some themes leave it
+  // set to a garbage color because a background-image will cover it
+  // anyway.  So we instead render the background into a single pixel,
+  // removing any borders, and hope that we get a good color.
+  auto context = GetStyleContextFromCss(css_selector);
+  RemoveBorders(context);
+  PixelSurface surface;
+  gtk_render_background(context, surface.cairo(), 0, 0, 1, 1);
+  return surface.GetPixelValue();
+}
+
+SkColor GetBorderColor(const char* css_selector) {
+  // Borders have the same issue as backgrounds, due to the
+  // border-image property.
+  auto context = GetStyleContextFromCss(css_selector);
+  GtkStateFlags state = gtk_style_context_get_state(context);
+  GtkBorderStyle border_style = GTK_BORDER_STYLE_NONE;
+  gtk_style_context_get(context, state, GTK_STYLE_PROPERTY_BORDER_STYLE,
+                        &border_style, nullptr);
+  GtkBorder border;
+  gtk_style_context_get_border(context, state, &border);
+  if ((border_style == GTK_BORDER_STYLE_NONE ||
+       border_style == GTK_BORDER_STYLE_HIDDEN) ||
+      (!border.left && !border.right && !border.top && !border.bottom)) {
+    return SK_ColorTRANSPARENT;
+  }
+
+  AddBorders(context);
+  PixelSurface surface;
+  gtk_render_frame(context, surface.cairo(), 0, 0, 1, 1);
+  return surface.GetPixelValue();
+}
+#endif
+
 }  // namespace libgtkui
diff --git a/chrome/browser/ui/libgtkui/gtk_util.h b/chrome/browser/ui/libgtkui/gtk_util.h
index ab174c2..fc9aa31 100644
--- a/chrome/browser/ui/libgtkui/gtk_util.h
+++ b/chrome/browser/ui/libgtkui/gtk_util.h
@@ -67,6 +67,74 @@
 // Clears the transient parent for |dialog|.
 void ClearAuraTransientParent(GtkWidget* dialog);
 
+#if GTK_MAJOR_VERSION > 2
+template <typename T>
+class ScopedGObject {
+ public:
+  explicit ScopedGObject(T* obj) : obj_(obj) {
+    // Increase the reference count of |obj_|, removing the floating
+    // reference if it has one.
+    g_object_ref_sink(obj_);
+  }
+
+  ScopedGObject(const ScopedGObject<T>& other) : obj_(other.obj_) {
+    g_object_ref(obj_);
+  }
+
+  ScopedGObject(ScopedGObject<T>&& other) : obj_(other.obj_) {
+    other.obj_ = nullptr;
+  }
+
+  ~ScopedGObject() {
+    if (obj_)
+      g_object_unref(obj_);
+  }
+
+  ScopedGObject<T>& operator=(const ScopedGObject<T>& other) {
+    g_object_ref(other.obj_);
+    g_object_unref(obj_);
+    obj_ = other.obj_;
+    return *this;
+  }
+
+  ScopedGObject<T>& operator=(ScopedGObject<T>&& other) {
+    g_object_unref(obj_);
+    obj_ = other.obj_;
+    other.obj_ = nullptr;
+    return *this;
+  }
+
+  operator T*() { return obj_; }
+
+ private:
+  T* obj_;
+};
+
+typedef ScopedGObject<GtkStyleContext> ScopedStyleContext;
+
+// Parses |css_selector| into a GtkStyleContext.  The format is a
+// sequence of whitespace-separated objects.  Each object may have at
+// most one object name at the beginning of the string, and any number
+// of '.'-prefixed classes and ':'-prefixed pseudoclasses.  An example
+// is "GtkButton.button.suggested-action:hover:active".  The caller
+// must g_object_unref() the returned context.
+ScopedStyleContext GetStyleContextFromCss(const char* css_selector);
+
+// Get the 'color' property from the style context created by
+// GetStyleContextFromCss(|css_selector|).
+SkColor GetFGColor(const char* css_selector);
+
+// Renders a background from the style context created by
+// GetStyleContextFromCss(|css_selector|) into a single pixel and
+// returns the color.
+SkColor GetBGColor(const char* css_selector);
+
+// If there is a border, renders the border from the style context
+// created by GetStyleContextFromCss(|css_selector|) into a single
+// pixel and returns the color.  Otherwise returns kInvalidColor.
+SkColor GetBorderColor(const char* css_selector);
+#endif
+
 }  // namespace libgtkui
 
 #endif  // CHROME_BROWSER_UI_LIBGTKUI_GTK_UTIL_H_
diff --git a/chrome/browser/ui/libgtkui/native_theme_gtk3.cc b/chrome/browser/ui/libgtkui/native_theme_gtk3.cc
index 097b80a..b91c783b 100644
--- a/chrome/browser/ui/libgtkui/native_theme_gtk3.cc
+++ b/chrome/browser/ui/libgtkui/native_theme_gtk3.cc
@@ -6,12 +6,11 @@
 
 #include <gtk/gtk.h>
 
-#include "base/strings/string_split.h"
-#include "base/strings/string_util.h"
 #include "chrome/browser/ui/libgtkui/chrome_gtk_frame.h"
 #include "chrome/browser/ui/libgtkui/chrome_gtk_menu_subclasses.h"
 #include "chrome/browser/ui/libgtkui/gtk_util.h"
 #include "chrome/browser/ui/libgtkui/skia_utils_gtk.h"
+#include "ui/gfx/color_palette.h"
 #include "ui/gfx/color_utils.h"
 #include "ui/gfx/geometry/rect.h"
 #include "ui/native_theme/native_theme_dark_aura.h"
@@ -20,92 +19,6 @@
 
 namespace {
 
-enum WidgetState {
-  NORMAL = 0,
-  ACTIVE = 1,
-  PRELIGHT = 2,
-  SELECTED = 3,
-  INSENSITIVE = 4,
-};
-
-// Same order as enum WidgetState above
-const GtkStateFlags stateMap[] = {
-    GTK_STATE_FLAG_NORMAL,      GTK_STATE_FLAG_ACTIVE,
-    GTK_STATE_FLAG_PRELIGHT,    GTK_STATE_FLAG_SELECTED,
-    GTK_STATE_FLAG_INSENSITIVE,
-};
-
-// The caller must g_object_unref the returned context.
-GtkStyleContext* GetStyleContextFromCss(const char* css_selector) {
-  GtkWidgetPath* path = gtk_widget_path_new();
-  for (const auto& widget_type :
-       base::SplitString(css_selector, base::kWhitespaceASCII,
-                         base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY)) {
-    gtk_widget_path_append_type(path, G_TYPE_NONE);
-    for (const auto& widget_class :
-         base::SplitString(widget_type, ".", base::TRIM_WHITESPACE,
-                           base::SPLIT_WANT_NONEMPTY)) {
-      gtk_widget_path_iter_add_class(path, -1, widget_class.c_str());
-    }
-  }
-
-  GtkStyleContext* context = gtk_style_context_new();
-  gtk_style_context_set_path(context, path);
-  gtk_widget_path_unref(path);
-  return context;
-}
-
-SkColor GdkRgbaToSkColor(const GdkRGBA& color) {
-  return SkColorSetARGB(color.alpha * 255, color.red * 255, color.green * 255,
-                        color.blue * 255);
-}
-
-SkColor ColorFromContext(GtkStyleContext* context,
-                         GtkStateFlags state,
-                         const char* color_name) {
-  GdkRGBA* color = nullptr;
-  gtk_style_context_get(context, state, color_name, &color, nullptr);
-  DCHECK(color);
-  SkColor sk_color = GdkRgbaToSkColor(*color);
-  gdk_rgba_free(color);
-  return sk_color;
-}
-
-SkColor GetFGColor(GtkWidget* widget, WidgetState state) {
-  return ColorFromContext(gtk_widget_get_style_context(widget), stateMap[state],
-                          "color");
-}
-
-SkColor GetBGColor(GtkWidget* widget, WidgetState state) {
-  return ColorFromContext(gtk_widget_get_style_context(widget), stateMap[state],
-                          "background-color");
-}
-
-SkColor GetFGColor(const char* css_selector, GtkStateFlags state) {
-  GtkStyleContext* context = GetStyleContextFromCss(css_selector);
-  SkColor color = ColorFromContext(context, state, "color");
-  g_object_unref(context);
-  return color;
-}
-
-SkColor GetBGColor(const char* css_selector, GtkStateFlags state) {
-  GtkStyleContext* context = GetStyleContextFromCss(css_selector);
-  SkColor color = ColorFromContext(context, state, "background-color");
-  g_object_unref(context);
-  return color;
-}
-
-SkColor GetBorderColor(const char* css_selector, GtkStateFlags state) {
-  GtkStyleContext* context = GetStyleContextFromCss(css_selector);
-  GtkBorder border;
-  gtk_style_context_get_border(context, state, &border);
-  bool has_border = border.left || border.right || border.top || border.bottom;
-  SkColor color = ColorFromContext(
-      context, state, has_border ? "border-color" : "background-color");
-  g_object_unref(context);
-  return color;
-}
-
 void PaintWidget(SkCanvas* canvas,
                  const gfx::Rect& rect,
                  const char* css_selector,
@@ -120,7 +33,7 @@
       cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, rect.width()));
   cairo_t* cr = cairo_create(surface);
 
-  GtkStyleContext* context = GetStyleContextFromCss(css_selector);
+  auto context = GetStyleContextFromCss(css_selector);
   gtk_style_context_set_state(context, state);
 
   gtk_render_background(context, cr, 0, 0, rect.width(), rect.height());
@@ -128,8 +41,6 @@
   cairo_destroy(cr);
   cairo_surface_destroy(surface);
   canvas->drawBitmap(bitmap, rect.x(), rect.y());
-
-  g_object_unref(context);
 }
 
 GtkStateFlags StateToStateFlags(NativeThemeGtk3::State state) {
@@ -149,6 +60,232 @@
   }
 }
 
+SkColor SkColorFromColorId(ui::NativeTheme::ColorId color_id) {
+  const SkColor kPositiveTextColor = SkColorSetRGB(0x0b, 0x80, 0x43);
+  const SkColor kNegativeTextColor = SkColorSetRGB(0xc5, 0x39, 0x29);
+
+  switch (color_id) {
+    // Windows
+    case ui::NativeTheme::kColorId_WindowBackground:
+    // Dialogs
+    case ui::NativeTheme::kColorId_DialogBackground:
+    case ui::NativeTheme::kColorId_BubbleBackground:
+      return GetBGColor("");
+
+    // FocusableBorder
+    case ui::NativeTheme::kColorId_FocusedBorderColor:
+      return GetBorderColor("GtkEntry.entry:focus");
+    case ui::NativeTheme::kColorId_UnfocusedBorderColor:
+      return GetBorderColor("GtkEntry.entry");
+
+    // Menu
+    case ui::NativeTheme::kColorId_MenuBackgroundColor:
+      return GetBGColor("GtkMenu.menu");
+    case ui::NativeTheme::kColorId_MenuBorderColor:
+      return GetBorderColor("GtkMenu.menu");
+    case ui::NativeTheme::kColorId_FocusedMenuItemBackgroundColor:
+      return GetBGColor("GtkMenu.menu GtkMenuItem.menuitem:focus");
+    case ui::NativeTheme::kColorId_EnabledMenuItemForegroundColor:
+      return GetFGColor("GtkMenu.menu GtkMenuItem.menuitem GtkLabel.label");
+    case ui::NativeTheme::kColorId_SelectedMenuItemForegroundColor:
+      return GetFGColor(
+          "GtkMenu.menu GtkMenuItem.menuitem:selected GtkLabel.label");
+    case ui::NativeTheme::kColorId_DisabledMenuItemForegroundColor:
+      return GetFGColor(
+          "GtkMenu.menu GtkMenuItem.menuitem:disabled GtkLabel.label");
+    case ui::NativeTheme::kColorId_MenuItemSubtitleColor:
+      return GetFGColor(
+          "GtkMenu.menu GtkMenuItem.menuitem GtkLabel.label.accelerator");
+    case ui::NativeTheme::kColorId_MenuSeparatorColor:
+    // MenuButton borders are used the same way as menu separators in Chrome.
+    case ui::NativeTheme::kColorId_EnabledMenuButtonBorderColor:
+    case ui::NativeTheme::kColorId_FocusedMenuButtonBorderColor:
+    case ui::NativeTheme::kColorId_HoverMenuButtonBorderColor:
+      return GetFGColor("GtkMenu.menu GtkMenuItem.menuitem.separator:disabled");
+
+    // Label
+    case ui::NativeTheme::kColorId_LabelEnabledColor:
+      return GetFGColor("GtkLabel.label");
+    case ui::NativeTheme::kColorId_LabelDisabledColor:
+      return GetFGColor("GtkLabel.label:disabled");
+    case ui::NativeTheme::kColorId_LabelTextSelectionColor:
+      return GetFGColor("GtkLabel.label .selection:selected");
+    case ui::NativeTheme::kColorId_LabelTextSelectionBackgroundFocused:
+      return GetBGColor("GtkLabel.label .selection:selected");
+
+    // Link
+    case ui::NativeTheme::kColorId_LinkDisabled:
+      return SkColorSetA(
+          SkColorFromColorId(ui::NativeTheme::kColorId_LinkEnabled), 0xBB);
+    case ui::NativeTheme::kColorId_LinkPressed:
+    case ui::NativeTheme::kColorId_LinkEnabled: {
+      // TODO(thomasanderson): Gtk changed the way links are colored in 3.12.
+      // Add code for later versions.
+      auto link_context = GetStyleContextFromCss("GtkLabel.label.view");
+      GdkColor* color;
+      gtk_style_context_get_style(link_context, "link-color", &color, nullptr);
+      if (color) {
+        SkColor ret_color = SkColorSetRGB(color->red / 255, color->green / 255,
+                                          color->blue / 255);
+        gdk_color_free(color);
+        return ret_color;
+      } else {
+        // Default color comes from gtklinkbutton.c.
+        return SkColorSetRGB(0x00, 0x00, 0xEE);
+      }
+    }
+
+    // Button
+    case ui::NativeTheme::kColorId_ButtonEnabledColor:
+      return GetFGColor("GtkButton.button.text-button GtkLabel.label");
+    case ui::NativeTheme::kColorId_ButtonDisabledColor:
+      return GetFGColor("GtkButton.button.text-button:disabled GtkLabel.label");
+    case ui::NativeTheme::kColorId_ButtonHoverColor:
+      return GetFGColor("GtkButton.button.text-button:hover GtkLabel.label");
+    case ui::NativeTheme::kColorId_ButtonPressedShade:
+      return SK_ColorTRANSPARENT;
+
+    case ui::NativeTheme::kColorId_BlueButtonEnabledColor:
+      return GetFGColor(
+          "GtkButton.button.text-button.suggested-action GtkLabel.label");
+    case ui::NativeTheme::kColorId_BlueButtonDisabledColor:
+      return GetFGColor(
+          "GtkButton.button.text-button.suggested-action:disabled "
+          "GtkLabel.label");
+    case ui::NativeTheme::kColorId_BlueButtonHoverColor:
+      return GetFGColor(
+          "GtkButton.button.text-button.suggested-action:hover GtkLabel.label");
+    case ui::NativeTheme::kColorId_BlueButtonPressedColor:
+      return GetFGColor(
+          "GtkButton.button.text-button.suggested-action:hover:active "
+          "GtkLabel.label");
+    case ui::NativeTheme::kColorId_BlueButtonShadowColor:
+      return SK_ColorTRANSPARENT;
+
+    case ui::NativeTheme::kColorId_ProminentButtonColor:
+      return GetBGColor("GtkButton.button.text-button.destructive-action");
+    case ui::NativeTheme::kColorId_TextOnProminentButtonColor:
+      return GetFGColor(
+          "GtkButton.button.text-button.destructive-action GtkLabel.label");
+
+    // Textfield
+    case ui::NativeTheme::kColorId_TextfieldDefaultColor:
+      return GetFGColor("GtkEntry.entry");
+    case ui::NativeTheme::kColorId_TextfieldDefaultBackground:
+      return GetBGColor("GtkEntry.entry");
+    case ui::NativeTheme::kColorId_TextfieldReadOnlyColor:
+      return GetFGColor("GtkEntry.entry:disabled");
+    case ui::NativeTheme::kColorId_TextfieldReadOnlyBackground:
+      return GetBGColor("GtkEntry.entry:disabled");
+    case ui::NativeTheme::kColorId_TextfieldSelectionColor:
+      return GetFGColor("GtkEntry.entry .selection:selected");
+    case ui::NativeTheme::kColorId_TextfieldSelectionBackgroundFocused:
+      return GetBGColor("GtkEntry.entry .selection:selected");
+
+    // Tooltips
+    case ui::NativeTheme::kColorId_TooltipBackground:
+      return GetBGColor("GtkTooltip.tooltip");
+    case ui::NativeTheme::kColorId_TooltipText:
+      return color_utils::GetReadableColor(GetFGColor("GtkTooltip.tooltip"),
+                                           GetBGColor("GtkTooltip.tooltip"));
+
+    // Trees and Tables (implemented on GTK using the same class)
+    case ui::NativeTheme::kColorId_TableBackground:
+    case ui::NativeTheme::kColorId_TreeBackground:
+      return GetBGColor("GtkTreeView.view");
+    case ui::NativeTheme::kColorId_TableText:
+    case ui::NativeTheme::kColorId_TreeText:
+    case ui::NativeTheme::kColorId_TreeArrow:
+    case ui::NativeTheme::kColorId_TableGroupingIndicatorColor:
+      return GetFGColor("GtkTreeView.view .cell");
+    case ui::NativeTheme::kColorId_TableSelectedText:
+    case ui::NativeTheme::kColorId_TableSelectedTextUnfocused:
+    case ui::NativeTheme::kColorId_TreeSelectedText:
+    case ui::NativeTheme::kColorId_TreeSelectedTextUnfocused:
+      return GetFGColor("GtkTreeView.view .cell:selected");
+    case ui::NativeTheme::kColorId_TableSelectionBackgroundFocused:
+    case ui::NativeTheme::kColorId_TableSelectionBackgroundUnfocused:
+    case ui::NativeTheme::kColorId_TreeSelectionBackgroundFocused:
+    case ui::NativeTheme::kColorId_TreeSelectionBackgroundUnfocused:
+      return GetBGColor("GtkTreeView.view .cell:selected");
+
+    // Results Table
+    // TODO(thomasanderson): The GtkEntry selectors was how the gtk2 theme got
+    // these colors.  Update this code to use a different widget.
+    case ui::NativeTheme::kColorId_ResultsTableNormalBackground:
+      return GetBGColor("GtkEntry.entry");
+    case ui::NativeTheme::kColorId_ResultsTableHoveredBackground:
+      return color_utils::AlphaBlend(
+          GetBGColor("GtkEntry.entry"),
+          GetBGColor("GtkEntry.entry .selection:selected"), 0x80);
+    case ui::NativeTheme::kColorId_ResultsTableSelectedBackground:
+      return GetBGColor("GtkEntry.entry .selection:selected");
+    case ui::NativeTheme::kColorId_ResultsTableNormalText:
+    case ui::NativeTheme::kColorId_ResultsTableHoveredText:
+      return GetFGColor("GtkEntry.entry");
+    case ui::NativeTheme::kColorId_ResultsTableSelectedText:
+      return GetFGColor("GtkEntry.entry .selection:selected");
+    case ui::NativeTheme::kColorId_ResultsTableNormalDimmedText:
+    case ui::NativeTheme::kColorId_ResultsTableHoveredDimmedText:
+      return color_utils::AlphaBlend(GetFGColor("GtkEntry.entry"),
+                                     GetBGColor("GtkEntry.entry"), 0x80);
+    case ui::NativeTheme::kColorId_ResultsTableSelectedDimmedText:
+      return color_utils::AlphaBlend(
+          GetFGColor("GtkEntry.entry .selection:selected"),
+          GetBGColor("GtkEntry.entry"), 0x80);
+    case ui::NativeTheme::kColorId_ResultsTableNormalUrl:
+    case ui::NativeTheme::kColorId_ResultsTableHoveredUrl:
+      return NormalURLColor(GetFGColor("GtkEntry.entry"));
+    case ui::NativeTheme::kColorId_ResultsTableSelectedUrl:
+      return SelectedURLColor(GetFGColor("GtkEntry.entry .selection:selected"),
+                              GetBGColor("GtkEntry.entry .selection:selected"));
+
+    case ui::NativeTheme::kColorId_ResultsTablePositiveText:
+      return color_utils::GetReadableColor(kPositiveTextColor,
+                                           GetBGColor("GtkEntry.entry"));
+    case ui::NativeTheme::kColorId_ResultsTablePositiveHoveredText:
+      return color_utils::GetReadableColor(kPositiveTextColor,
+                                           GetBGColor("GtkEntry.entry:hover"));
+    case ui::NativeTheme::kColorId_ResultsTablePositiveSelectedText:
+      return color_utils::GetReadableColor(
+          kPositiveTextColor, GetBGColor("GtkEntry.entry:selected"));
+    case ui::NativeTheme::kColorId_ResultsTableNegativeText:
+      return color_utils::GetReadableColor(kNegativeTextColor,
+                                           GetBGColor("GtkEntry.entry"));
+    case ui::NativeTheme::kColorId_ResultsTableNegativeHoveredText:
+      return color_utils::GetReadableColor(kNegativeTextColor,
+                                           GetBGColor("GtkEntry.entry:hover"));
+    case ui::NativeTheme::kColorId_ResultsTableNegativeSelectedText:
+      return color_utils::GetReadableColor(
+          kNegativeTextColor, GetBGColor("GtkEntry.entry:selected"));
+
+    // Throbber
+    // TODO(thomasanderson): Render GtkSpinner directly.
+    case ui::NativeTheme::kColorId_ThrobberSpinningColor:
+    case ui::NativeTheme::kColorId_ThrobberWaitingColor:
+      return GetFGColor("GtkMenu.menu GtkSpinner.spinner");
+    case ui::NativeTheme::kColorId_ThrobberLightColor:
+      return GetFGColor("GtkMenu.menu GtkSpinner.spinner:disabled");
+
+    // Alert icons
+    case ui::NativeTheme::kColorId_AlertSeverityLow:
+      return GetBGColor("GtkInfoBar.infobar.info");
+    case ui::NativeTheme::kColorId_AlertSeverityMedium:
+      return GetBGColor("GtkInfoBar.infobar.warning");
+    case ui::NativeTheme::kColorId_AlertSeverityHigh:
+      return GetBGColor("GtkInfoBar.infobar.error");
+
+    case ui::NativeTheme::kColorId_NumColors:
+      NOTREACHED();
+      break;
+  }
+  return kInvalidColorIdColor;
+}
+
+void OnThemeChanged(GObject* obj, GParamSpec* param, NativeThemeGtk3* theme) {
+  theme->ResetColorCache();
+}
+
 }  // namespace
 
 // static
@@ -158,242 +295,50 @@
 }
 
 // Constructors automatically called
-NativeThemeGtk3::NativeThemeGtk3() {}
+NativeThemeGtk3::NativeThemeGtk3() {
+  // These types are needed by g_type_from_name(), but may not be registered at
+  // this point.  We need the g_type_class magic to make sure the compiler
+  // doesn't optimize away this code.
+  g_type_class_unref(g_type_class_ref(gtk_button_get_type()));
+  g_type_class_unref(g_type_class_ref(gtk_label_get_type()));
+  g_type_class_unref(g_type_class_ref(gtk_window_get_type()));
+  g_type_class_unref(g_type_class_ref(gtk_link_button_get_type()));
+  g_type_class_unref(g_type_class_ref(gtk_spinner_get_type()));
+  g_type_class_unref(g_type_class_ref(gtk_menu_get_type()));
+  g_type_class_unref(g_type_class_ref(gtk_menu_item_get_type()));
+  g_type_class_unref(g_type_class_ref(gtk_entry_get_type()));
+  g_type_class_unref(g_type_class_ref(gtk_info_bar_get_type()));
+  g_type_class_unref(g_type_class_ref(gtk_tooltip_get_type()));
+  g_type_class_unref(g_type_class_ref(gtk_scrollbar_get_type()));
+  g_type_class_unref(g_type_class_ref(gtk_toolbar_get_type()));
+  g_type_class_unref(g_type_class_ref(gtk_text_view_get_type()));
+
+  g_signal_connect_after(gtk_settings_get_default(), "notify::gtk-theme-name",
+                         G_CALLBACK(OnThemeChanged), this);
+}
+
 // This doesn't actually get called
 NativeThemeGtk3::~NativeThemeGtk3() {}
 
-SkColor NativeThemeGtk3::LookupGtkThemeColor(ColorId color_id) const {
-  const SkColor kPositiveTextColor = SkColorSetRGB(0x0b, 0x80, 0x43);
-  const SkColor kNegativeTextColor = SkColorSetRGB(0xc5, 0x39, 0x29);
-
-  switch (color_id) {
-    // Windows
-    case kColorId_WindowBackground:
-      return GetBGColor(GetWindow(), SELECTED);
-
-    // Dialogs
-    case kColorId_DialogBackground:
-    case kColorId_BubbleBackground:
-      return GetBGColor(GetWindow(), NORMAL);
-
-    // FocusableBorder
-    case kColorId_FocusedBorderColor:
-      return GetBGColor(GetEntry(), SELECTED);
-    case kColorId_UnfocusedBorderColor:
-      return GetFGColor(GetEntry(), NORMAL);
-
-    // Menu
-    case kColorId_MenuBackgroundColor:
-      return GetBGColor("menu", GTK_STATE_FLAG_NORMAL);
-    case kColorId_MenuBorderColor:
-      return GetBorderColor("menu", GTK_STATE_FLAG_NORMAL);
-    case kColorId_FocusedMenuItemBackgroundColor:
-      return GetBGColor("menu menuitem", GTK_STATE_FLAG_FOCUSED);
-    case kColorId_EnabledMenuItemForegroundColor:
-      return GetFGColor("menu menuitem label", GTK_STATE_FLAG_NORMAL);
-    case kColorId_SelectedMenuItemForegroundColor:
-      return GetFGColor("menu menuitem label", GTK_STATE_FLAG_SELECTED);
-    case kColorId_DisabledMenuItemForegroundColor:
-      return GetFGColor("menu menuitem label", GTK_STATE_FLAG_INSENSITIVE);
-    case kColorId_MenuItemSubtitleColor:
-      return GetFGColor("menu menuitem accelerator", GTK_STATE_FLAG_NORMAL);
-    case kColorId_MenuSeparatorColor:
-    // MenuButton borders are used the same way as menu separtors in Chrome.
-    case kColorId_EnabledMenuButtonBorderColor:
-    case kColorId_FocusedMenuButtonBorderColor:
-    case kColorId_HoverMenuButtonBorderColor:
-      return GetFGColor("menu menuitem.separator", GTK_STATE_FLAG_INSENSITIVE);
-
-    // Label
-    case kColorId_LabelEnabledColor:
-      return GetFGColor(GetEntry(), NORMAL);
-    case kColorId_LabelDisabledColor:
-      return GetFGColor(GetLabel(), INSENSITIVE);
-    case kColorId_LabelTextSelectionColor:
-      return GetFGColor(GetLabel(), SELECTED);
-    case kColorId_LabelTextSelectionBackgroundFocused:
-      return GetBGColor(GetLabel(), SELECTED);
-
-    // Link
-    case kColorId_LinkDisabled:
-      return SkColorSetA(GetSystemColor(kColorId_LinkEnabled), 0xBB);
-    case kColorId_LinkEnabled: {
-      SkColor link_color = SK_ColorTRANSPARENT;
-      GdkColor* style_color = nullptr;
-      gtk_widget_style_get(GetWindow(), "link-color", &style_color, nullptr);
-      if (style_color) {
-        link_color = GdkColorToSkColor(*style_color);
-        gdk_color_free(style_color);
-      }
-      if (link_color != SK_ColorTRANSPARENT)
-        return link_color;
-      // Default color comes from gtklinkbutton.c.
-      return SkColorSetRGB(0x00, 0x00, 0xEE);
-    }
-    case kColorId_LinkPressed:
-      return SK_ColorRED;
-
-    // Button
-    case kColorId_ButtonEnabledColor:
-      return GetFGColor(GetButton(), NORMAL);
-    case kColorId_BlueButtonEnabledColor:
-      return GetFGColor(GetBlueButton(), NORMAL);
-    case kColorId_ButtonDisabledColor:
-      return GetFGColor(GetButton(), INSENSITIVE);
-    case kColorId_BlueButtonDisabledColor:
-      return GetFGColor(GetBlueButton(), INSENSITIVE);
-    case kColorId_ButtonHoverColor:
-      return GetFGColor(GetButton(), PRELIGHT);
-    case kColorId_BlueButtonHoverColor:
-      return GetFGColor(GetBlueButton(), PRELIGHT);
-    case kColorId_BlueButtonPressedColor:
-      return GetFGColor(GetBlueButton(), ACTIVE);
-    case kColorId_BlueButtonShadowColor:
-      return SK_ColorTRANSPARENT;
-    case kColorId_ProminentButtonColor:
-      return GetSystemColor(kColorId_LinkEnabled);
-    case kColorId_TextOnProminentButtonColor:
-      return GetFGColor(GetLabel(), SELECTED);
-    case kColorId_ButtonPressedShade:
-      return SK_ColorTRANSPARENT;
-
-    // Textfield
-    case kColorId_TextfieldDefaultColor:
-      return GetFGColor(GetEntry(), NORMAL);
-    case kColorId_TextfieldDefaultBackground:
-      return GetBGColor(GetEntry(), NORMAL);
-
-    case kColorId_TextfieldReadOnlyColor:
-      return GetFGColor(GetEntry(), SELECTED);
-    case kColorId_TextfieldReadOnlyBackground:
-      return GetBGColor(GetEntry(), SELECTED);
-    case kColorId_TextfieldSelectionColor:
-      return GetFGColor(GetLabel(), SELECTED);
-    case kColorId_TextfieldSelectionBackgroundFocused:
-      return GetBGColor(GetLabel(), SELECTED);
-
-    // Tooltips
-    case kColorId_TooltipBackground:
-      return GetBGColor(GetTooltip(), NORMAL);
-    case kColorId_TooltipText:
-      return GetFGColor(GetTooltip(), NORMAL);
-
-    // Trees and Tables (implemented on GTK using the same class)
-    case kColorId_TableBackground:
-    case kColorId_TreeBackground:
-      return GetBGColor(GetTree(), NORMAL);
-    case kColorId_TableText:
-    case kColorId_TreeText:
-      return GetFGColor(GetTree(), NORMAL);
-    case kColorId_TableSelectedText:
-    case kColorId_TableSelectedTextUnfocused:
-    case kColorId_TreeSelectedText:
-    case kColorId_TreeSelectedTextUnfocused:
-      return GetFGColor(GetTree(), SELECTED);
-    case kColorId_TableSelectionBackgroundFocused:
-    case kColorId_TableSelectionBackgroundUnfocused:
-    case kColorId_TreeSelectionBackgroundFocused:
-    case kColorId_TreeSelectionBackgroundUnfocused:
-      return GetBGColor(GetTree(), SELECTED);
-    case kColorId_TreeArrow:
-      return GetFGColor(GetTree(), NORMAL);
-    case kColorId_TableGroupingIndicatorColor:
-      return GetFGColor(GetTree(), NORMAL);
-
-    // Results Table
-    case kColorId_ResultsTableNormalBackground:
-      return GetSystemColor(kColorId_TextfieldDefaultBackground);
-    case kColorId_ResultsTableHoveredBackground:
-      return color_utils::AlphaBlend(
-          GetSystemColor(kColorId_TextfieldDefaultBackground),
-          GetSystemColor(kColorId_TextfieldSelectionBackgroundFocused), 0x80);
-    case kColorId_ResultsTableSelectedBackground:
-      return GetSystemColor(kColorId_TextfieldSelectionBackgroundFocused);
-    case kColorId_ResultsTableNormalText:
-    case kColorId_ResultsTableHoveredText:
-      return GetSystemColor(kColorId_TextfieldDefaultColor);
-    case kColorId_ResultsTableSelectedText:
-      return GetSystemColor(kColorId_TextfieldSelectionColor);
-    case kColorId_ResultsTableNormalDimmedText:
-    case kColorId_ResultsTableHoveredDimmedText:
-      return color_utils::AlphaBlend(
-          GetSystemColor(kColorId_TextfieldDefaultColor),
-          GetSystemColor(kColorId_TextfieldDefaultBackground), 0x80);
-    case kColorId_ResultsTableSelectedDimmedText:
-      return color_utils::AlphaBlend(
-          GetSystemColor(kColorId_TextfieldSelectionColor),
-          GetSystemColor(kColorId_TextfieldDefaultBackground), 0x80);
-    case kColorId_ResultsTableNormalUrl:
-    case kColorId_ResultsTableHoveredUrl:
-      return NormalURLColor(GetSystemColor(kColorId_TextfieldDefaultColor));
-
-    case kColorId_ResultsTableSelectedUrl:
-      return SelectedURLColor(
-          GetSystemColor(kColorId_TextfieldSelectionColor),
-          GetSystemColor(kColorId_TextfieldSelectionBackgroundFocused));
-
-    case kColorId_ResultsTablePositiveText:
-      return color_utils::GetReadableColor(kPositiveTextColor,
-                                           GetBGColor(GetEntry(), NORMAL));
-    case kColorId_ResultsTablePositiveHoveredText:
-      return color_utils::GetReadableColor(kPositiveTextColor,
-                                           GetBGColor(GetEntry(), PRELIGHT));
-    case kColorId_ResultsTablePositiveSelectedText:
-      return color_utils::GetReadableColor(kPositiveTextColor,
-                                           GetBGColor(GetEntry(), SELECTED));
-    case kColorId_ResultsTableNegativeText:
-      return color_utils::GetReadableColor(kNegativeTextColor,
-                                           GetBGColor(GetEntry(), NORMAL));
-    case kColorId_ResultsTableNegativeHoveredText:
-      return color_utils::GetReadableColor(kNegativeTextColor,
-                                           GetBGColor(GetEntry(), PRELIGHT));
-    case kColorId_ResultsTableNegativeSelectedText:
-      return color_utils::GetReadableColor(kNegativeTextColor,
-                                           GetBGColor(GetEntry(), SELECTED));
-
-    // Throbber
-    case kColorId_ThrobberSpinningColor:
-    case kColorId_ThrobberLightColor:
-      return GetSystemColor(kColorId_TextfieldSelectionBackgroundFocused);
-
-    case kColorId_ThrobberWaitingColor:
-      return color_utils::AlphaBlend(
-          GetSystemColor(kColorId_TextfieldSelectionBackgroundFocused),
-          GetBGColor(GetWindow(), NORMAL), 0x80);
-
-    // Alert icons
-    // Just fall back to the same colors as Aura.
-    case kColorId_AlertSeverityLow:
-    case kColorId_AlertSeverityMedium:
-    case kColorId_AlertSeverityHigh:
-      return SK_ColorTRANSPARENT;
-
-    case kColorId_NumColors:
-      NOTREACHED();
-      break;
-  }
-
-  return kInvalidColorIdColor;
+void NativeThemeGtk3::ResetColorCache() {
+  for (auto& color : color_cache_)
+    color = base::nullopt;
 }
 
 SkColor NativeThemeGtk3::GetSystemColor(ColorId color_id) const {
-  SkColor color = LookupGtkThemeColor(color_id);
-  if (SkColorGetA(color))
-    return color;
-  gboolean prefer_dark_theme = FALSE;
-  g_object_get(gtk_settings_get_default(), "gtk-application-prefer-dark-theme",
-               &prefer_dark_theme, nullptr);
-  ui::NativeTheme* fallback_theme =
-      prefer_dark_theme ? ui::NativeThemeDarkAura::instance()
-                        : ui::NativeTheme::GetInstanceForNativeUi();
-  return fallback_theme->GetSystemColor(color_id);
+  if (color_cache_[color_id])
+    return color_cache_[color_id].value();
+
+  SkColor color = SkColorFromColorId(color_id);
+  color_cache_[color_id] = color;
+  return color;
 }
 
 void NativeThemeGtk3::PaintMenuPopupBackground(
     SkCanvas* canvas,
     const gfx::Size& size,
     const MenuBackgroundExtraParams& menu_background) const {
-  PaintWidget(canvas, gfx::Rect(size), "menu", GTK_STATE_FLAG_NORMAL);
+  PaintWidget(canvas, gfx::Rect(size), "GtkMenu.menu", GTK_STATE_FLAG_NORMAL);
 }
 
 void NativeThemeGtk3::PaintMenuItemBackground(
@@ -401,103 +346,8 @@
     State state,
     const gfx::Rect& rect,
     const MenuItemExtraParams& menu_item) const {
-  PaintWidget(canvas, rect, "menu menuitem", StateToStateFlags(state));
-}
-
-GtkWidget* NativeThemeGtk3::GetWindow() const {
-  static GtkWidget* fake_window = NULL;
-
-  if (!fake_window) {
-    fake_window = chrome_gtk_frame_new();
-    gtk_widget_realize(fake_window);
-  }
-
-  return fake_window;
-}
-
-GtkWidget* NativeThemeGtk3::GetEntry() const {
-  static GtkWidget* fake_entry = NULL;
-
-  if (!fake_entry) {
-    fake_entry = gtk_entry_new();
-
-    // The fake entry needs to be in the window so it can be realized so we can
-    // use the computed parts of the style.
-    gtk_container_add(GTK_CONTAINER(GetWindow()), fake_entry);
-    gtk_widget_realize(fake_entry);
-  }
-
-  return fake_entry;
-}
-
-GtkWidget* NativeThemeGtk3::GetLabel() const {
-  static GtkWidget* fake_label = NULL;
-
-  if (!fake_label)
-    fake_label = gtk_label_new("");
-
-  return fake_label;
-}
-
-GtkWidget* NativeThemeGtk3::GetButton() const {
-  static GtkWidget* fake_button = NULL;
-
-  if (!fake_button)
-    fake_button = gtk_button_new();
-
-  return fake_button;
-}
-
-GtkWidget* NativeThemeGtk3::GetBlueButton() const {
-  static GtkWidget* fake_bluebutton = NULL;
-
-  if (!fake_bluebutton) {
-    fake_bluebutton = gtk_button_new();
-    TurnButtonBlue(fake_bluebutton);
-  }
-
-  return fake_bluebutton;
-}
-
-GtkWidget* NativeThemeGtk3::GetTree() const {
-  static GtkWidget* fake_tree = NULL;
-
-  if (!fake_tree)
-    fake_tree = gtk_tree_view_new();
-
-  return fake_tree;
-}
-
-GtkWidget* NativeThemeGtk3::GetTooltip() const {
-  static GtkWidget* fake_tooltip = NULL;
-
-  if (!fake_tooltip) {
-    fake_tooltip = gtk_window_new(GTK_WINDOW_TOPLEVEL);
-    gtk_widget_set_name(fake_tooltip, "gtk-tooltip");
-    gtk_widget_realize(fake_tooltip);
-  }
-
-  return fake_tooltip;
-}
-
-GtkWidget* NativeThemeGtk3::GetMenu() const {
-  static GtkWidget* fake_menu = NULL;
-
-  if (!fake_menu)
-    fake_menu = gtk_custom_menu_new();
-
-  return fake_menu;
-}
-
-GtkWidget* NativeThemeGtk3::GetMenuItem() const {
-  static GtkWidget* fake_menu_item = NULL;
-
-  if (!fake_menu_item) {
-    fake_menu_item = gtk_custom_menu_item_new();
-    gtk_menu_shell_append(GTK_MENU_SHELL(GetMenu()), fake_menu_item);
-  }
-
-  return fake_menu_item;
+  PaintWidget(canvas, rect, "GtkMenu.menu GtkMenuItem.menuitem",
+              StateToStateFlags(state));
 }
 
 }  // namespace libgtkui
diff --git a/chrome/browser/ui/libgtkui/native_theme_gtk3.h b/chrome/browser/ui/libgtkui/native_theme_gtk3.h
index 72920a5..577375ce 100644
--- a/chrome/browser/ui/libgtkui/native_theme_gtk3.h
+++ b/chrome/browser/ui/libgtkui/native_theme_gtk3.h
@@ -6,10 +6,9 @@
 #define CHROME_BROWSER_UI_LIBGTKUI_NATIVE_THEME_GTK3_H_
 
 #include "base/macros.h"
+#include "base/optional.h"
 #include "ui/native_theme/native_theme_base.h"
 
-typedef struct _GtkWidget GtkWidget;
-
 namespace libgtkui {
 
 // A version of NativeTheme that uses GTK3-rendered widgets.
@@ -17,6 +16,9 @@
  public:
   static NativeThemeGtk3* instance();
 
+  // Called when gtk theme changes.
+  void ResetColorCache();
+
   // Overridden from ui::NativeThemeBase:
   SkColor GetSystemColor(ColorId color_id) const override;
   void PaintMenuPopupBackground(
@@ -33,19 +35,7 @@
   NativeThemeGtk3();
   ~NativeThemeGtk3() override;
 
-  SkColor LookupGtkThemeColor(ColorId color_id) const;
-
-  // Returns various widgets for theming use.
-  // TODO(thomasanderson): Remove all of these.
-  GtkWidget* GetWindow() const;
-  GtkWidget* GetEntry() const;
-  GtkWidget* GetLabel() const;
-  GtkWidget* GetButton() const;
-  GtkWidget* GetBlueButton() const;
-  GtkWidget* GetTree() const;
-  GtkWidget* GetTooltip() const;
-  GtkWidget* GetMenu() const;
-  GtkWidget* GetMenuItem() const;
+  mutable base::Optional<SkColor> color_cache_[kColorId_NumColors];
 
   DISALLOW_COPY_AND_ASSIGN(NativeThemeGtk3);
 };
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index f899ab13..ab0ced4f 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -667,6 +667,8 @@
         "//ash/resources",
         "//chrome/browser/media/router:test_support",
         "//chromeos",
+        "//mojo/edk/system",
+        "//ui/app_list/presenter:test_support",
       ]
 
       data += [
@@ -770,6 +772,7 @@
 
     if (use_ash) {
       sources += [
+        "../browser/ui/ash/app_list/app_list_interactive_uitest.cc",
         "../browser/ui/views/ash/tab_scrubber_browsertest.cc",
         "../browser/ui/window_sizer/window_sizer_ash_uitest.cc",
         "//ash/drag_drop/drag_drop_interactive_uitest.cc",
diff --git a/chromeos/components/tether/BUILD.gn b/chromeos/components/tether/BUILD.gn
index 6856664..b187e2b8 100644
--- a/chromeos/components/tether/BUILD.gn
+++ b/chromeos/components/tether/BUILD.gn
@@ -10,6 +10,8 @@
     "ble_advertisement_device_queue.h",
     "ble_constants.cc",
     "ble_constants.h",
+    "ble_scanner.cc",
+    "ble_scanner.h",
     "host_scan_scheduler.cc",
     "host_scan_scheduler.h",
     "host_scanner.cc",
@@ -25,6 +27,7 @@
     "//chromeos",
     "//components/cryptauth",
     "//components/proximity_auth/logging",
+    "//device/bluetooth",
   ]
 
   public_deps = [
@@ -35,7 +38,10 @@
 static_library("test_support") {
   testonly = true
 
-  sources = []
+  sources = [
+    "mock_local_device_data_provider.cc",
+    "mock_local_device_data_provider.h",
+  ]
 
   public_deps = [
     ":tether",
@@ -43,6 +49,7 @@
 
   deps = [
     "//base",
+    "//components/cryptauth",
     "//testing/gmock",
   ]
 }
@@ -52,6 +59,7 @@
 
   sources = [
     "ble_advertisement_device_queue_unittest.cc",
+    "ble_scanner_unittest.cc",
     "host_scan_scheduler_unittest.cc",
     "local_device_data_provider_unittest.cc",
   ]
@@ -63,6 +71,8 @@
     "//chromeos",
     "//components/cryptauth",
     "//components/cryptauth:test_support",
+    "//device/bluetooth",
+    "//device/bluetooth:mocks",
     "//testing/gmock",
     "//testing/gtest",
   ]
diff --git a/chromeos/components/tether/DEPS b/chromeos/components/tether/DEPS
index 093164a..14a7c43 100644
--- a/chromeos/components/tether/DEPS
+++ b/chromeos/components/tether/DEPS
@@ -1,4 +1,5 @@
 include_rules = [
   "+components/cryptauth",
   "+components/proximity_auth/logging",
+  "+device/bluetooth",
 ]
diff --git a/chromeos/components/tether/ble_constants.cc b/chromeos/components/tether/ble_constants.cc
index 48ca0c09..39633439 100644
--- a/chromeos/components/tether/ble_constants.cc
+++ b/chromeos/components/tether/ble_constants.cc
@@ -8,7 +8,7 @@
 
 namespace tether {
 
-const int kMaxConcurrentAdvertisements = 2;
+const uint8_t kMaxConcurrentAdvertisements = 2;
 const char kAdvertisingServiceUuid[] = "0000fe50-0000-1000-8000-00805f9b34fb";
 
 }  // namespace tether
diff --git a/chromeos/components/tether/ble_constants.h b/chromeos/components/tether/ble_constants.h
index 25f4b01c..fadb42b40 100644
--- a/chromeos/components/tether/ble_constants.h
+++ b/chromeos/components/tether/ble_constants.h
@@ -18,7 +18,7 @@
 // Note that this upper limit on concurrent advertisements is imposed due to a
 // hardware limit of advertisements (many devices have <10 total advertisement
 // slots).
-extern const int kMaxConcurrentAdvertisements;
+extern const uint8_t kMaxConcurrentAdvertisements;
 
 // The service UUID used for BLE advertisements.
 extern const char kAdvertisingServiceUuid[];
diff --git a/chromeos/components/tether/ble_scanner.cc b/chromeos/components/tether/ble_scanner.cc
new file mode 100644
index 0000000..fa86feb
--- /dev/null
+++ b/chromeos/components/tether/ble_scanner.cc
@@ -0,0 +1,312 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chromeos/components/tether/ble_scanner.h"
+
+#include "base/bind.h"
+#include "base/memory/ptr_util.h"
+#include "base/strings/string_util.h"
+#include "chromeos/components/tether/ble_constants.h"
+#include "components/cryptauth/proto/cryptauth_api.pb.h"
+#include "components/cryptauth/remote_device.h"
+#include "components/proximity_auth/logging/logging.h"
+#include "device/bluetooth/bluetooth_device.h"
+#include "device/bluetooth/bluetooth_discovery_session.h"
+#include "device/bluetooth/bluetooth_uuid.h"
+
+namespace chromeos {
+
+namespace tether {
+
+namespace {
+
+// Minimum RSSI value to use for discovery. The -90 value was determined
+// empirically and is borrowed from
+// |proximity_auth::BluetoothLowEnergyConnectionFinder|.
+const int kMinDiscoveryRSSI = -90;
+
+// Valid service data must include at least 4 bytes: 2 bytes associated with the
+// scanning device (used as a scan filter) and 2 bytes which identify the
+// advertising device to the scanning device.
+const size_t kMinNumBytesInServiceData = 4;
+
+// Returns out |string|s data as a hex string.
+std::string StringToHexOfContents(const std::string& string) {
+  std::stringstream ss;
+  ss << "0x" << std::hex;
+
+  for (size_t i = 0; i < string.size(); i++) {
+    ss << static_cast<int>(string.data()[i]);
+  }
+
+  return ss.str();
+}
+
+}  // namespace
+
+BleScanner::DelegateImpl::DelegateImpl() {}
+
+BleScanner::DelegateImpl::~DelegateImpl() {}
+
+bool BleScanner::DelegateImpl::IsBluetoothAdapterAvailable() const {
+  return device::BluetoothAdapterFactory::IsBluetoothAdapterAvailable();
+}
+
+void BleScanner::DelegateImpl::GetAdapter(
+    const device::BluetoothAdapterFactory::AdapterCallback& callback) {
+  device::BluetoothAdapterFactory::GetAdapter(callback);
+}
+
+const std::vector<uint8_t>* BleScanner::DelegateImpl::GetServiceDataForUUID(
+    const device::BluetoothUUID& service_uuid,
+    device::BluetoothDevice* bluetooth_device) {
+  return bluetooth_device->GetServiceDataForUUID(service_uuid);
+}
+
+BleScanner::BleScanner(
+    const LocalDeviceDataProvider* local_device_data_provider)
+    : BleScanner(base::MakeUnique<DelegateImpl>(),
+                 cryptauth::EidGenerator::GetInstance(),
+                 local_device_data_provider) {}
+
+BleScanner::~BleScanner() {}
+
+BleScanner::BleScanner(
+    std::unique_ptr<Delegate> delegate,
+    const cryptauth::EidGenerator* eid_generator,
+    const LocalDeviceDataProvider* local_device_data_provider)
+    : delegate_(std::move(delegate)),
+      eid_generator_(eid_generator),
+      local_device_data_provider_(local_device_data_provider),
+      is_initializing_adapter_(false),
+      is_initializing_discovery_session_(false),
+      discovery_session_(nullptr),
+      weak_ptr_factory_(this) {}
+
+bool BleScanner::RegisterScanFilterForDevice(
+    const cryptauth::RemoteDevice& remote_device) {
+  if (!delegate_->IsBluetoothAdapterAvailable()) {
+    PA_LOG(ERROR) << "Bluetooth is not supported on this platform.";
+    return false;
+  }
+
+  if (registered_remote_devices_.size() >= kMaxConcurrentAdvertisements) {
+    // Each scan filter corresponds to an advertisement. Thus, the number of
+    // concurrent advertisements cannot exceed the maximum number of concurrent
+    // advertisements.
+    return false;
+  }
+
+  std::vector<cryptauth::BeaconSeed> local_device_beacon_seeds;
+  if (!local_device_data_provider_->GetLocalDeviceData(
+          nullptr, &local_device_beacon_seeds)) {
+    // If the local device's beacon seeds could not be fetched, a scan filter
+    // cannot be generated.
+    return false;
+  }
+
+  std::unique_ptr<cryptauth::EidGenerator::EidData> scan_filters =
+      eid_generator_->GenerateBackgroundScanFilter(local_device_beacon_seeds);
+  if (!scan_filters) {
+    // If a background scan filter cannot be generated, give up.
+    return false;
+  }
+
+  registered_remote_devices_.push_back(remote_device);
+  UpdateDiscoveryStatus();
+
+  return true;
+}
+
+bool BleScanner::UnregisterScanFilterForDevice(
+    const cryptauth::RemoteDevice& remote_device) {
+  for (auto it = registered_remote_devices_.begin();
+       it != registered_remote_devices_.end(); ++it) {
+    if (it->GetDeviceId() == remote_device.GetDeviceId()) {
+      registered_remote_devices_.erase(it);
+      UpdateDiscoveryStatus();
+      return true;
+    }
+  }
+
+  return false;
+}
+
+bool BleScanner::IsDeviceRegistered(const std::string& device_id) {
+  for (auto it = registered_remote_devices_.begin();
+       it != registered_remote_devices_.end(); ++it) {
+    if (it->GetDeviceId() == device_id) {
+      return true;
+    }
+  }
+
+  return false;
+}
+
+void BleScanner::AddObserver(Observer* observer) {
+  observer_list_.AddObserver(observer);
+}
+
+void BleScanner::RemoveObserver(Observer* observer) {
+  observer_list_.RemoveObserver(observer);
+}
+
+void BleScanner::AdapterPoweredChanged(device::BluetoothAdapter* adapter,
+                                       bool powered) {
+  DCHECK_EQ(adapter_.get(), adapter);
+  PA_LOG(INFO) << "Adapter power changed. Powered = " << powered;
+  UpdateDiscoveryStatus();
+}
+
+void BleScanner::DeviceAdded(device::BluetoothAdapter* adapter,
+                             device::BluetoothDevice* bluetooth_device) {
+  DCHECK_EQ(adapter_.get(), adapter);
+  HandleDeviceUpdated(bluetooth_device);
+}
+
+void BleScanner::DeviceChanged(device::BluetoothAdapter* adapter,
+                               device::BluetoothDevice* bluetooth_device) {
+  DCHECK_EQ(adapter_.get(), adapter);
+  HandleDeviceUpdated(bluetooth_device);
+}
+
+void BleScanner::UpdateDiscoveryStatus() {
+  if (registered_remote_devices_.empty()) {
+    StopDiscoverySession();
+    return;
+  }
+
+  if (is_initializing_adapter_) {
+    return;
+  } else if (!adapter_) {
+    InitializeBluetoothAdapter();
+    return;
+  }
+
+  if (!adapter_->IsPowered()) {
+    // If the adapter has powered off, no devices can be discovered.
+    StopDiscoverySession();
+    return;
+  }
+
+  if (is_initializing_discovery_session_) {
+    return;
+  } else if (!discovery_session_ ||
+             (discovery_session_ && !discovery_session_->IsActive())) {
+    StartDiscoverySession();
+  }
+}
+
+void BleScanner::InitializeBluetoothAdapter() {
+  PA_LOG(INFO) << "Initializing Bluetooth adapter.";
+  is_initializing_adapter_ = true;
+  delegate_->GetAdapter(base::Bind(&BleScanner::OnAdapterInitialized,
+                                   weak_ptr_factory_.GetWeakPtr()));
+}
+
+void BleScanner::OnAdapterInitialized(
+    scoped_refptr<device::BluetoothAdapter> adapter) {
+  DCHECK(is_initializing_adapter_ && !discovery_session_ &&
+         !is_initializing_discovery_session_);
+  PA_LOG(INFO) << "Bluetooth adapter initialized.";
+  is_initializing_adapter_ = false;
+
+  adapter_ = adapter;
+  adapter_->AddObserver(this);
+
+  UpdateDiscoveryStatus();
+}
+
+void BleScanner::StartDiscoverySession() {
+  DCHECK(adapter_);
+  PA_LOG(INFO) << "Starting discovery session.";
+  is_initializing_discovery_session_ = true;
+
+  // Discover only low energy (LE) devices with strong enough signal.
+  std::unique_ptr<device::BluetoothDiscoveryFilter> filter =
+      base::MakeUnique<device::BluetoothDiscoveryFilter>(
+          device::BLUETOOTH_TRANSPORT_LE);
+  filter->SetRSSI(kMinDiscoveryRSSI);
+
+  adapter_->StartDiscoverySessionWithFilter(
+      std::move(filter), base::Bind(&BleScanner::OnDiscoverySessionStarted,
+                                    weak_ptr_factory_.GetWeakPtr()),
+      base::Bind(&BleScanner::OnStartDiscoverySessionError,
+                 weak_ptr_factory_.GetWeakPtr()));
+}
+
+void BleScanner::OnDiscoverySessionStarted(
+    std::unique_ptr<device::BluetoothDiscoverySession> discovery_session) {
+  PA_LOG(INFO) << "Discovery session started. Scanning fully initialized.";
+  is_initializing_discovery_session_ = false;
+  discovery_session_ = std::move(discovery_session);
+}
+
+void BleScanner::OnStartDiscoverySessionError() {
+  PA_LOG(WARNING) << "Error starting discovery session. Initialization failed.";
+  is_initializing_discovery_session_ = false;
+}
+
+void BleScanner::StopDiscoverySession() {
+  if (!discovery_session_) {
+    // If there is no discovery session to stop, return early.
+    return;
+  }
+
+  PA_LOG(WARNING) << "Stopping discovery session.";
+  discovery_session_.reset();
+}
+
+void BleScanner::HandleDeviceUpdated(
+    device::BluetoothDevice* bluetooth_device) {
+  DCHECK(bluetooth_device);
+
+  const std::vector<uint8_t>* service_data = delegate_->GetServiceDataForUUID(
+      device::BluetoothUUID(kAdvertisingServiceUuid), bluetooth_device);
+  if (!service_data || service_data->size() < kMinNumBytesInServiceData) {
+    // If there is no service data or the service data is of insufficient
+    // length, there is not enough information to create a connection.
+    return;
+  }
+
+  // Convert the service data from a std::vector<uint8_t> to a std::string.
+  std::string service_data_str;
+  char* string_contents_ptr =
+      base::WriteInto(&service_data_str, service_data->size() + 1);
+  memcpy(string_contents_ptr, service_data->data(), service_data->size() + 1);
+
+  CheckForMatchingScanFilters(bluetooth_device, service_data_str);
+}
+
+void BleScanner::CheckForMatchingScanFilters(
+    device::BluetoothDevice* bluetooth_device,
+    std::string& service_data) {
+  std::vector<cryptauth::BeaconSeed> beacon_seeds;
+  if (!local_device_data_provider_->GetLocalDeviceData(nullptr,
+                                                       &beacon_seeds)) {
+    // If no beacon seeds are available, the scan cannot be checked for a match.
+    return;
+  }
+
+  const cryptauth::RemoteDevice* identified_device =
+      eid_generator_->IdentifyRemoteDeviceByAdvertisement(
+          service_data, registered_remote_devices_, beacon_seeds);
+
+  if (identified_device) {
+    PA_LOG(INFO) << "Received advertisement from remote device with ID "
+                 << identified_device->GetTruncatedDeviceIdForLogs() << ".";
+    for (auto& observer : observer_list_) {
+      observer.OnReceivedAdvertisementFromDevice(bluetooth_device,
+                                                 *identified_device);
+    }
+  } else {
+    PA_LOG(INFO) << "Received advertisement remote device, but could not "
+                 << "identify the device. Service data: "
+                 << StringToHexOfContents(service_data) << ".";
+  }
+}
+
+}  // namespace tether
+
+}  // namespace chromeos
diff --git a/chromeos/components/tether/ble_scanner.h b/chromeos/components/tether/ble_scanner.h
new file mode 100644
index 0000000..9122c7f5
--- /dev/null
+++ b/chromeos/components/tether/ble_scanner.h
@@ -0,0 +1,125 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROMEOS_COMPONENTS_BLE_SCANNER_H_
+#define CHROMEOS_COMPONENTS_BLE_SCANNER_H_
+
+#include <map>
+
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "base/observer_list.h"
+#include "chromeos/components/tether/local_device_data_provider.h"
+#include "components/cryptauth/eid_generator.h"
+#include "device/bluetooth/bluetooth_adapter.h"
+#include "device/bluetooth/bluetooth_adapter_factory.h"
+
+namespace device {
+class BluetoothDevice;
+class BluetoothDiscoverySession;
+}
+
+namespace chromeos {
+
+namespace tether {
+
+class BleScanner : public device::BluetoothAdapter::Observer {
+ public:
+  class Observer {
+   public:
+    virtual void OnReceivedAdvertisementFromDevice(
+        const device::BluetoothDevice* bluetooth_device,
+        cryptauth::RemoteDevice remote_device) = 0;
+  };
+
+  BleScanner(const LocalDeviceDataProvider* local_device_data_provider);
+  ~BleScanner() override;
+
+  bool RegisterScanFilterForDevice(
+      const cryptauth::RemoteDevice& remote_device);
+  bool UnregisterScanFilterForDevice(
+      const cryptauth::RemoteDevice& remote_device);
+
+  bool IsDeviceRegistered(const std::string& device_id);
+
+  void AddObserver(Observer* observer);
+  void RemoveObserver(Observer* observer);
+
+  // device::BluetoothAdapter::Observer
+  void AdapterPoweredChanged(device::BluetoothAdapter* adapter,
+                             bool powered) override;
+  void DeviceAdded(device::BluetoothAdapter* adapter,
+                   device::BluetoothDevice* bluetooth_device) override;
+  void DeviceChanged(device::BluetoothAdapter* adapter,
+                     device::BluetoothDevice* bluetooth_device) override;
+
+ private:
+  friend class BleScannerTest;
+
+  class Delegate {
+   public:
+    virtual ~Delegate() {}
+    virtual bool IsBluetoothAdapterAvailable() const = 0;
+    virtual void GetAdapter(
+        const device::BluetoothAdapterFactory::AdapterCallback& callback) = 0;
+    virtual const std::vector<uint8_t>* GetServiceDataForUUID(
+        const device::BluetoothUUID& service_uuid,
+        device::BluetoothDevice* bluetooth_device) = 0;
+  };
+
+  class DelegateImpl : public Delegate {
+   public:
+    DelegateImpl();
+    ~DelegateImpl() override;
+    bool IsBluetoothAdapterAvailable() const override;
+    void GetAdapter(const device::BluetoothAdapterFactory::AdapterCallback&
+                        callback) override;
+    const std::vector<uint8_t>* GetServiceDataForUUID(
+        const device::BluetoothUUID& service_uuid,
+        device::BluetoothDevice* bluetooth_device) override;
+  };
+
+  BleScanner(std::unique_ptr<Delegate> delegate,
+             const cryptauth::EidGenerator* eid_generator,
+             const LocalDeviceDataProvider* local_device_data_provider);
+
+  void UpdateDiscoveryStatus();
+  void InitializeBluetoothAdapter();
+  void OnAdapterInitialized(scoped_refptr<device::BluetoothAdapter> adapter);
+  void StartDiscoverySession();
+  void OnDiscoverySessionStarted(
+      std::unique_ptr<device::BluetoothDiscoverySession> discovery_session);
+  void OnStartDiscoverySessionError();
+  void StopDiscoverySession();
+  void HandleDeviceUpdated(device::BluetoothDevice* bluetooth_device);
+  void CheckForMatchingScanFilters(device::BluetoothDevice* bluetooth_device,
+                                   std::string& service_data);
+
+  std::unique_ptr<Delegate> delegate_;
+
+  // |eid_generator_| and |local_device_data_provider_| are not owned by this
+  // instance and must outlive it.
+  const cryptauth::EidGenerator* eid_generator_;
+  const LocalDeviceDataProvider* local_device_data_provider_;
+
+  bool is_initializing_adapter_;
+  scoped_refptr<device::BluetoothAdapter> adapter_;
+
+  bool is_initializing_discovery_session_;
+  std::unique_ptr<device::BluetoothDiscoverySession> discovery_session_;
+
+  std::vector<cryptauth::RemoteDevice> registered_remote_devices_;
+
+  base::ObserverList<Observer> observer_list_;
+
+  base::WeakPtrFactory<BleScanner> weak_ptr_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(BleScanner);
+};
+
+}  // namespace tether
+
+}  // namespace chromeos
+
+#endif  // CHROMEOS_COMPONENTS_BLE_SCANNER_H_
diff --git a/chromeos/components/tether/ble_scanner_unittest.cc b/chromeos/components/tether/ble_scanner_unittest.cc
new file mode 100644
index 0000000..b2b9bbca
--- /dev/null
+++ b/chromeos/components/tether/ble_scanner_unittest.cc
@@ -0,0 +1,620 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chromeos/components/tether/ble_scanner.h"
+
+#include "base/logging.h"
+#include "chromeos/components/tether/ble_constants.h"
+#include "chromeos/components/tether/mock_local_device_data_provider.h"
+#include "components/cryptauth/mock_eid_generator.h"
+#include "components/cryptauth/proto/cryptauth_api.pb.h"
+#include "components/cryptauth/remote_device_test_util.h"
+#include "device/bluetooth/test/mock_bluetooth_adapter.h"
+#include "device/bluetooth/test/mock_bluetooth_device.h"
+#include "device/bluetooth/test/mock_bluetooth_discovery_session.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using testing::_;
+using testing::DoAll;
+using testing::Eq;
+using testing::Invoke;
+using testing::NiceMock;
+using testing::SaveArg;
+using testing::Return;
+
+namespace chromeos {
+
+namespace tether {
+
+namespace {
+class MockBleScannerObserver : public BleScanner::Observer {
+ public:
+  MockBleScannerObserver() {}
+
+  void OnReceivedAdvertisementFromDevice(
+      const device::BluetoothDevice* bluetooth_device,
+      cryptauth::RemoteDevice remote_device) override {
+    bluetooth_devices_.push_back(bluetooth_device);
+    remote_devices_.push_back(remote_device);
+  }
+
+  int GetNumCalls() { return static_cast<int>(bluetooth_devices_.size()); }
+
+  std::vector<const device::BluetoothDevice*>& bluetooth_devices() {
+    return bluetooth_devices_;
+  }
+
+  std::vector<cryptauth::RemoteDevice>& remote_devices() {
+    return remote_devices_;
+  }
+
+ private:
+  std::vector<const device::BluetoothDevice*> bluetooth_devices_;
+  std::vector<cryptauth::RemoteDevice> remote_devices_;
+};
+
+class MockBluetoothDeviceWithServiceData : public device::MockBluetoothDevice {
+ public:
+  MockBluetoothDeviceWithServiceData(device::MockBluetoothAdapter* adapter,
+                                     const std::string& service_data)
+      : device::MockBluetoothDevice(adapter,
+                                    /* bluetooth_class */ 0,
+                                    "name",
+                                    "11:22:33:44:55:66",
+                                    false,
+                                    false) {
+    for (size_t i = 0; i < service_data.size(); i++) {
+      service_data_.push_back(static_cast<uint8_t>(service_data[i]));
+    }
+  }
+
+  const std::vector<uint8_t>* service_data() { return &service_data_; }
+
+ private:
+  std::vector<uint8_t> service_data_;
+};
+
+const int kExpectedDiscoveryRSSI = -90;
+const size_t kMinNumBytesInServiceData = 4;
+
+const std::string fake_local_public_key = "fakeLocalPublicKey";
+
+const std::string current_eid_data = "currentEidData";
+const int64_t current_eid_start_ms = 1000L;
+const int64_t current_eid_end_ms = 2000L;
+
+const std::string adjacent_eid_data = "adjacentEidData";
+const int64_t adjacent_eid_start_ms = 2000L;
+const int64_t adjacent_eid_end_ms = 3000L;
+
+const std::string fake_beacon_seed1_data = "fakeBeaconSeed1Data";
+const int64_t fake_beacon_seed1_start_ms = current_eid_start_ms;
+const int64_t fake_beacon_seed1_end_ms = current_eid_end_ms;
+
+const std::string fake_beacon_seed2_data = "fakeBeaconSeed2Data";
+const int64_t fake_beacon_seed2_start_ms = adjacent_eid_start_ms;
+const int64_t fake_beacon_seed2_end_ms = adjacent_eid_end_ms;
+
+std::unique_ptr<cryptauth::EidGenerator::EidData>
+CreateFakeBackgroundScanFilter() {
+  cryptauth::EidGenerator::DataWithTimestamp current(
+      current_eid_data, current_eid_start_ms, current_eid_end_ms);
+
+  std::unique_ptr<cryptauth::EidGenerator::DataWithTimestamp> adjacent =
+      base::MakeUnique<cryptauth::EidGenerator::DataWithTimestamp>(
+          adjacent_eid_data, adjacent_eid_start_ms, adjacent_eid_end_ms);
+
+  return base::MakeUnique<cryptauth::EidGenerator::EidData>(
+      current, std::move(adjacent));
+}
+
+std::vector<cryptauth::BeaconSeed> CreateFakeBeaconSeeds() {
+  cryptauth::BeaconSeed seed1;
+  seed1.set_data(fake_beacon_seed1_data);
+  seed1.set_start_time_millis(fake_beacon_seed1_start_ms);
+  seed1.set_start_time_millis(fake_beacon_seed1_end_ms);
+
+  cryptauth::BeaconSeed seed2;
+  seed2.set_data(fake_beacon_seed2_data);
+  seed2.set_start_time_millis(fake_beacon_seed2_start_ms);
+  seed2.set_start_time_millis(fake_beacon_seed2_end_ms);
+
+  std::vector<cryptauth::BeaconSeed> seeds = {seed1, seed2};
+  return seeds;
+}
+}  // namespace
+
+class BleScannerTest : public testing::Test {
+ protected:
+  class TestDelegate : public BleScanner::Delegate {
+   public:
+    TestDelegate()
+        : is_bluetooth_adapter_available_(true),
+          last_get_adapter_callback_(nullptr) {}
+
+    ~TestDelegate() override {}
+
+    bool IsBluetoothAdapterAvailable() const override {
+      return is_bluetooth_adapter_available_;
+    }
+
+    void set_is_bluetooth_adapter_available(
+        bool is_bluetooth_adapter_available) {
+      is_bluetooth_adapter_available_ = is_bluetooth_adapter_available;
+    }
+
+    void GetAdapter(const device::BluetoothAdapterFactory::AdapterCallback&
+                        callback) override {
+      last_get_adapter_callback_ = callback;
+    }
+
+    const device::BluetoothAdapterFactory::AdapterCallback
+    last_get_adapter_callback() {
+      return last_get_adapter_callback_;
+    }
+
+    const std::vector<uint8_t>* GetServiceDataForUUID(
+        const device::BluetoothUUID& service_uuid,
+        device::BluetoothDevice* bluetooth_device) override {
+      if (device::BluetoothUUID(kAdvertisingServiceUuid) == service_uuid) {
+        return reinterpret_cast<MockBluetoothDeviceWithServiceData*>(
+                   bluetooth_device)
+            ->service_data();
+      }
+
+      return nullptr;
+    }
+
+   private:
+    bool is_bluetooth_adapter_available_;
+    device::BluetoothAdapterFactory::AdapterCallback last_get_adapter_callback_;
+  };
+
+  BleScannerTest()
+      : test_devices_(cryptauth::GenerateTestRemoteDevices(3)),
+        test_beacon_seeds_(CreateFakeBeaconSeeds()) {}
+
+  void SetUp() override {
+    test_delegate_ = new TestDelegate();
+    EXPECT_TRUE(test_delegate_->IsBluetoothAdapterAvailable());
+    EXPECT_FALSE(test_delegate_->last_get_adapter_callback());
+
+    mock_eid_generator_ = base::MakeUnique<cryptauth::MockEidGenerator>();
+    mock_eid_generator_->set_background_scan_filter(
+        CreateFakeBackgroundScanFilter());
+
+    mock_local_device_data_provider_ =
+        base::MakeUnique<MockLocalDeviceDataProvider>();
+    mock_local_device_data_provider_->SetPublicKey(
+        base::MakeUnique<std::string>(fake_local_public_key));
+    mock_local_device_data_provider_->SetBeaconSeeds(
+        base::MakeUnique<std::vector<cryptauth::BeaconSeed>>(
+            test_beacon_seeds_));
+
+    mock_adapter_ =
+        make_scoped_refptr(new NiceMock<device::MockBluetoothAdapter>());
+    stored_discovery_filter_.reset();
+    stored_discovery_callback_.Reset();
+    stored_discovery_errback_.Reset();
+    ON_CALL(*mock_adapter_, StartDiscoverySessionWithFilterRaw(_, _, _))
+        .WillByDefault(Invoke(
+            this, &BleScannerTest::SaveStartDiscoverySessionWithFilterArgs));
+    ON_CALL(*mock_adapter_, IsPowered()).WillByDefault(Return(true));
+
+    mock_discovery_session_ = nullptr;
+
+    ble_scanner_ = base::WrapUnique(new BleScanner(
+        base::WrapUnique(test_delegate_), mock_eid_generator_.get(),
+        mock_local_device_data_provider_.get()));
+
+    mock_observer_ = base::MakeUnique<MockBleScannerObserver>();
+    ble_scanner_->AddObserver(mock_observer_.get());
+  }
+
+  void SaveStartDiscoverySessionWithFilterArgs(
+      const device::BluetoothDiscoveryFilter* discovery_filter,
+      const device::BluetoothAdapter::DiscoverySessionCallback& callback,
+      const device::BluetoothAdapter::ErrorCallback& errback) {
+    stored_discovery_filter_ =
+        base::MakeUnique<device::BluetoothDiscoveryFilter>(
+            device::BluetoothTransport::BLUETOOTH_TRANSPORT_LE);
+    stored_discovery_filter_->CopyFrom(*discovery_filter);
+    stored_discovery_callback_ = callback;
+    stored_discovery_errback_ = errback;
+  }
+
+  void InvokeAdapterCallback() {
+    const device::BluetoothAdapterFactory::AdapterCallback
+        last_get_adapter_callback = test_delegate_->last_get_adapter_callback();
+    ASSERT_TRUE(last_get_adapter_callback);
+
+    // Because the adapter has just been initialized, the discovery session
+    // should not have been started yet.
+    EXPECT_FALSE(stored_discovery_filter_);
+    EXPECT_TRUE(stored_discovery_callback_.is_null());
+    EXPECT_TRUE(stored_discovery_errback_.is_null());
+
+    EXPECT_CALL(*mock_adapter_, AddObserver(ble_scanner_.get()));
+    last_get_adapter_callback.Run(mock_adapter_);
+
+    // Once the adapter callback is returned, a discovery session should be
+    // started via that adapter.
+    AssertDiscoverySessionRequested();
+  }
+
+  void AssertDiscoverySessionRequested() {
+    // First, ensure that the correct discovery filter was passed.
+    EXPECT_TRUE(stored_discovery_filter_);
+    EXPECT_EQ(device::BluetoothTransport::BLUETOOTH_TRANSPORT_LE,
+              stored_discovery_filter_->GetTransport());
+    int16_t observed_rssi;
+    ASSERT_TRUE(stored_discovery_filter_->GetRSSI(&observed_rssi));
+    EXPECT_EQ(kExpectedDiscoveryRSSI, observed_rssi);
+
+    // Now, ensure that both a callback and errback were passed.
+    EXPECT_FALSE(stored_discovery_callback_.is_null());
+    EXPECT_FALSE(stored_discovery_errback_.is_null());
+  }
+
+  void InvokeDiscoveryStartedCallback() {
+    EXPECT_FALSE(stored_discovery_callback_.is_null());
+
+    mock_discovery_session_ = new device::MockBluetoothDiscoverySession();
+    stored_discovery_callback_.Run(base::WrapUnique(mock_discovery_session_));
+  }
+
+  std::vector<cryptauth::RemoteDevice> test_devices_;
+  std::vector<cryptauth::BeaconSeed> test_beacon_seeds_;
+
+  std::unique_ptr<MockBleScannerObserver> mock_observer_;
+
+  TestDelegate* test_delegate_;
+  std::unique_ptr<cryptauth::MockEidGenerator> mock_eid_generator_;
+  std::unique_ptr<MockLocalDeviceDataProvider> mock_local_device_data_provider_;
+
+  scoped_refptr<NiceMock<device::MockBluetoothAdapter>> mock_adapter_;
+  device::MockBluetoothDiscoverySession* mock_discovery_session_;
+
+  std::unique_ptr<device::BluetoothDiscoveryFilter> stored_discovery_filter_;
+  device::BluetoothAdapter::DiscoverySessionCallback stored_discovery_callback_;
+  device::BluetoothAdapter::ErrorCallback stored_discovery_errback_;
+
+  std::unique_ptr<BleScanner> ble_scanner_;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(BleScannerTest);
+};
+
+TEST_F(BleScannerTest, TestNoBluetoothAdapter) {
+  test_delegate_->set_is_bluetooth_adapter_available(false);
+  EXPECT_FALSE(ble_scanner_->RegisterScanFilterForDevice(test_devices_[0]));
+  EXPECT_FALSE(
+      ble_scanner_->IsDeviceRegistered(test_devices_[0].GetDeviceId()));
+  EXPECT_FALSE(test_delegate_->last_get_adapter_callback());
+  EXPECT_FALSE(mock_observer_->GetNumCalls());
+}
+
+TEST_F(BleScannerTest, TestNoLocalBeaconSeeds) {
+  mock_local_device_data_provider_->SetBeaconSeeds(nullptr);
+  EXPECT_FALSE(ble_scanner_->RegisterScanFilterForDevice(test_devices_[0]));
+  EXPECT_FALSE(
+      ble_scanner_->IsDeviceRegistered(test_devices_[0].GetDeviceId()));
+  EXPECT_FALSE(test_delegate_->last_get_adapter_callback());
+  EXPECT_FALSE(mock_observer_->GetNumCalls());
+}
+
+TEST_F(BleScannerTest, TestNoBackgroundScanFilter) {
+  mock_eid_generator_->set_background_scan_filter(nullptr);
+  EXPECT_FALSE(ble_scanner_->RegisterScanFilterForDevice(test_devices_[0]));
+  EXPECT_FALSE(
+      ble_scanner_->IsDeviceRegistered(test_devices_[0].GetDeviceId()));
+  EXPECT_FALSE(test_delegate_->last_get_adapter_callback());
+  EXPECT_FALSE(mock_observer_->GetNumCalls());
+}
+
+TEST_F(BleScannerTest, TestAdapterDoesNotInitialize) {
+  EXPECT_TRUE(ble_scanner_->RegisterScanFilterForDevice(test_devices_[0]));
+  EXPECT_TRUE(ble_scanner_->IsDeviceRegistered(test_devices_[0].GetDeviceId()));
+  EXPECT_TRUE(test_delegate_->last_get_adapter_callback());
+
+  // Do not call the last GetAdapter() callback. The device should still be able
+  // to be unregistered.
+  EXPECT_TRUE(ble_scanner_->UnregisterScanFilterForDevice(test_devices_[0]));
+  EXPECT_FALSE(
+      ble_scanner_->IsDeviceRegistered(test_devices_[0].GetDeviceId()));
+  EXPECT_FALSE(mock_observer_->GetNumCalls());
+}
+
+TEST_F(BleScannerTest, TestAdapterDoesNotInitialize_MultipleDevicesRegistered) {
+  EXPECT_TRUE(ble_scanner_->RegisterScanFilterForDevice(test_devices_[0]));
+  EXPECT_TRUE(ble_scanner_->RegisterScanFilterForDevice(test_devices_[1]));
+  EXPECT_TRUE(ble_scanner_->IsDeviceRegistered(test_devices_[0].GetDeviceId()));
+  EXPECT_TRUE(ble_scanner_->IsDeviceRegistered(test_devices_[1].GetDeviceId()));
+  EXPECT_TRUE(test_delegate_->last_get_adapter_callback());
+
+  // Do not call the last GetAdapter() callback. The devices should still be
+  // able to be unregistered.
+  EXPECT_TRUE(ble_scanner_->UnregisterScanFilterForDevice(test_devices_[0]));
+  EXPECT_TRUE(ble_scanner_->UnregisterScanFilterForDevice(test_devices_[1]));
+  EXPECT_FALSE(
+      ble_scanner_->IsDeviceRegistered(test_devices_[0].GetDeviceId()));
+  EXPECT_FALSE(
+      ble_scanner_->IsDeviceRegistered(test_devices_[1].GetDeviceId()));
+  EXPECT_FALSE(mock_observer_->GetNumCalls());
+}
+
+TEST_F(BleScannerTest, TestDiscoverySessionFailsToStart) {
+  EXPECT_TRUE(ble_scanner_->RegisterScanFilterForDevice(test_devices_[0]));
+  EXPECT_TRUE(ble_scanner_->IsDeviceRegistered(test_devices_[0].GetDeviceId()));
+
+  InvokeAdapterCallback();
+  stored_discovery_errback_.Run();
+
+  EXPECT_TRUE(ble_scanner_->IsDeviceRegistered(test_devices_[0].GetDeviceId()));
+  EXPECT_TRUE(ble_scanner_->UnregisterScanFilterForDevice(test_devices_[0]));
+  EXPECT_FALSE(mock_observer_->GetNumCalls());
+}
+
+TEST_F(BleScannerTest, TestDiscoveryStartsButNoDevicesFound) {
+  EXPECT_TRUE(ble_scanner_->RegisterScanFilterForDevice(test_devices_[0]));
+  EXPECT_TRUE(ble_scanner_->IsDeviceRegistered(test_devices_[0].GetDeviceId()));
+
+  InvokeAdapterCallback();
+  InvokeDiscoveryStartedCallback();
+
+  // No devices found.
+
+  EXPECT_TRUE(ble_scanner_->IsDeviceRegistered(test_devices_[0].GetDeviceId()));
+  EXPECT_TRUE(ble_scanner_->UnregisterScanFilterForDevice(test_devices_[0]));
+  EXPECT_FALSE(mock_observer_->GetNumCalls());
+}
+
+TEST_F(BleScannerTest, TestDiscovery_NoServiceData) {
+  std::string empty_service_data = "";
+
+  EXPECT_TRUE(ble_scanner_->RegisterScanFilterForDevice(test_devices_[0]));
+  EXPECT_TRUE(ble_scanner_->IsDeviceRegistered(test_devices_[0].GetDeviceId()));
+
+  InvokeAdapterCallback();
+  InvokeDiscoveryStartedCallback();
+
+  // Device with no service data connected. Service data is required to identify
+  // the advertising device.
+  MockBluetoothDeviceWithServiceData device1(mock_adapter_.get(),
+                                             empty_service_data);
+  ble_scanner_->DeviceAdded(mock_adapter_.get(), &device1);
+  EXPECT_FALSE(mock_eid_generator_->num_identify_calls());
+  EXPECT_FALSE(mock_observer_->GetNumCalls());
+}
+
+TEST_F(BleScannerTest, TestDiscovery_ServiceDataTooShort) {
+  std::string short_service_data = "abc";
+  ASSERT_TRUE(short_service_data.size() < kMinNumBytesInServiceData);
+
+  EXPECT_TRUE(ble_scanner_->RegisterScanFilterForDevice(test_devices_[0]));
+  EXPECT_TRUE(ble_scanner_->IsDeviceRegistered(test_devices_[0].GetDeviceId()));
+
+  InvokeAdapterCallback();
+  InvokeDiscoveryStartedCallback();
+
+  // Device with short service data connected. Service data of at least 4 bytes
+  // is required to identify the advertising device.
+  MockBluetoothDeviceWithServiceData device2(mock_adapter_.get(),
+                                             short_service_data);
+  ble_scanner_->DeviceAdded(mock_adapter_.get(), &device2);
+  EXPECT_FALSE(mock_eid_generator_->num_identify_calls());
+  EXPECT_FALSE(mock_observer_->GetNumCalls());
+}
+
+TEST_F(BleScannerTest, TestDiscovery_LocalDeviceDataCannotBeFetched) {
+  std::string valid_service_data_for_other_device = "abcd";
+  ASSERT_TRUE(valid_service_data_for_other_device.size() >=
+              kMinNumBytesInServiceData);
+
+  EXPECT_TRUE(ble_scanner_->RegisterScanFilterForDevice(test_devices_[0]));
+  EXPECT_TRUE(ble_scanner_->IsDeviceRegistered(test_devices_[0].GetDeviceId()));
+
+  InvokeAdapterCallback();
+  InvokeDiscoveryStartedCallback();
+
+  // Device with valid service data connected, but the local device data
+  // cannot be fetched.
+  mock_local_device_data_provider_->SetPublicKey(nullptr);
+  mock_local_device_data_provider_->SetBeaconSeeds(nullptr);
+  MockBluetoothDeviceWithServiceData device3(
+      mock_adapter_.get(), valid_service_data_for_other_device);
+  ble_scanner_->DeviceAdded(mock_adapter_.get(), &device3);
+  EXPECT_FALSE(mock_eid_generator_->num_identify_calls());
+  EXPECT_FALSE(mock_observer_->GetNumCalls());
+}
+
+TEST_F(BleScannerTest, TestDiscovery_ScanSuccessfulButNoRegisteredDevice) {
+  std::string valid_service_data_for_other_device = "abcd";
+  ASSERT_TRUE(valid_service_data_for_other_device.size() >=
+              kMinNumBytesInServiceData);
+
+  EXPECT_TRUE(ble_scanner_->RegisterScanFilterForDevice(test_devices_[0]));
+  EXPECT_TRUE(ble_scanner_->IsDeviceRegistered(test_devices_[0].GetDeviceId()));
+
+  InvokeAdapterCallback();
+  InvokeDiscoveryStartedCallback();
+
+  // Device with valid service data connected, but there was no registered
+  // device corresponding to the one that just connected.
+  mock_local_device_data_provider_->SetPublicKey(
+      base::MakeUnique<std::string>(fake_local_public_key));
+  mock_local_device_data_provider_->SetBeaconSeeds(
+      base::MakeUnique<std::vector<cryptauth::BeaconSeed>>(test_beacon_seeds_));
+  MockBluetoothDeviceWithServiceData device4(
+      mock_adapter_.get(), valid_service_data_for_other_device);
+  ble_scanner_->DeviceAdded(mock_adapter_.get(), &device4);
+  EXPECT_EQ(1, mock_eid_generator_->num_identify_calls());
+  EXPECT_FALSE(mock_observer_->GetNumCalls());
+}
+
+TEST_F(BleScannerTest, TestDiscovery_Success) {
+  std::string valid_service_data_for_registered_device = "abcde";
+  ASSERT_TRUE(valid_service_data_for_registered_device.size() >=
+              kMinNumBytesInServiceData);
+
+  EXPECT_TRUE(ble_scanner_->RegisterScanFilterForDevice(test_devices_[0]));
+  EXPECT_TRUE(ble_scanner_->IsDeviceRegistered(test_devices_[0].GetDeviceId()));
+
+  InvokeAdapterCallback();
+  InvokeDiscoveryStartedCallback();
+
+  // Registered device connects.
+  MockBluetoothDeviceWithServiceData device5(
+      mock_adapter_.get(), valid_service_data_for_registered_device);
+  mock_eid_generator_->set_identified_device(&test_devices_[0]);
+  ble_scanner_->DeviceAdded(mock_adapter_.get(), &device5);
+  EXPECT_EQ(1, mock_eid_generator_->num_identify_calls());
+  EXPECT_EQ(1, mock_observer_->GetNumCalls());
+  EXPECT_EQ(1, static_cast<int>(mock_observer_->bluetooth_devices().size()));
+  EXPECT_EQ(&device5, mock_observer_->bluetooth_devices()[0]);
+  EXPECT_EQ(1, static_cast<int>(mock_observer_->remote_devices().size()));
+  EXPECT_EQ(test_devices_[0], mock_observer_->remote_devices()[0]);
+
+  EXPECT_TRUE(ble_scanner_->IsDeviceRegistered(test_devices_[0].GetDeviceId()));
+  EXPECT_TRUE(ble_scanner_->UnregisterScanFilterForDevice(test_devices_[0]));
+  EXPECT_EQ(1, mock_eid_generator_->num_identify_calls());
+  EXPECT_EQ(1, mock_observer_->GetNumCalls());
+}
+
+TEST_F(BleScannerTest, TestDiscovery_MultipleObservers) {
+  MockBleScannerObserver extra_observer;
+  ble_scanner_->AddObserver(&extra_observer);
+
+  EXPECT_TRUE(ble_scanner_->RegisterScanFilterForDevice(test_devices_[0]));
+  EXPECT_TRUE(ble_scanner_->IsDeviceRegistered(test_devices_[0].GetDeviceId()));
+
+  InvokeAdapterCallback();
+  InvokeDiscoveryStartedCallback();
+
+  MockBluetoothDeviceWithServiceData mock_bluetooth_device(mock_adapter_.get(),
+                                                           "fakeServiceData");
+  mock_eid_generator_->set_identified_device(&test_devices_[0]);
+  ble_scanner_->DeviceAdded(mock_adapter_.get(), &mock_bluetooth_device);
+
+  EXPECT_EQ(1, mock_observer_->GetNumCalls());
+  EXPECT_EQ(1, static_cast<int>(mock_observer_->bluetooth_devices().size()));
+  EXPECT_EQ(&mock_bluetooth_device, mock_observer_->bluetooth_devices()[0]);
+  EXPECT_EQ(1, static_cast<int>(mock_observer_->remote_devices().size()));
+  EXPECT_EQ(test_devices_[0], mock_observer_->remote_devices()[0]);
+
+  EXPECT_EQ(1, extra_observer.GetNumCalls());
+  EXPECT_EQ(1, static_cast<int>(extra_observer.bluetooth_devices().size()));
+  EXPECT_EQ(&mock_bluetooth_device, extra_observer.bluetooth_devices()[0]);
+  EXPECT_EQ(1, static_cast<int>(extra_observer.remote_devices().size()));
+  EXPECT_EQ(test_devices_[0], extra_observer.remote_devices()[0]);
+
+  // Now, unregister both observers.
+  ble_scanner_->RemoveObserver(mock_observer_.get());
+  ble_scanner_->RemoveObserver(&extra_observer);
+
+  // Now, simulate another scan being received. The observers should not be
+  // notified since they are unregistered, so they should still have a call
+  // count of 1.
+  ble_scanner_->DeviceAdded(mock_adapter_.get(), &mock_bluetooth_device);
+  EXPECT_EQ(1, mock_observer_->GetNumCalls());
+  EXPECT_EQ(1, extra_observer.GetNumCalls());
+
+  EXPECT_TRUE(ble_scanner_->IsDeviceRegistered(test_devices_[0].GetDeviceId()));
+  EXPECT_TRUE(ble_scanner_->UnregisterScanFilterForDevice(test_devices_[0]));
+}
+
+TEST_F(BleScannerTest, TestRegistrationLimit) {
+  EXPECT_TRUE(ble_scanner_->RegisterScanFilterForDevice(test_devices_[0]));
+  EXPECT_TRUE(ble_scanner_->IsDeviceRegistered(test_devices_[0].GetDeviceId()));
+  EXPECT_TRUE(ble_scanner_->RegisterScanFilterForDevice(test_devices_[1]));
+  EXPECT_TRUE(ble_scanner_->IsDeviceRegistered(test_devices_[1].GetDeviceId()));
+
+  // Attempt to register another device. Registration should fail since the
+  // maximum number of devices have already been registered.
+  ASSERT_EQ(2, kMaxConcurrentAdvertisements);
+  EXPECT_FALSE(ble_scanner_->RegisterScanFilterForDevice(test_devices_[2]));
+  EXPECT_FALSE(
+      ble_scanner_->IsDeviceRegistered(test_devices_[2].GetDeviceId()));
+
+  // Unregistering a device which is not registered should also return false.
+  EXPECT_FALSE(ble_scanner_->UnregisterScanFilterForDevice(test_devices_[2]));
+  EXPECT_FALSE(
+      ble_scanner_->IsDeviceRegistered(test_devices_[2].GetDeviceId()));
+
+  // Unregister device 0.
+  EXPECT_TRUE(ble_scanner_->IsDeviceRegistered(test_devices_[0].GetDeviceId()));
+  EXPECT_TRUE(ble_scanner_->UnregisterScanFilterForDevice(test_devices_[0]));
+  EXPECT_FALSE(
+      ble_scanner_->IsDeviceRegistered(test_devices_[0].GetDeviceId()));
+
+  // Now, device 2 can be registered.
+  EXPECT_TRUE(ble_scanner_->RegisterScanFilterForDevice(test_devices_[2]));
+  EXPECT_TRUE(ble_scanner_->IsDeviceRegistered(test_devices_[2].GetDeviceId()));
+
+  // Now, unregister the devices.
+  EXPECT_TRUE(ble_scanner_->IsDeviceRegistered(test_devices_[1].GetDeviceId()));
+  EXPECT_TRUE(ble_scanner_->UnregisterScanFilterForDevice(test_devices_[1]));
+  EXPECT_FALSE(
+      ble_scanner_->IsDeviceRegistered(test_devices_[1].GetDeviceId()));
+
+  EXPECT_TRUE(ble_scanner_->IsDeviceRegistered(test_devices_[2].GetDeviceId()));
+  EXPECT_TRUE(ble_scanner_->UnregisterScanFilterForDevice(test_devices_[2]));
+  EXPECT_FALSE(
+      ble_scanner_->IsDeviceRegistered(test_devices_[2].GetDeviceId()));
+}
+
+TEST_F(BleScannerTest, TestAdapterPoweredChanged) {
+  // This test starts with the adapter powered on, then turns power off before
+  // discovery starts, then turns power back on and allows discovery to
+  // complete, then turns power back off again, and finally turns it back on to
+  // complete another discovery session.
+  EXPECT_CALL(*mock_adapter_, IsPowered())
+      .WillOnce(Return(true))
+      .WillOnce(Return(false))
+      .WillOnce(Return(true))
+      .WillOnce(Return(false))
+      .WillOnce(Return(true));
+
+  EXPECT_TRUE(ble_scanner_->RegisterScanFilterForDevice(test_devices_[0]));
+  EXPECT_TRUE(ble_scanner_->IsDeviceRegistered(test_devices_[0].GetDeviceId()));
+  InvokeAdapterCallback();
+
+  // The discovery session should have been requested but not yet initialized.
+  EXPECT_TRUE(stored_discovery_filter_.get());
+  EXPECT_FALSE(mock_discovery_session_);
+
+  // Turn the adapter off before the discovery session starts.
+  ble_scanner_->AdapterPoweredChanged(mock_adapter_.get(), false);
+
+  // Turn the adapter back on, and finish initializing discovery this time.
+  ble_scanner_->AdapterPoweredChanged(mock_adapter_.get(), true);
+  InvokeDiscoveryStartedCallback();
+
+  // The session should have been started.
+  device::MockBluetoothDiscoverySession* session1 = mock_discovery_session_;
+  EXPECT_NE(nullptr, session1);
+
+  // Now turn the adapter off again.
+  ble_scanner_->AdapterPoweredChanged(mock_adapter_.get(), false);
+
+  // Turn the adapter back on.
+  ble_scanner_->AdapterPoweredChanged(mock_adapter_.get(), true);
+  InvokeDiscoveryStartedCallback();
+
+  // A new session should have started, so the session objects should not be the
+  // same.
+  device::MockBluetoothDiscoverySession* session2 = mock_discovery_session_;
+  EXPECT_NE(nullptr, session2);
+  EXPECT_NE(session1, session2);
+
+  // Unregister device.
+  EXPECT_TRUE(ble_scanner_->IsDeviceRegistered(test_devices_[0].GetDeviceId()));
+  EXPECT_TRUE(ble_scanner_->UnregisterScanFilterForDevice(test_devices_[0]));
+  EXPECT_FALSE(
+      ble_scanner_->IsDeviceRegistered(test_devices_[0].GetDeviceId()));
+}
+
+}  // namespace tether
+
+}  // namespace chromeos
diff --git a/chromeos/components/tether/local_device_data_provider.h b/chromeos/components/tether/local_device_data_provider.h
index 0d507cc..4125049 100644
--- a/chromeos/components/tether/local_device_data_provider.h
+++ b/chromeos/components/tether/local_device_data_provider.h
@@ -36,7 +36,7 @@
   // Fetches the public key and/or the beacon seeds for the local device.
   // Returns whether the operation succeeded. If |nullptr| is passed as a
   // parameter, the associated data will not be fetched.
-  bool GetLocalDeviceData(
+  virtual bool GetLocalDeviceData(
       std::string* public_key_out,
       std::vector<cryptauth::BeaconSeed>* beacon_seeds_out) const;
 
diff --git a/chromeos/components/tether/mock_local_device_data_provider.cc b/chromeos/components/tether/mock_local_device_data_provider.cc
new file mode 100644
index 0000000..2774368
--- /dev/null
+++ b/chromeos/components/tether/mock_local_device_data_provider.cc
@@ -0,0 +1,56 @@
+// 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 "chromeos/components/tether/mock_local_device_data_provider.h"
+
+#include "components/cryptauth/cryptauth_device_manager.h"
+#include "components/cryptauth/cryptauth_enrollment_manager.h"
+#include "components/cryptauth/proto/cryptauth_api.pb.h"
+
+namespace chromeos {
+
+namespace tether {
+
+MockLocalDeviceDataProvider::MockLocalDeviceDataProvider()
+    : LocalDeviceDataProvider(nullptr, nullptr) {}
+
+MockLocalDeviceDataProvider::~MockLocalDeviceDataProvider() {}
+
+void MockLocalDeviceDataProvider::SetPublicKey(
+    std::unique_ptr<std::string> public_key) {
+  if (public_key) {
+    public_key_ = std::move(public_key);
+  } else {
+    public_key_.reset();
+  }
+}
+
+void MockLocalDeviceDataProvider::SetBeaconSeeds(
+    std::unique_ptr<std::vector<cryptauth::BeaconSeed>> beacon_seeds) {
+  if (beacon_seeds) {
+    beacon_seeds_ = std::move(beacon_seeds);
+  } else {
+    beacon_seeds_.reset();
+  }
+}
+
+bool MockLocalDeviceDataProvider::GetLocalDeviceData(
+    std::string* public_key_out,
+    std::vector<cryptauth::BeaconSeed>* beacon_seeds_out) const {
+  if (public_key_ && beacon_seeds_) {
+    if (public_key_out) {
+      *public_key_out = *public_key_;
+    }
+    if (beacon_seeds_out) {
+      *beacon_seeds_out = *beacon_seeds_;
+    }
+    return true;
+  }
+
+  return false;
+}
+
+}  // namespace tether
+
+}  // namespace cryptauth
diff --git a/chromeos/components/tether/mock_local_device_data_provider.h b/chromeos/components/tether/mock_local_device_data_provider.h
new file mode 100644
index 0000000..40fa924
--- /dev/null
+++ b/chromeos/components/tether/mock_local_device_data_provider.h
@@ -0,0 +1,50 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROMEOS_COMPONENTS_TETHER_MOCK_LOCAL_DEVICE_DATA_PROVIDER_H
+#define CHROMEOS_COMPONENTS_TETHER_MOCK_LOCAL_DEVICE_DATA_PROVIDER_H
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "base/macros.h"
+#include "base/memory/ptr_util.h"
+#include "chromeos/components/tether/local_device_data_provider.h"
+
+namespace cryptauth {
+class BeaconSeed;
+}
+
+namespace chromeos {
+
+namespace tether {
+
+// Test double for LocalDeviceDataProvider.
+class MockLocalDeviceDataProvider : public LocalDeviceDataProvider {
+ public:
+  MockLocalDeviceDataProvider();
+  ~MockLocalDeviceDataProvider() override;
+
+  void SetPublicKey(std::unique_ptr<std::string> public_key);
+  void SetBeaconSeeds(
+      std::unique_ptr<std::vector<cryptauth::BeaconSeed>> beacon_seeds);
+
+  // LocalDeviceDataProvider:
+  bool GetLocalDeviceData(
+      std::string* public_key_out,
+      std::vector<cryptauth::BeaconSeed>* beacon_seeds_out) const override;
+
+ private:
+  std::unique_ptr<std::string> public_key_;
+  std::unique_ptr<std::vector<cryptauth::BeaconSeed>> beacon_seeds_;
+
+  DISALLOW_COPY_AND_ASSIGN(MockLocalDeviceDataProvider);
+};
+
+}  // namespace tether
+
+}  // namespace chromeos
+
+#endif  // CHROMEOS_COMPONENTS_TETHER_MOCK_LOCAL_DEVICE_DATA_PROVIDER_H
diff --git a/components/cronet/ios/BUILD.gn b/components/cronet/ios/BUILD.gn
index ba6ad852..a449ceba 100644
--- a/components/cronet/ios/BUILD.gn
+++ b/components/cronet/ios/BUILD.gn
@@ -53,6 +53,7 @@
     "../url_request_context_config.h",
     "Cronet.h",
     "Cronet.mm",
+    "cronet_c_for_grpc.h",
     "cronet_environment.h",
     "cronet_environment.mm",
   ]
@@ -153,7 +154,10 @@
     "//components/grpc_support",
   ]
 
-  public_headers = [ "Cronet.h" ]
+  public_headers = [
+    "Cronet.h",
+    "cronet_c_for_grpc.h",
+  ]
   public_headers += grpc_public_headers
 
   sources = [
diff --git a/components/cronet/ios/Cronet.h b/components/cronet/ios/Cronet.h
index fc42612..ba3dd7dc 100644
--- a/components/cronet/ios/Cronet.h
+++ b/components/cronet/ios/Cronet.h
@@ -6,6 +6,10 @@
 
 #include "bidirectional_stream_c.h"
 
+// TODO(mef): Remove this header after transition to bidirectional_stream_c.h
+// See crbug.com/650462 for details.
+#include "cronet_c_for_grpc.h"
+
 // A block, that takes a request, and returns YES if the request should
 // be handled.
 typedef BOOL (^RequestFilterBlock)(NSURLRequest* request);
diff --git a/components/cronet/ios/cronet_c_for_grpc.h b/components/cronet/ios/cronet_c_for_grpc.h
new file mode 100644
index 0000000..f2700023
--- /dev/null
+++ b/components/cronet/ios/cronet_c_for_grpc.h
@@ -0,0 +1,36 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_CRONET_IOS_CRONET_C_FOR_GRPC_H_
+#define COMPONENTS_CRONET_IOS_CRONET_C_FOR_GRPC_H_
+
+#include "bidirectional_stream_c.h"
+
+// TODO(mef): Remove this header after transition to bidirectional_stream_c.h
+// See crbug.com/650462 for details.
+
+/* Define deprecated Cronet Engine API using current API. */
+#define cronet_engine stream_engine
+
+/* Define deprecated Bidirectional Stream  API using current API. */
+#define cronet_bidirectional_stream bidirectional_stream
+#define cronet_bidirectional_stream_header bidirectional_stream_header
+#define cronet_bidirectional_stream_header_array \
+  bidirectional_stream_header_array
+#define cronet_bidirectional_stream_callback bidirectional_stream_callback
+
+#define cronet_bidirectional_stream_create bidirectional_stream_create
+#define cronet_bidirectional_stream_destroy bidirectional_stream_destroy
+#define cronet_bidirectional_stream_disable_auto_flush \
+  bidirectional_stream_disable_auto_flush
+#define cronet_bidirectional_stream_delay_request_headers_until_flush \
+  bidirectional_stream_delay_request_headers_until_flush
+#define cronet_bidirectional_stream_start bidirectional_stream_start
+#define cronet_bidirectional_stream_read bidirectional_stream_read
+#define cronet_bidirectional_stream_write bidirectional_stream_write
+#define cronet_bidirectional_stream_flush bidirectional_stream_flush
+#define cronet_bidirectional_stream_cancel bidirectional_stream_cancel
+#define cronet_bidirectional_stream_is_done bidirectional_stream_is_done
+
+#endif  // COMPONENTS_CRONET_IOS_CRONET_C_FOR_GRPC_H_
diff --git a/components/cryptauth/BUILD.gn b/components/cryptauth/BUILD.gn
index 46d9cdc..6093685 100644
--- a/components/cryptauth/BUILD.gn
+++ b/components/cryptauth/BUILD.gn
@@ -38,6 +38,8 @@
     "eid_generator.h",
     "pref_names.cc",
     "pref_names.h",
+    "remote_beacon_seed_fetcher.cc",
+    "remote_beacon_seed_fetcher.h",
     "remote_device.cc",
     "remote_device.h",
     "secure_message_delegate.cc",
@@ -88,6 +90,8 @@
     "mock_cryptauth_client.h",
     "mock_eid_generator.cc",
     "mock_eid_generator.h",
+    "mock_remote_beacon_seed_fetcher.cc",
+    "mock_remote_beacon_seed_fetcher.h",
     "mock_sync_scheduler.cc",
     "mock_sync_scheduler.h",
     "remote_device_test_util.cc",
@@ -119,6 +123,7 @@
     "cryptauth_gcm_manager_impl_unittest.cc",
     "eid_generator_unittest.cc",
     "fake_secure_message_delegate_unittest.cc",
+    "remote_beacon_seed_fetcher_unittest.cc",
     "sync_scheduler_impl_unittest.cc",
     "wire_message_unittest.cc",
   ]
diff --git a/components/cryptauth/cryptauth_device_manager.cc b/components/cryptauth/cryptauth_device_manager.cc
index f2e6b55..98931e4 100644
--- a/components/cryptauth/cryptauth_device_manager.cc
+++ b/components/cryptauth/cryptauth_device_manager.cc
@@ -312,8 +312,18 @@
   UpdateUnlockKeysFromPrefs();
 }
 
+// Test-only constructor.
+CryptAuthDeviceManager::CryptAuthDeviceManager()
+    : clock_(nullptr),
+      client_factory_(nullptr),
+      gcm_manager_(nullptr),
+      pref_service_(nullptr),
+      weak_ptr_factory_(this) {}
+
 CryptAuthDeviceManager::~CryptAuthDeviceManager() {
-  gcm_manager_->RemoveObserver(this);
+  if (gcm_manager_) {
+    gcm_manager_->RemoveObserver(this);
+  }
 }
 
 // static
diff --git a/components/cryptauth/cryptauth_device_manager.h b/components/cryptauth/cryptauth_device_manager.h
index a4b8deb..96ba7ff40 100644
--- a/components/cryptauth/cryptauth_device_manager.h
+++ b/components/cryptauth/cryptauth_device_manager.h
@@ -106,15 +106,19 @@
   bool IsRecoveringFromFailure() const;
 
   // Returns a list of all remote devices that have been synced.
-  std::vector<cryptauth::ExternalDeviceInfo> GetSyncedDevices() const;
+  virtual std::vector<cryptauth::ExternalDeviceInfo> GetSyncedDevices() const;
 
   // Returns a list of remote devices that can unlock the user's other devices.
-  std::vector<cryptauth::ExternalDeviceInfo> GetUnlockKeys() const;
+  virtual std::vector<cryptauth::ExternalDeviceInfo> GetUnlockKeys() const;
 
   // Returns a list of remote devices that can host tether hotspots.
-  std::vector<cryptauth::ExternalDeviceInfo> GetTetherHosts() const;
+  virtual std::vector<cryptauth::ExternalDeviceInfo> GetTetherHosts() const;
 
  protected:
+  // Empty constructor, to be used by tests to mock the device manager. Do not
+  // use this constructor outside of tests.
+  CryptAuthDeviceManager();
+
   // Creates a new SyncScheduler instance. Exposed for testing.
   virtual std::unique_ptr<SyncScheduler> CreateSyncScheduler();
 
diff --git a/components/cryptauth/mock_eid_generator.cc b/components/cryptauth/mock_eid_generator.cc
index adebf26b..c9515a7 100644
--- a/components/cryptauth/mock_eid_generator.cc
+++ b/components/cryptauth/mock_eid_generator.cc
@@ -11,7 +11,8 @@
 MockEidGenerator::MockEidGenerator() : background_scan_filter_(nullptr),
                                        advertisement_(nullptr),
                                        possible_advertisements_(nullptr),
-                                       identified_device_(nullptr) {}
+                                       identified_device_(nullptr),
+                                       num_identify_calls_(0) {}
 
 MockEidGenerator::~MockEidGenerator() {}
 
@@ -62,6 +63,11 @@
     const std::string& advertisement_service_data,
     const std::vector<RemoteDevice>& device_list,
     const std::vector<BeaconSeed>& scanning_device_beacon_seeds) const {
+  // Increment num_identify_calls_. Since this overrides a const method, some
+  // hacking is needed to modify the num_identify_calls_ instance variable.
+  int* num_identify_calls_ptr = const_cast<int*>(&num_identify_calls_);
+  *num_identify_calls_ptr = *num_identify_calls_ptr + 1;
+
   return identified_device_;
 }
 
diff --git a/components/cryptauth/mock_eid_generator.h b/components/cryptauth/mock_eid_generator.h
index 6317200..4f624a2 100644
--- a/components/cryptauth/mock_eid_generator.h
+++ b/components/cryptauth/mock_eid_generator.h
@@ -60,11 +60,17 @@
       const std::vector<BeaconSeed>& scanning_device_beacon_seeds)
           const override;
 
+  int num_identify_calls() {
+    return num_identify_calls_;
+  }
+
  private:
   std::unique_ptr<EidData> background_scan_filter_;
   std::unique_ptr<DataWithTimestamp> advertisement_;
   std::unique_ptr<std::vector<std::string>> possible_advertisements_;
   const RemoteDevice* identified_device_;
+
+  int num_identify_calls_;
 };
 
 }  // namespace cryptauth
diff --git a/components/cryptauth/mock_remote_beacon_seed_fetcher.cc b/components/cryptauth/mock_remote_beacon_seed_fetcher.cc
new file mode 100644
index 0000000..73058fe
--- /dev/null
+++ b/components/cryptauth/mock_remote_beacon_seed_fetcher.cc
@@ -0,0 +1,35 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/cryptauth/mock_remote_beacon_seed_fetcher.h"
+
+#include "components/cryptauth/cryptauth_device_manager.h"
+
+namespace cryptauth {
+
+MockRemoteBeaconSeedFetcher::MockRemoteBeaconSeedFetcher()
+    : RemoteBeaconSeedFetcher(nullptr) {}
+
+MockRemoteBeaconSeedFetcher::~MockRemoteBeaconSeedFetcher() {}
+
+bool MockRemoteBeaconSeedFetcher::FetchSeedsForDevice(
+    const RemoteDevice& remote_device,
+    std::vector<BeaconSeed>* beacon_seeds_out) {
+  const auto& seeds_iter =
+      public_key_to_beacon_seeds_map_.find(remote_device.public_key);
+  if (seeds_iter == public_key_to_beacon_seeds_map_.end()) {
+    return false;
+  }
+
+  *beacon_seeds_out = seeds_iter->second;
+  return true;
+}
+
+void MockRemoteBeaconSeedFetcher::SetSeedsForDevice(
+    const RemoteDevice& remote_device,
+    const std::vector<BeaconSeed>& beacon_seeds) {
+  public_key_to_beacon_seeds_map_[remote_device.public_key] = beacon_seeds;
+}
+
+}  // namespace cryptauth
diff --git a/components/cryptauth/mock_remote_beacon_seed_fetcher.h b/components/cryptauth/mock_remote_beacon_seed_fetcher.h
new file mode 100644
index 0000000..db4cac9
--- /dev/null
+++ b/components/cryptauth/mock_remote_beacon_seed_fetcher.h
@@ -0,0 +1,39 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_CRYPTAUTH_MOCK_BEACON_SEED_FETCHER_H_
+#define COMPONENTS_CRYPTAUTH_MOCK_BEACON_SEED_FETCHER_H_
+
+#include <map>
+#include <vector>
+
+#include "components/cryptauth/proto/cryptauth_api.pb.h"
+#include "components/cryptauth/remote_beacon_seed_fetcher.h"
+
+namespace cryptauth {
+
+class MockRemoteBeaconSeedFetcher : public RemoteBeaconSeedFetcher {
+ public:
+  MockRemoteBeaconSeedFetcher();
+  ~MockRemoteBeaconSeedFetcher() override;
+
+  // RemoteBeaconSeedFetcher:
+  bool FetchSeedsForDevice(
+      const RemoteDevice& remote_device,
+      std::vector<BeaconSeed>* beacon_seeds_out) override;
+
+  void SetSeedsForDevice(
+      const RemoteDevice& remote_device,
+      const std::vector<BeaconSeed>& beacon_seeds);
+
+ private:
+  std::map<std::string, std::vector<BeaconSeed>>
+      public_key_to_beacon_seeds_map_;
+
+  DISALLOW_COPY_AND_ASSIGN(MockRemoteBeaconSeedFetcher);
+};
+
+}  // namespace cryptauth
+
+#endif  // COMPONENTS_CRYPTAUTH_MOCK_BEACON_SEED_FETCHER_H_
diff --git a/components/cryptauth/remote_beacon_seed_fetcher.cc b/components/cryptauth/remote_beacon_seed_fetcher.cc
new file mode 100644
index 0000000..e1f47c8c
--- /dev/null
+++ b/components/cryptauth/remote_beacon_seed_fetcher.cc
@@ -0,0 +1,41 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/cryptauth/remote_beacon_seed_fetcher.h"
+
+#include "components/cryptauth/cryptauth_device_manager.h"
+
+namespace cryptauth {
+
+RemoteBeaconSeedFetcher::RemoteBeaconSeedFetcher(
+    const CryptAuthDeviceManager* device_manager)
+    : device_manager_(device_manager) {}
+
+RemoteBeaconSeedFetcher::~RemoteBeaconSeedFetcher() {}
+
+bool RemoteBeaconSeedFetcher::FetchSeedsForDevice(
+    const RemoteDevice& remote_device,
+    std::vector<BeaconSeed>* beacon_seeds_out) {
+  if (remote_device.public_key.empty()) {
+    return false;
+  }
+
+  for(const auto& device_info : device_manager_->GetSyncedDevices()) {
+    if (device_info.public_key() == remote_device.public_key) {
+      if (device_info.beacon_seeds_size() == 0) {
+        return false;
+      }
+
+      beacon_seeds_out->clear();
+      for (int i = 0; i < device_info.beacon_seeds_size(); i++) {
+        beacon_seeds_out->push_back(device_info.beacon_seeds(i));
+      }
+      return true;
+    }
+  }
+
+  return false;
+}
+
+}  // namespace cryptauth
diff --git a/components/cryptauth/remote_beacon_seed_fetcher.h b/components/cryptauth/remote_beacon_seed_fetcher.h
new file mode 100644
index 0000000..8d4401e8
--- /dev/null
+++ b/components/cryptauth/remote_beacon_seed_fetcher.h
@@ -0,0 +1,40 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_CRYPTAUTH_REMOTE_BEACON_SEED_FETCHER_H_
+#define COMPONENTS_CRYPTAUTH_REMOTE_BEACON_SEED_FETCHER_H_
+
+#include <vector>
+
+#include "base/macros.h"
+#include "components/cryptauth/proto/cryptauth_api.pb.h"
+#include "components/cryptauth/remote_device.h"
+
+namespace cryptauth {
+
+class CryptAuthDeviceManager;
+
+// Fetches |BeaconSeed|s corresponding to a given |RemoteDevice|. Note that an
+// alternate solution would be to embed a list of |BeaconSeed|s in each
+// |RemoteDevice| object; however, because |BeaconSeed| objects take up almost
+// 1kB of memory apiece, that approach adds unnecessary memory overhead.
+class RemoteBeaconSeedFetcher {
+ public:
+  RemoteBeaconSeedFetcher(const CryptAuthDeviceManager* device_manager);
+  virtual ~RemoteBeaconSeedFetcher();
+
+  virtual bool FetchSeedsForDevice(
+      const RemoteDevice& remote_device,
+      std::vector<BeaconSeed>* beacon_seeds_out);
+
+ private:
+  // Not owned by this instance and must outlive it.
+  const CryptAuthDeviceManager* device_manager_;
+
+  DISALLOW_COPY_AND_ASSIGN(RemoteBeaconSeedFetcher);
+};
+
+}  // namespace cryptauth
+
+#endif  // COMPONENTS_CRYPTAUTH_REMOTE_BEACON_SEED_FETCHER_H_
diff --git a/components/cryptauth/remote_beacon_seed_fetcher_unittest.cc b/components/cryptauth/remote_beacon_seed_fetcher_unittest.cc
new file mode 100644
index 0000000..25cda2f
--- /dev/null
+++ b/components/cryptauth/remote_beacon_seed_fetcher_unittest.cc
@@ -0,0 +1,173 @@
+// 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/cryptauth/remote_beacon_seed_fetcher.h"
+
+#include "base/macros.h"
+#include "base/memory/ptr_util.h"
+#include "components/cryptauth/cryptauth_client.h"
+#include "components/cryptauth/cryptauth_device_manager.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using testing::StrictMock;
+using testing::Return;
+
+namespace cryptauth {
+
+namespace {
+
+const std::string fake_beacon_seed1_data = "fakeBeaconSeed1Data";
+const int64_t fake_beacon_seed1_start_ms = 1000L;
+const int64_t fake_beacon_seed1_end_ms = 2000L;
+
+const std::string fake_beacon_seed2_data = "fakeBeaconSeed2Data";
+const int64_t fake_beacon_seed2_start_ms = 2000L;
+const int64_t fake_beacon_seed2_end_ms = 3000L;
+
+const std::string fake_beacon_seed3_data = "fakeBeaconSeed3Data";
+const int64_t fake_beacon_seed3_start_ms = 1000L;
+const int64_t fake_beacon_seed3_end_ms = 2000L;
+
+const std::string fake_beacon_seed4_data = "fakeBeaconSeed4Data";
+const int64_t fake_beacon_seed4_start_ms = 2000L;
+const int64_t fake_beacon_seed4_end_ms = 3000L;
+
+const std::string public_key1 = "publicKey1";
+const std::string public_key2 = "publicKey2";
+
+class MockDeviceManager : public CryptAuthDeviceManager {
+ public:
+  MockDeviceManager() {}
+  ~MockDeviceManager() override {}
+
+  MOCK_CONST_METHOD0(GetSyncedDevices, std::vector<ExternalDeviceInfo>());
+};
+
+RemoteDevice CreateRemoteDevice(const std::string& public_key) {
+  RemoteDevice remote_device;
+  remote_device.public_key = public_key;
+  return remote_device;
+}
+
+ExternalDeviceInfo CreateFakeInfo1() {
+  BeaconSeed seed1;
+  seed1.set_data(fake_beacon_seed1_data);
+  seed1.set_start_time_millis(fake_beacon_seed1_start_ms);
+  seed1.set_end_time_millis(fake_beacon_seed1_end_ms);
+
+  BeaconSeed seed2;
+  seed2.set_data(fake_beacon_seed2_data);
+  seed2.set_start_time_millis(fake_beacon_seed2_start_ms);
+  seed2.set_end_time_millis(fake_beacon_seed2_end_ms);
+
+  ExternalDeviceInfo info1;
+  info1.set_public_key(public_key1);
+  info1.add_beacon_seeds()->CopyFrom(seed1);
+  info1.add_beacon_seeds()->CopyFrom(seed2);
+  return info1;
+}
+
+ExternalDeviceInfo CreateFakeInfo2() {
+  BeaconSeed seed3;
+  seed3.set_data(fake_beacon_seed3_data);
+  seed3.set_start_time_millis(fake_beacon_seed3_start_ms);
+  seed3.set_end_time_millis(fake_beacon_seed3_end_ms);
+
+  BeaconSeed seed4;
+  seed4.set_data(fake_beacon_seed4_data);
+  seed4.set_start_time_millis(fake_beacon_seed4_start_ms);
+  seed4.set_end_time_millis(fake_beacon_seed4_end_ms);
+
+  ExternalDeviceInfo info2;
+  info2.set_public_key(public_key2);
+  info2.add_beacon_seeds()->CopyFrom(seed3);
+  info2.add_beacon_seeds()->CopyFrom(seed4);
+  return info2;
+}
+
+}  // namespace
+
+class CryptAuthRemoteBeaconSeedFetcherTest : public testing::Test {
+ protected:
+  CryptAuthRemoteBeaconSeedFetcherTest()
+      : fake_info1_(CreateFakeInfo1()), fake_info2_(CreateFakeInfo2()) {}
+
+  void SetUp() override {
+    mock_device_manager_ = base::MakeUnique<MockDeviceManager>();
+    fetcher_ = base::MakeUnique<StrictMock<RemoteBeaconSeedFetcher>>(
+        mock_device_manager_.get());
+  }
+
+  std::unique_ptr<RemoteBeaconSeedFetcher> fetcher_;
+  std::unique_ptr<MockDeviceManager> mock_device_manager_;
+
+  const ExternalDeviceInfo fake_info1_;
+  const ExternalDeviceInfo fake_info2_;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(CryptAuthRemoteBeaconSeedFetcherTest);
+};
+
+TEST_F(CryptAuthRemoteBeaconSeedFetcherTest, TestRemoteDeviceWithNoPublicKey) {
+  RemoteDevice device = CreateRemoteDevice("");
+
+  std::vector<BeaconSeed> seeds;
+  EXPECT_FALSE(fetcher_->FetchSeedsForDevice(device, &seeds));
+}
+
+TEST_F(CryptAuthRemoteBeaconSeedFetcherTest, TestNoSyncedDevices) {
+  RemoteDevice device = CreateRemoteDevice(public_key1);
+
+  EXPECT_CALL(*mock_device_manager_, GetSyncedDevices())
+      .WillOnce(Return(std::vector<ExternalDeviceInfo>()));
+
+  std::vector<BeaconSeed> seeds;
+  EXPECT_FALSE(fetcher_->FetchSeedsForDevice(device, &seeds));
+}
+
+TEST_F(CryptAuthRemoteBeaconSeedFetcherTest, TestDeviceHasDifferentPublicKey) {
+  // A public key which is different from the public keys of all of the synced
+  // devices.
+  RemoteDevice device = CreateRemoteDevice("differentPublicKey");
+
+  std::vector<ExternalDeviceInfo> device_infos = {fake_info1_, fake_info2_};
+  EXPECT_CALL(*mock_device_manager_, GetSyncedDevices())
+      .WillOnce(Return(device_infos));
+
+  std::vector<BeaconSeed> seeds;
+  EXPECT_FALSE(fetcher_->FetchSeedsForDevice(device, &seeds));
+}
+
+TEST_F(CryptAuthRemoteBeaconSeedFetcherTest, TestSuccess) {
+  RemoteDevice device1 = CreateRemoteDevice(public_key1);
+  RemoteDevice device2 = CreateRemoteDevice(public_key2);
+
+  std::vector<ExternalDeviceInfo> device_infos = {fake_info1_, fake_info2_};
+  EXPECT_CALL(*mock_device_manager_, GetSyncedDevices())
+      .Times(2)
+      .WillRepeatedly(Return(device_infos));
+
+  std::vector<BeaconSeed> seeds1;
+  ASSERT_TRUE(fetcher_->FetchSeedsForDevice(device1, &seeds1));
+  ASSERT_EQ(static_cast<size_t>(2), seeds1.size());
+  EXPECT_EQ(fake_beacon_seed1_data, seeds1[0].data());
+  EXPECT_EQ(fake_beacon_seed1_start_ms, seeds1[0].start_time_millis());
+  EXPECT_EQ(fake_beacon_seed1_end_ms, seeds1[0].end_time_millis());
+  EXPECT_EQ(fake_beacon_seed2_data, seeds1[1].data());
+  EXPECT_EQ(fake_beacon_seed2_start_ms, seeds1[1].start_time_millis());
+  EXPECT_EQ(fake_beacon_seed2_end_ms, seeds1[1].end_time_millis());
+
+  std::vector<BeaconSeed> seeds2;
+  ASSERT_TRUE(fetcher_->FetchSeedsForDevice(device2, &seeds2));
+  ASSERT_EQ(static_cast<size_t>(2), seeds2.size());
+  EXPECT_EQ(fake_beacon_seed3_data, seeds2[0].data());
+  EXPECT_EQ(fake_beacon_seed3_start_ms, seeds2[0].start_time_millis());
+  EXPECT_EQ(fake_beacon_seed3_end_ms, seeds2[0].end_time_millis());
+  EXPECT_EQ(fake_beacon_seed4_data, seeds2[1].data());
+  EXPECT_EQ(fake_beacon_seed4_start_ms, seeds2[1].start_time_millis());
+  EXPECT_EQ(fake_beacon_seed4_end_ms, seeds2[1].end_time_millis());
+}
+
+}  // namespace cryptauth
diff --git a/components/exo/gamepad.cc b/components/exo/gamepad.cc
index 876d05d8..8d5e2311 100644
--- a/components/exo/gamepad.cc
+++ b/components/exo/gamepad.cc
@@ -123,7 +123,6 @@
     fetcher_->GetGamepadData(
         false /* No hardware changed notification from the system */);
 
-    new_state.length = 0;
     device::PadState& pad_state = pad_states_.get()[0];
 
     // After querying the gamepad clear the state if it did not have it's active
@@ -137,22 +136,15 @@
     MapAndSanitizeGamepadData(&pad_state, &new_state.items[0],
                               false /* Don't sanitize gamepad data */);
 
-    // If the gamepad was active then increment the length of the WebGamepads
-    // struct to indicate it's valid, then set the pad state to inactive. If the
-    // gamepad is still actively reporting the next call to GetGamepadData will
-    // set the active state to active again.
-    if (pad_state.active_state) {
-      new_state.length++;
+    // If the gamepad is still actively reporting the next call to
+    // GetGamepadData will set the active state to active again.
+    if (pad_state.active_state)
       pad_state.active_state = device::GAMEPAD_INACTIVE;
-    }
 
-    if (std::max(new_state.length, state_.length) > 0) {
-      if (new_state.items[0].connected != state_.items[0].connected ||
-          new_state.items[0].timestamp > state_.items[0].timestamp) {
-        origin_task_runner_->PostTask(
-            FROM_HERE,
-            base::Bind(process_gamepad_changes_, new_state.items[0]));
-      }
+    if (new_state.items[0].connected != state_.items[0].connected ||
+        new_state.items[0].timestamp > state_.items[0].timestamp) {
+      origin_task_runner_->PostTask(
+          FROM_HERE, base::Bind(process_gamepad_changes_, new_state.items[0]));
     }
 
     state_ = new_state;
diff --git a/components/exo/gamepad_unittest.cc b/components/exo/gamepad_unittest.cc
index 34a7e16..560c7e4 100644
--- a/components/exo/gamepad_unittest.cc
+++ b/components/exo/gamepad_unittest.cc
@@ -105,7 +105,6 @@
   // Gamepad connected.
   EXPECT_CALL(delegate, OnStateChange(true)).Times(1);
   blink::WebGamepads gamepad_connected;
-  gamepad_connected.length = 1;
   gamepad_connected.items[0].connected = true;
   gamepad_connected.items[0].timestamp = 1;
   SetDataAndPostToDelegate(gamepad_connected);
@@ -134,7 +133,6 @@
   InitializeGamepad(&delegate);
 
   blink::WebGamepads axis_moved;
-  axis_moved.length = 1;
   axis_moved.items[0].connected = true;
   axis_moved.items[0].timestamp = 1;
   axis_moved.items[0].axesLength = 1;
@@ -164,7 +162,6 @@
   InitializeGamepad(&delegate);
 
   blink::WebGamepads axis_moved;
-  axis_moved.length = 1;
   axis_moved.items[0].connected = true;
   axis_moved.items[0].timestamp = 1;
   axis_moved.items[0].buttonsLength = 1;
diff --git a/components/metrics/metrics_log_manager.cc b/components/metrics/metrics_log_manager.cc
index 387b2001..cc35bbd 100644
--- a/components/metrics/metrics_log_manager.cc
+++ b/components/metrics/metrics_log_manager.cc
@@ -45,7 +45,6 @@
                              new PersistedLogsMetricsImpl()),
                          local_state,
                          prefs::kMetricsInitialLogs,
-                         prefs::kDeprecatedMetricsInitialLogs,
                          kInitialLogsPersistLimit,
                          kStorageByteLimitPerLogType,
                          0),
@@ -53,7 +52,6 @@
                              new PersistedLogsMetricsImpl()),
                          local_state,
                          prefs::kMetricsOngoingLogs,
-                         prefs::kDeprecatedMetricsOngoingLogs,
                          kOngoingLogsPersistLimit,
                          kStorageByteLimitPerLogType,
                          max_ongoing_log_size) {}
diff --git a/components/metrics/metrics_log_manager_unittest.cc b/components/metrics/metrics_log_manager_unittest.cc
index 8040ed0..e30a16c 100644
--- a/components/metrics/metrics_log_manager_unittest.cc
+++ b/components/metrics/metrics_log_manager_unittest.cc
@@ -167,7 +167,7 @@
       PersistedLogs ongoing_logs(std::unique_ptr<PersistedLogsMetricsImpl>(
                                      new PersistedLogsMetricsImpl()),
                                  &pref_service, prefs::kMetricsOngoingLogs,
-                                 prefs::kDeprecatedMetricsOngoingLogs, 1, 1, 0);
+                                 1, 1, 0);
       ongoing_logs.StoreLog(log);
       ongoing_logs.SerializeLogs();
     }
diff --git a/components/metrics/metrics_pref_names.cc b/components/metrics/metrics_pref_names.cc
index 1d5a7b63..c5ad9c6 100644
--- a/components/metrics/metrics_pref_names.cc
+++ b/components/metrics/metrics_pref_names.cc
@@ -7,21 +7,6 @@
 namespace metrics {
 namespace prefs {
 
-// Array of strings that are each UMA logs that were supposed to be sent in the
-// first minute of a browser session. These logs include things like crash count
-// info, etc.
-// Deprecated by kMetricsInitialLogs.
-const char kDeprecatedMetricsInitialLogs[] =
-    "user_experience_metrics.initial_logs_list";
-
-// Array of strings that are each UMA logs that were not sent because the
-// browser terminated before these accumulated metrics could be sent. These
-// logs typically include histograms and memory reports, as well as ongoing
-// user activities.
-// Deprecated by kMetricsOngoingLogs.
-const char kDeprecatedMetricsOngoingLogs[] =
-    "user_experience_metrics.ongoing_logs_list";
-
 // Set once, to the current epoch time, on the first run of chrome on this
 // machine. Attached to metrics reports forever thereafter.
 const char kInstallDate[] = "uninstall_metrics.installation_date2";
diff --git a/components/metrics/persisted_logs.cc b/components/metrics/persisted_logs.cc
index a407a0d..b7aa8f0 100644
--- a/components/metrics/persisted_logs.cc
+++ b/components/metrics/persisted_logs.cc
@@ -27,17 +27,6 @@
 const char kLogTimestampKey[] = "timestamp";
 const char kLogDataKey[] = "data";
 
-// Reads the value at |index| from |list_value| as a string and Base64-decodes
-// it into |result|. Returns true on success.
-bool ReadBase64String(const base::ListValue& list_value,
-                      size_t index,
-                      std::string* result) {
-  std::string base64_result;
-  if (!list_value.GetString(index, &base64_result))
-    return false;
-  return base::Base64Decode(base64_result, result);
-}
-
 std::string EncodeToBase64(const std::string& to_convert) {
   std::string base64_result;
   base::Base64Encode(to_convert, &base64_result);
@@ -71,14 +60,12 @@
 PersistedLogs::PersistedLogs(std::unique_ptr<PersistedLogsMetrics> metrics,
                              PrefService* local_state,
                              const char* pref_name,
-                             const char* outdated_pref_name,
                              size_t min_log_count,
                              size_t min_log_bytes,
                              size_t max_log_size)
     : metrics_(std::move(metrics)),
       local_state_(local_state),
       pref_name_(pref_name),
-      outdated_pref_name_(outdated_pref_name),
       min_log_count_(min_log_count),
       min_log_bytes_(min_log_bytes),
       max_log_size_(max_log_size != 0 ? max_log_size : static_cast<size_t>(-1)),
@@ -93,20 +80,9 @@
 void PersistedLogs::SerializeLogs() const {
   ListPrefUpdate update(local_state_, pref_name_);
   WriteLogsToPrefList(update.Get());
-
-  // After writing all the logs to the new pref remove old outdated pref.
-  // TODO(gayane): Remove when all users are migrated. crbug.com/649440
-  if (local_state_->HasPrefPath(outdated_pref_name_))
-    local_state_->ClearPref(outdated_pref_name_);
 }
 
 PersistedLogs::LogReadStatus PersistedLogs::DeserializeLogs() {
-  // TODO(gayane): Remove the code for reading logs from outdated pref when all
-  // users are migrated. crbug.com/649440
-  if (local_state_->HasPrefPath(outdated_pref_name_)) {
-    return ReadLogsFromOldFormatPrefList(
-        *local_state_->GetList(outdated_pref_name_));
-  }
   return ReadLogsFromPrefList(*local_state_->GetList(pref_name_));
 }
 
@@ -207,29 +183,4 @@
     metrics_->RecordDroppedLogsNum(dropped_logs_num);
 }
 
-PersistedLogs::LogReadStatus PersistedLogs::ReadLogsFromOldFormatPrefList(
-    const base::ListValue& list_value) {
-  if (list_value.empty())
-    return metrics_->RecordLogReadStatus(LIST_EMPTY);
-
-  // For each log, there's two entries in the list (the data and the hash).
-  DCHECK_EQ(0U, list_value.GetSize() % 2);
-  const size_t log_count = list_value.GetSize() / 2;
-
-  // Resize |list_| ahead of time, so that values can be decoded directly into
-  // the elements of the list.
-  DCHECK(list_.empty());
-  list_.resize(log_count);
-
-  for (size_t i = 0; i < log_count; ++i) {
-    if (!ReadBase64String(list_value, i * 2, &list_[i].compressed_log_data) ||
-        !ReadBase64String(list_value, i * 2 + 1, &list_[i].hash)) {
-      list_.clear();
-      return metrics_->RecordLogReadStatus(LOG_STRING_CORRUPTION);
-    }
-  }
-
-  return metrics_->RecordLogReadStatus(RECALL_SUCCESS);
-}
-
 }  // namespace metrics
diff --git a/components/metrics/persisted_logs.h b/components/metrics/persisted_logs.h
index 2af474b..18a80008 100644
--- a/components/metrics/persisted_logs.h
+++ b/components/metrics/persisted_logs.h
@@ -55,7 +55,6 @@
   PersistedLogs(std::unique_ptr<PersistedLogsMetrics> metrics,
                 PrefService* local_state,
                 const char* pref_name,
-                const char* outdated_pref_name,
                 size_t min_log_count,
                 size_t min_log_bytes,
                 size_t max_log_size);
@@ -111,9 +110,6 @@
   // Reads the list from the ListValue.
   LogReadStatus ReadLogsFromPrefList(const base::ListValue& list);
 
-  // Reads the list from the ListValue in the old Log-hash pair format.
-  LogReadStatus ReadLogsFromOldFormatPrefList(const base::ListValue& list);
-
   // An object for recording UMA metrics.
   std::unique_ptr<PersistedLogsMetrics> metrics_;
 
@@ -125,10 +121,6 @@
   // The name of the preference to serialize logs to/from.
   const char* pref_name_;
 
-  // The name of the preference to serialize logs to/from which may contain log
-  // in the old formatting.
-  const char* outdated_pref_name_;
-
   // We will keep at least this |min_log_count_| logs or |min_log_bytes_| bytes
   // of logs, whichever is greater, when writing to disk.  These apply after
   // skipping logs greater than |max_log_size_|.
diff --git a/components/metrics/persisted_logs_unittest.cc b/components/metrics/persisted_logs_unittest.cc
index 179f4d8..e1b0548f 100644
--- a/components/metrics/persisted_logs_unittest.cc
+++ b/components/metrics/persisted_logs_unittest.cc
@@ -23,7 +23,6 @@
 namespace {
 
 const char kTestPrefName[] = "TestPref";
-const char kTestOutdatedPrefName[] = "OutdatedTestPref";
 const size_t kLogCountLimit = 3;
 const size_t kLogByteLimit = 1000;
 
@@ -69,7 +68,6 @@
                              new PersistedLogsMetricsImpl()),
                       service,
                       kTestPrefName,
-                      kTestOutdatedPrefName,
                       kLogCountLimit,
                       min_log_bytes,
                       0) {}
diff --git a/components/ssl_config/ssl_config_service_manager_pref.cc b/components/ssl_config/ssl_config_service_manager_pref.cc
index a870ab12..0dab204 100644
--- a/components/ssl_config/ssl_config_service_manager_pref.cc
+++ b/components/ssl_config/ssl_config_service_manager_pref.cc
@@ -240,7 +240,7 @@
       ssl_config::prefs::kCertRevocationCheckingRequiredLocalAnchors,
       default_config.rev_checking_required_local_anchors);
   registry->RegisterBooleanPref(ssl_config::prefs::kCertEnableSha1LocalAnchors,
-                                default_config.sha1_local_anchors_enabled);
+                                false);
   registry->RegisterStringPref(ssl_config::prefs::kSSLVersionMin,
                                std::string());
   registry->RegisterStringPref(ssl_config::prefs::kSSLVersionMax,
diff --git a/components/ssl_config/ssl_config_service_manager_pref_unittest.cc b/components/ssl_config/ssl_config_service_manager_pref_unittest.cc
index 6758330..9c2e24b8 100644
--- a/components/ssl_config/ssl_config_service_manager_pref_unittest.cc
+++ b/components/ssl_config/ssl_config_service_manager_pref_unittest.cc
@@ -199,3 +199,53 @@
   config_service->GetSSLConfig(&ssl_config);
   EXPECT_EQ(net::SSL_PROTOCOL_VERSION_TLS1_3, ssl_config.version_max);
 }
+
+// Tests that SHA-1 signatures for local trust anchors can be enabled.
+TEST_F(SSLConfigServiceManagerPrefTest, SHA1ForLocalAnchors) {
+  scoped_refptr<TestingPrefStore> local_state_store(new TestingPrefStore());
+
+  TestingPrefServiceSimple local_state;
+  SSLConfigServiceManager::RegisterPrefs(local_state.registry());
+
+  std::unique_ptr<SSLConfigServiceManager> config_manager(
+      SSLConfigServiceManager::CreateDefaultManager(
+          &local_state, base::ThreadTaskRunnerHandle::Get()));
+  ASSERT_TRUE(config_manager);
+  scoped_refptr<SSLConfigService> config_service(config_manager->Get());
+  ASSERT_TRUE(config_service);
+
+  // By default, SHA-1 local trust anchors should be enabled when not
+  // using any pref service.
+  SSLConfig config1;
+  EXPECT_TRUE(config1.sha1_local_anchors_enabled);
+
+  // Using a pref service without any preference set should result in
+  // SHA-1 local trust anchors being disabled.
+  SSLConfig config2;
+  config_service->GetSSLConfig(&config2);
+  EXPECT_FALSE(config2.sha1_local_anchors_enabled);
+
+  // Enabling the local preference should result in SHA-1 local trust anchors
+  // being enabled.
+  local_state.SetUserPref(ssl_config::prefs::kCertEnableSha1LocalAnchors,
+                          new base::FundamentalValue(true));
+  // Pump the message loop to notify the SSLConfigServiceManagerPref that the
+  // preferences changed.
+  base::RunLoop().RunUntilIdle();
+
+  SSLConfig config3;
+  config_service->GetSSLConfig(&config3);
+  EXPECT_TRUE(config3.sha1_local_anchors_enabled);
+
+  // Disabling the local preference should result in SHA-1 local trust
+  // anchors being disabled.
+  local_state.SetUserPref(ssl_config::prefs::kCertEnableSha1LocalAnchors,
+                          new base::FundamentalValue(false));
+  // Pump the message loop to notify the SSLConfigServiceManagerPref that the
+  // preferences changed.
+  base::RunLoop().RunUntilIdle();
+
+  SSLConfig config4;
+  config_service->GetSSLConfig(&config4);
+  EXPECT_FALSE(config4.sha1_local_anchors_enabled);
+}
diff --git a/components/sync/engine_impl/sync_manager_impl.cc b/components/sync/engine_impl/sync_manager_impl.cc
index cbacc993..a237ea97 100644
--- a/components/sync/engine_impl/sync_manager_impl.cc
+++ b/components/sync/engine_impl/sync_manager_impl.cc
@@ -296,10 +296,8 @@
   DVLOG(1) << "Setting invalidator client ID: " << args->invalidator_client_id;
   allstatus_.SetInvalidatorClientId(args->invalidator_client_id);
 
-  // TODO(crbug.com/658002): Pass in the real USS migrator function once initial
-  // GetUpdates issues are addressed.
   model_type_registry_ = base::MakeUnique<ModelTypeRegistry>(
-      args->workers, &share_, this, UssMigrator());
+      args->workers, &share_, this, base::Bind(&MigrateDirectoryData));
   sync_encryption_handler_->AddObserver(model_type_registry_.get());
 
   // Build a SyncCycleContext and store the worker in it.
diff --git a/components/test_runner/BUILD.gn b/components/test_runner/BUILD.gn
index d4e6dcf9..5cc0c62 100644
--- a/components/test_runner/BUILD.gn
+++ b/components/test_runner/BUILD.gn
@@ -116,7 +116,7 @@
     "//gin",
     "//gpu",
     "//gpu/command_buffer/client:gles2_interface",
-    "//media/midi:mojo_cpp_sources",
+    "//media/midi:mojo",
     "//net",
     "//skia",
     "//third_party/WebKit/public:blink",
diff --git a/components/test_runner/gamepad_controller.cc b/components/test_runner/gamepad_controller.cc
index 18c2fa4..a30e221 100644
--- a/components/test_runner/gamepad_controller.cc
+++ b/components/test_runner/gamepad_controller.cc
@@ -178,11 +178,6 @@
   if (index < 0 || index >= static_cast<int>(WebGamepads::itemsLengthCap))
     return;
   gamepads_.items[index].connected = true;
-  gamepads_.length = 0;
-  for (unsigned i = 0; i < WebGamepads::itemsLengthCap; ++i) {
-    if (gamepads_.items[i].connected)
-      gamepads_.length = i + 1;
-  }
 }
 
 void GamepadController::DispatchConnected(int index) {
@@ -199,11 +194,6 @@
     return;
   WebGamepad& pad = gamepads_.items[index];
   pad.connected = false;
-  gamepads_.length = 0;
-  for (unsigned i = 0; i < WebGamepads::itemsLengthCap; ++i) {
-    if (gamepads_.items[i].connected)
-      gamepads_.length = i + 1;
-  }
   if (listener_)
     listener_->didDisconnectGamepad(index, pad);
 }
diff --git a/content/browser/BUILD.gn b/content/browser/BUILD.gn
index f30a568f..564b13e2 100644
--- a/content/browser/BUILD.gn
+++ b/content/browser/BUILD.gn
@@ -93,7 +93,7 @@
     "//media/capture/mojo:image_capture",
     "//media/gpu/ipc/client",
     "//media/midi",
-    "//media/midi:mojo_cpp_sources",
+    "//media/midi:mojo",
     "//media/mojo/interfaces",
     "//mojo/common",
     "//mojo/edk/system",
diff --git a/content/browser/browser_context.cc b/content/browser/browser_context.cc
index 1e7f2c3..b287a6c 100644
--- a/content/browser/browser_context.cc
+++ b/content/browser/browser_context.cc
@@ -239,8 +239,6 @@
   std::string partition_name;
   bool in_memory = false;
 
-  // TODO(ajwong): After GetDefaultStoragePartition() is removed, get rid of
-  // this conditional and require that |site_instance| is non-NULL.
   if (site_instance) {
     GetContentClient()->browser()->GetStoragePartitionConfigForSite(
         browser_context, site_instance->GetSiteURL(), true,
diff --git a/content/browser/dom_storage/dom_storage_context_wrapper.cc b/content/browser/dom_storage/dom_storage_context_wrapper.cc
index 60083d7e..926b4c0fe 100644
--- a/content/browser/dom_storage/dom_storage_context_wrapper.cc
+++ b/content/browser/dom_storage/dom_storage_context_wrapper.cc
@@ -9,6 +9,7 @@
 
 #include "base/bind.h"
 #include "base/bind_helpers.h"
+#include "base/command_line.h"
 #include "base/files/file_path.h"
 #include "base/location.h"
 #include "base/memory/memory_coordinator_client_registry.h"
@@ -30,6 +31,7 @@
 #include "content/public/browser/session_storage_usage_info.h"
 #include "content/public/common/content_client.h"
 #include "content/public/common/content_features.h"
+#include "content/public/common/content_switches.h"
 
 namespace content {
 namespace {
@@ -44,11 +46,12 @@
 }
 
 void GetLocalStorageUsageHelper(
+    std::vector<LocalStorageUsageInfo> mojo_usage,
     base::SingleThreadTaskRunner* reply_task_runner,
     DOMStorageContextImpl* context,
     const DOMStorageContext::GetLocalStorageUsageCallback& callback) {
   std::vector<LocalStorageUsageInfo>* infos =
-      new std::vector<LocalStorageUsageInfo>;
+      new std::vector<LocalStorageUsageInfo>(std::move(mojo_usage));
   context->GetLocalStorageUsage(infos, true);
   reply_task_runner->PostTask(
       FROM_HERE, base::Bind(&InvokeLocalStorageUsageCallbackHelper, callback,
@@ -80,14 +83,17 @@
     const base::FilePath& profile_path,
     const base::FilePath& local_partition_path,
     storage::SpecialStoragePolicy* special_storage_policy) {
-  base::FilePath storage_dir;
-  if (!profile_path.empty())
-    storage_dir = local_partition_path.AppendASCII(kLocalStorageDirectory);
-  // TODO(michaeln): Enable writing to disk when db is versioned,
-  // for now using an empty subdirectory to use an in-memory db.
-  // subdirectory_(subdirectory),
-  mojo_state_.reset(new LocalStorageContextMojo(
-      connector, base::FilePath() /* storage_dir */));
+  if (base::CommandLine::ForCurrentProcess()->HasSwitch(
+          switches::kMojoLocalStorage)) {
+    base::FilePath storage_dir;
+    if (!profile_path.empty())
+      storage_dir = local_partition_path.AppendASCII(kLocalStorageDirectory);
+    // TODO(michaeln): Enable writing to disk when db is versioned,
+    // for now using an empty subdirectory to use an in-memory db.
+    // subdirectory_(subdirectory),
+    mojo_state_.reset(new LocalStorageContextMojo(
+        connector, base::FilePath() /* storage_dir */));
+  }
 
   base::FilePath data_path;
   if (!profile_path.empty())
@@ -141,9 +147,15 @@
 void DOMStorageContextWrapper::GetLocalStorageUsage(
     const GetLocalStorageUsageCallback& callback) {
   DCHECK(context_.get());
+  if (mojo_state_) {
+    mojo_state_->GetStorageUsage(base::BindOnce(
+        &DOMStorageContextWrapper::GotMojoLocalStorageUsage, this, callback));
+    return;
+  }
   context_->task_runner()->PostShutdownBlockingTask(
       FROM_HERE, DOMStorageTaskRunner::PRIMARY_SEQUENCE,
       base::Bind(&GetLocalStorageUsageHelper,
+                 std::vector<LocalStorageUsageInfo>(),
                  base::RetainedRef(base::ThreadTaskRunnerHandle::Get()),
                  base::RetainedRef(context_), callback));
 }
@@ -165,6 +177,8 @@
       FROM_HERE, DOMStorageTaskRunner::PRIMARY_SEQUENCE,
       base::Bind(&DOMStorageContextImpl::DeleteLocalStorageForPhysicalOrigin,
                  context_, origin));
+  if (mojo_state_)
+    mojo_state_->DeleteStorageForPhysicalOrigin(url::Origin(origin));
 }
 
 void DOMStorageContextWrapper::DeleteLocalStorage(const GURL& origin) {
@@ -173,6 +187,8 @@
       FROM_HERE,
       DOMStorageTaskRunner::PRIMARY_SEQUENCE,
       base::Bind(&DOMStorageContextImpl::DeleteLocalStorage, context_, origin));
+  if (mojo_state_)
+    mojo_state_->DeleteStorage(url::Origin(origin));
 }
 
 void DOMStorageContextWrapper::DeleteSessionStorage(
@@ -233,11 +249,15 @@
   context_->task_runner()->PostShutdownBlockingTask(
       FROM_HERE, DOMStorageTaskRunner::PRIMARY_SEQUENCE,
       base::Bind(&DOMStorageContextImpl::Flush, context_));
+  if (mojo_state_)
+    mojo_state_->Flush();
 }
 
 void DOMStorageContextWrapper::OpenLocalStorage(
     const url::Origin& origin,
     mojom::LevelDBWrapperRequest request) {
+  if (!mojo_state_)
+    return;
   mojo_state_->OpenLocalStorage(origin, std::move(request));
 }
 
@@ -281,4 +301,14 @@
       base::Bind(&DOMStorageContextImpl::PurgeMemory, context_, purge_option));
 }
 
+void DOMStorageContextWrapper::GotMojoLocalStorageUsage(
+    GetLocalStorageUsageCallback callback,
+    std::vector<LocalStorageUsageInfo> usage) {
+  context_->task_runner()->PostShutdownBlockingTask(
+      FROM_HERE, DOMStorageTaskRunner::PRIMARY_SEQUENCE,
+      base::Bind(&GetLocalStorageUsageHelper, base::Passed(&usage),
+                 base::RetainedRef(base::ThreadTaskRunnerHandle::Get()),
+                 base::RetainedRef(context_), callback));
+}
+
 }  // namespace content
diff --git a/content/browser/dom_storage/dom_storage_context_wrapper.h b/content/browser/dom_storage/dom_storage_context_wrapper.h
index 2a05e800..14efb451 100644
--- a/content/browser/dom_storage/dom_storage_context_wrapper.h
+++ b/content/browser/dom_storage/dom_storage_context_wrapper.h
@@ -94,6 +94,9 @@
 
   void PurgeMemory(DOMStorageContextImpl::PurgeOption purge_option);
 
+  void GotMojoLocalStorageUsage(GetLocalStorageUsageCallback callback,
+                                std::vector<LocalStorageUsageInfo> usage);
+
   // Keep all mojo-ish details together and not bleed them through the public
   // interface.
   std::unique_ptr<LocalStorageContextMojo> mojo_state_;
diff --git a/content/browser/dom_storage/local_storage_context_mojo.cc b/content/browser/dom_storage/local_storage_context_mojo.cc
index c9939f57..01d0c82 100644
--- a/content/browser/dom_storage/local_storage_context_mojo.cc
+++ b/content/browser/dom_storage/local_storage_context_mojo.cc
@@ -97,6 +97,11 @@
       weak_ptr_factory_.GetWeakPtr(), origin));
 }
 
+void LocalStorageContextMojo::Flush() {
+  for (const auto& it : level_db_wrappers_)
+    it.second->ScheduleImmediateCommit();
+}
+
 void LocalStorageContextMojo::SetDatabaseForTesting(
     leveldb::mojom::LevelDBDatabasePtr database) {
   DCHECK_EQ(connection_state_, NO_CONNECTION);
diff --git a/content/browser/dom_storage/local_storage_context_mojo.h b/content/browser/dom_storage/local_storage_context_mojo.h
index c6fea56a..e9d2f53 100644
--- a/content/browser/dom_storage/local_storage_context_mojo.h
+++ b/content/browser/dom_storage/local_storage_context_mojo.h
@@ -40,6 +40,7 @@
   void DeleteStorage(const url::Origin& origin);
   // Like DeleteStorage(), but also deletes storage for all sub-origins.
   void DeleteStorageForPhysicalOrigin(const url::Origin& origin);
+  void Flush();
 
   void SetDatabaseForTesting(leveldb::mojom::LevelDBDatabasePtr database);
 
diff --git a/content/browser/permissions/permission_service_context.cc b/content/browser/permissions/permission_service_context.cc
index a83267a0..cffb065 100644
--- a/content/browser/permissions/permission_service_context.cc
+++ b/content/browser/permissions/permission_service_context.cc
@@ -7,13 +7,45 @@
 #include <utility>
 
 #include "content/browser/permissions/permission_service_impl.h"
+#include "content/public/browser/browser_context.h"
 #include "content/public/browser/navigation_details.h"
+#include "content/public/browser/permission_manager.h"
 #include "content/public/browser/render_frame_host.h"
 #include "content/public/browser/render_process_host.h"
 #include "content/public/browser/web_contents.h"
 
+using blink::mojom::PermissionObserverPtr;
+
 namespace content {
 
+class PermissionServiceContext::PermissionSubscription {
+ public:
+  PermissionSubscription(PermissionServiceContext* context,
+                         PermissionObserverPtr observer)
+      : context_(context), observer_(std::move(observer)) {
+    observer_.set_connection_error_handler(base::Bind(
+        &PermissionSubscription::OnConnectionError, base::Unretained(this)));
+  }
+
+  ~PermissionSubscription() = default;
+
+  void OnConnectionError() {
+    DCHECK_NE(id_, 0);
+    context_->ObserverHadConnectionError(id_);
+  }
+
+  void OnPermissionStatusChanged(blink::mojom::PermissionStatus status) {
+    observer_->OnPermissionStatusChange(status);
+  }
+
+  void set_id(int id) { id_ = id; }
+
+ private:
+  PermissionServiceContext* context_;
+  PermissionObserverPtr observer_;
+  int id_ = 0;
+};
+
 PermissionServiceContext::PermissionServiceContext(
     RenderFrameHost* render_frame_host)
     : WebContentsObserver(WebContents::FromRenderFrameHost(render_frame_host)),
@@ -33,16 +65,58 @@
 
 void PermissionServiceContext::CreateService(
     mojo::InterfaceRequest<blink::mojom::PermissionService> request) {
-  services_.push_back(new PermissionServiceImpl(this, std::move(request)));
+  services_.push_back(
+      base::MakeUnique<PermissionServiceImpl>(this, std::move(request)));
+}
+
+void PermissionServiceContext::CreateSubscription(
+    PermissionType permission_type,
+    const url::Origin& origin,
+    PermissionObserverPtr observer) {
+  BrowserContext* browser_context = GetBrowserContext();
+  DCHECK(browser_context);
+  if (!browser_context->GetPermissionManager())
+    return;
+
+  auto subscription =
+      base::MakeUnique<PermissionSubscription>(this, std::move(observer));
+  GURL requesting_origin(origin.Serialize());
+  GURL embedding_origin = GetEmbeddingOrigin();
+  int subscription_id =
+      browser_context->GetPermissionManager()->SubscribePermissionStatusChange(
+          permission_type, requesting_origin,
+          // If the embedding_origin is empty, we'll use the |origin| instead.
+          embedding_origin.is_empty() ? requesting_origin : embedding_origin,
+          base::Bind(&PermissionSubscription::OnPermissionStatusChanged,
+                     base::Unretained(subscription.get())));
+  subscription->set_id(subscription_id);
+  subscriptions_[subscription_id] = std::move(subscription);
 }
 
 void PermissionServiceContext::ServiceHadConnectionError(
     PermissionServiceImpl* service) {
-  auto it = std::find(services_.begin(), services_.end(), service);
+  auto it = std::find_if(
+      services_.begin(), services_.end(),
+      [service](const std::unique_ptr<PermissionServiceImpl>& this_service) {
+        return service == this_service.get();
+      });
   DCHECK(it != services_.end());
   services_.erase(it);
 }
 
+void PermissionServiceContext::ObserverHadConnectionError(int subscription_id) {
+  BrowserContext* browser_context = GetBrowserContext();
+  DCHECK(browser_context);
+  if (browser_context->GetPermissionManager()) {
+    browser_context->GetPermissionManager()->UnsubscribePermissionStatusChange(
+        subscription_id);
+  }
+
+  auto it = subscriptions_.find(subscription_id);
+  DCHECK(it != subscriptions_.end());
+  subscriptions_.erase(it);
+}
+
 void PermissionServiceContext::RenderFrameHostChanged(
     RenderFrameHost* old_host,
     RenderFrameHost* new_host) {
@@ -70,7 +144,7 @@
   if (render_frame_host != render_frame_host_)
     return;
 
-  for (auto* service : services_)
+  for (const auto& service : services_)
     service->CancelPendingOperations();
 }
 
diff --git a/content/browser/permissions/permission_service_context.h b/content/browser/permissions/permission_service_context.h
index 3db134e..83274b4 100644
--- a/content/browser/permissions/permission_service_context.h
+++ b/content/browser/permissions/permission_service_context.h
@@ -7,15 +7,21 @@
 
 #include "base/macros.h"
 #include "base/memory/scoped_vector.h"
+#include "content/public/browser/permission_type.h"
 #include "content/public/browser/web_contents_observer.h"
 #include "mojo/public/cpp/bindings/interface_request.h"
 
 namespace blink {
 namespace mojom {
+class PermissionObserver;
 class PermissionService;
 }
 }
 
+namespace url {
+class Origin;
+}
+
 namespace content {
 
 class PermissionServiceImpl;
@@ -36,16 +42,26 @@
   void CreateService(
       mojo::InterfaceRequest<blink::mojom::PermissionService> request);
 
+  void CreateSubscription(
+      PermissionType permission_type,
+      const url::Origin& origin,
+      mojo::InterfacePtr<blink::mojom::PermissionObserver> observer);
+
   // Called by a PermissionServiceImpl identified as |service| when it has a
   // connection error in order to get unregistered and killed.
   void ServiceHadConnectionError(PermissionServiceImpl* service);
 
+  // Called when the connection to a PermissionObserver has an error.
+  void ObserverHadConnectionError(int subscription_id);
+
   BrowserContext* GetBrowserContext() const;
   GURL GetEmbeddingOrigin() const;
 
   RenderFrameHost* render_frame_host() const;
 
  private:
+  class PermissionSubscription;
+
   // WebContentsObserver
   void RenderFrameHostChanged(RenderFrameHost* old_host,
                               RenderFrameHost* new_host) override;
@@ -58,7 +74,9 @@
 
   RenderFrameHost* render_frame_host_;
   RenderProcessHost* render_process_host_;
-  ScopedVector<PermissionServiceImpl> services_;
+  std::vector<std::unique_ptr<PermissionServiceImpl>> services_;
+  std::unordered_map<int, std::unique_ptr<PermissionSubscription>>
+      subscriptions_;
 
   DISALLOW_COPY_AND_ASSIGN(PermissionServiceContext);
 };
diff --git a/content/browser/permissions/permission_service_impl.cc b/content/browser/permissions/permission_service_impl.cc
index 44851b7a..75e8a6cd 100644
--- a/content/browser/permissions/permission_service_impl.cc
+++ b/content/browser/permissions/permission_service_impl.cc
@@ -17,6 +17,7 @@
 
 using blink::mojom::PermissionDescriptorPtr;
 using blink::mojom::PermissionName;
+using blink::mojom::PermissionObserverPtr;
 using blink::mojom::PermissionStatus;
 
 namespace content {
@@ -81,17 +82,6 @@
   callback.Run(result);
 }
 
-PermissionServiceImpl::PendingSubscription::PendingSubscription(
-    PermissionType permission,
-    const url::Origin& origin,
-    const PermissionStatusCallback& callback)
-    : id(-1), permission(permission), origin(origin), callback(callback) {}
-
-PermissionServiceImpl::PendingSubscription::~PendingSubscription() {
-  if (!callback.is_null())
-    callback.Run(PermissionStatus::ASK);
-}
-
 PermissionServiceImpl::PermissionServiceImpl(
     PermissionServiceContext* context,
     mojo::InterfaceRequest<blink::mojom::PermissionService> request)
@@ -226,17 +216,6 @@
         it.GetCurrentValue()->id);
   }
   pending_requests_.Clear();
-
-  // Cancel pending subscriptions.
-  for (SubscriptionsMap::Iterator<PendingSubscription>
-          it(&pending_subscriptions_); !it.IsAtEnd(); it.Advance()) {
-    it.GetCurrentValue()->callback.Run(GetPermissionStatusFromType(
-        it.GetCurrentValue()->permission, it.GetCurrentValue()->origin));
-    it.GetCurrentValue()->callback.Reset();
-    permission_manager->UnsubscribePermissionStatusChange(
-        it.GetCurrentValue()->id);
-  }
-  pending_subscriptions_.Clear();
 }
 
 void PermissionServiceImpl::HasPermission(
@@ -267,45 +246,24 @@
   callback.Run(GetPermissionStatusFromType(permission_type, origin));
 }
 
-void PermissionServiceImpl::GetNextPermissionChange(
+void PermissionServiceImpl::AddPermissionObserver(
     PermissionDescriptorPtr permission,
     const url::Origin& origin,
     PermissionStatus last_known_status,
-    const PermissionStatusCallback& callback) {
+    PermissionObserverPtr observer) {
   PermissionStatus current_status = GetPermissionStatus(permission, origin);
   if (current_status != last_known_status) {
-    callback.Run(current_status);
-    return;
+    observer->OnPermissionStatusChange(current_status);
+    last_known_status = current_status;
   }
 
   BrowserContext* browser_context = context_->GetBrowserContext();
   DCHECK(browser_context);
-  if (!browser_context->GetPermissionManager()) {
-    callback.Run(current_status);
+  if (!browser_context->GetPermissionManager())
     return;
-  }
 
-  PermissionType permission_type =
-      PermissionDescriptorToPermissionType(permission);
-
-  // We need to pass the id of PendingSubscription in pending_subscriptions_
-  // to the callback but SubscribePermissionStatusChange() will also return an
-  // id which is different.
-  auto subscription =
-      base::MakeUnique<PendingSubscription>(permission_type, origin, callback);
-  PendingSubscription* subscription_raw = subscription.get();
-  int pending_subscription_id =
-      pending_subscriptions_.Add(std::move(subscription));
-
-  GURL requesting_origin(origin.Serialize());
-  GURL embedding_origin = context_->GetEmbeddingOrigin();
-  subscription_raw->id =
-      browser_context->GetPermissionManager()->SubscribePermissionStatusChange(
-          permission_type, requesting_origin,
-          // If the embedding_origin is empty, we,ll use the |origin| instead.
-          embedding_origin.is_empty() ? requesting_origin : embedding_origin,
-          base::Bind(&PermissionServiceImpl::OnPermissionStatusChanged,
-                     weak_factory_.GetWeakPtr(), pending_subscription_id));
+  context_->CreateSubscription(PermissionDescriptorToPermissionType(permission),
+                               origin, std::move(observer));
 }
 
 PermissionStatus PermissionServiceImpl::GetPermissionStatus(
@@ -346,25 +304,4 @@
       embedding_origin.is_empty() ? requesting_origin : embedding_origin);
 }
 
-void PermissionServiceImpl::OnPermissionStatusChanged(
-    int pending_subscription_id,
-    PermissionStatus status) {
-  PendingSubscription* subscription =
-      pending_subscriptions_.Lookup(pending_subscription_id);
-
-  BrowserContext* browser_context = context_->GetBrowserContext();
-  DCHECK(browser_context);
-  if (browser_context->GetPermissionManager()) {
-    browser_context->GetPermissionManager()->UnsubscribePermissionStatusChange(
-        subscription->id);
-  }
-
-  PermissionStatusCallback callback = subscription->callback;
-
-  subscription->callback.Reset();
-  pending_subscriptions_.Remove(pending_subscription_id);
-
-  callback.Run(status);
-}
-
 }  // namespace content
diff --git a/content/browser/permissions/permission_service_impl.h b/content/browser/permissions/permission_service_impl.h
index 6cc2872..7617697a 100644
--- a/content/browser/permissions/permission_service_impl.h
+++ b/content/browser/permissions/permission_service_impl.h
@@ -27,6 +27,9 @@
 // WebContents for example.
 class PermissionServiceImpl : public blink::mojom::PermissionService {
  public:
+  PermissionServiceImpl(
+      PermissionServiceContext* context,
+      mojo::InterfaceRequest<blink::mojom::PermissionService> request);
   ~PermissionServiceImpl() override;
 
   // Clear pending operations currently run by the service. This will be called
@@ -34,13 +37,6 @@
   // state for example, if the frame changes.
   void CancelPendingOperations();
 
- protected:
-  friend PermissionServiceContext;
-
-  PermissionServiceImpl(
-      PermissionServiceContext* context,
-      mojo::InterfaceRequest<blink::mojom::PermissionService> request);
-
  private:
   using PermissionStatusCallback =
       base::Callback<void(blink::mojom::PermissionStatus)>;
@@ -57,20 +53,6 @@
   };
   using RequestsMap = IDMap<std::unique_ptr<PendingRequest>>;
 
-  struct PendingSubscription {
-    PendingSubscription(PermissionType permission,
-                        const url::Origin& origin,
-                        const PermissionStatusCallback& callback);
-    ~PendingSubscription();
-
-    // Subscription ID received from the PermissionManager.
-    int id;
-    PermissionType permission;
-    url::Origin origin;
-    PermissionStatusCallback callback;
-  };
-  using SubscriptionsMap = IDMap<std::unique_ptr<PendingSubscription>>;
-
   // blink::mojom::PermissionService.
   void HasPermission(blink::mojom::PermissionDescriptorPtr permission,
                      const url::Origin& origin,
@@ -87,11 +69,11 @@
   void RevokePermission(blink::mojom::PermissionDescriptorPtr permission,
                         const url::Origin& origin,
                         const PermissionStatusCallback& callback) override;
-  void GetNextPermissionChange(
+  void AddPermissionObserver(
       blink::mojom::PermissionDescriptorPtr permission,
       const url::Origin& origin,
       blink::mojom::PermissionStatus last_known_status,
-      const PermissionStatusCallback& callback) override;
+      blink::mojom::PermissionObserverPtr observer) override;
 
   void OnConnectionError();
 
@@ -109,11 +91,7 @@
       const url::Origin& origin);
   void ResetPermissionStatus(PermissionType type, const url::Origin& origin);
 
-  void OnPermissionStatusChanged(int pending_subscription_id,
-                                 blink::mojom::PermissionStatus status);
-
   RequestsMap pending_requests_;
-  SubscriptionsMap pending_subscriptions_;
   // context_ owns |this|.
   PermissionServiceContext* context_;
   mojo::Binding<blink::mojom::PermissionService> binding_;
diff --git a/content/browser/renderer_host/pepper/pepper_gamepad_host_unittest.cc b/content/browser/renderer_host/pepper/pepper_gamepad_host_unittest.cc
index 4075847..cb42e44c 100644
--- a/content/browser/renderer_host/pepper/pepper_gamepad_host_unittest.cc
+++ b/content/browser/renderer_host/pepper/pepper_gamepad_host_unittest.cc
@@ -76,8 +76,6 @@
                 "gamepads data must match");
   ppapi::WebKitGamepads ppapi_gamepads;
   blink::WebGamepads web_gamepads;
-  EXPECT_EQ(AddressDiff(&web_gamepads.length, &web_gamepads),
-            AddressDiff(&ppapi_gamepads.length, &ppapi_gamepads));
 
   // See comment below on storage & the EXPECT macro.
   size_t webkit_items_length_cap = blink::WebGamepads::itemsLengthCap;
@@ -137,7 +135,6 @@
 TEST_F(PepperGamepadHostTest, MAYBE_WaitForReply) {
   blink::WebGamepads default_data;
   memset(&default_data, 0, sizeof(blink::WebGamepads));
-  default_data.length = 1;
   default_data.items[0].connected = true;
   default_data.items[0].buttonsLength = 1;
   ConstructService(default_data);
@@ -185,7 +182,6 @@
   const ppapi::ContentGamepadHardwareBuffer* buffer =
       static_cast<const ppapi::ContentGamepadHardwareBuffer*>(
           shared_memory.memory());
-  EXPECT_EQ(button_down_data.length, buffer->buffer.length);
   EXPECT_EQ(button_down_data.items[0].buttonsLength,
             buffer->buffer.items[0].buttons_length);
   for (size_t i = 0; i < ppapi::WebKitGamepad::kButtonsLengthCap; i++) {
diff --git a/content/common/BUILD.gn b/content/common/BUILD.gn
index c9bb374..6b78477 100644
--- a/content/common/BUILD.gn
+++ b/content/common/BUILD.gn
@@ -349,9 +349,6 @@
     "//third_party/WebKit/public:blink_headers",
   ]
   deps = [
-    # This looks needless as we have :mojo_bindings in public_deps, but it's
-    # needed because of allow_circular_includes_from.
-    ":mojo_bindings_cpp_sources",
     "//base",
     "//base/third_party/dynamic_annotations",
     "//build/util:webkit_version",
@@ -384,7 +381,7 @@
     "//media/gpu/ipc/client",
     "//media/gpu/ipc/common",
     "//media/midi",
-    "//media/midi:mojo_cpp_sources",
+    "//media/midi:mojo",
     "//mojo/common:common_base",
     "//mojo/edk/system",
     "//net",
@@ -427,7 +424,7 @@
   libs = []
   ldflags = []
 
-  allow_circular_includes_from = [ ":mojo_bindings_cpp_sources" ]
+  allow_circular_includes_from = [ ":mojo_bindings" ]
 
   if (is_android && use_seccomp_bpf) {
     set_sources_assignment_filter([])
diff --git a/content/public/common/BUILD.gn b/content/public/common/BUILD.gn
index dc256d5..7e7f0eb 100644
--- a/content/public/common/BUILD.gn
+++ b/content/public/common/BUILD.gn
@@ -254,7 +254,7 @@
   deps = [
     # This looks needless as we have //content/common in public_deps, but it's
     # needed because of allow_circular_includes_from.
-    "//content/common:mojo_bindings_cpp_sources",
+    "//content/common:mojo_bindings",
     "//ipc",
     "//media",
     "//mojo/common",
@@ -281,7 +281,7 @@
   # //content/common needs to include public headers.
   allow_circular_includes_from = [
     "//content/common",
-    "//content/common:mojo_bindings_cpp_sources",
+    "//content/common:mojo_bindings",
   ]
 
   if (!enable_plugins) {
diff --git a/content/renderer/BUILD.gn b/content/renderer/BUILD.gn
index 1698254..3a28c2d 100644
--- a/content/renderer/BUILD.gn
+++ b/content/renderer/BUILD.gn
@@ -431,7 +431,7 @@
     "//media/gpu/ipc/client",
     "//media/gpu/ipc/common",
     "//media/midi",
-    "//media/midi:mojo_cpp_sources",
+    "//media/midi:mojo",
     "//media/mojo/interfaces:remoting",
     "//mojo/common",
     "//mojo/edk/js",
diff --git a/content/renderer/input/main_thread_event_queue.cc b/content/renderer/input/main_thread_event_queue.cc
index fe67fb24..b5fac300 100644
--- a/content/renderer/input/main_thread_event_queue.cc
+++ b/content/renderer/input/main_thread_event_queue.cc
@@ -18,13 +18,8 @@
   switch (event->event().type) {
     case blink::WebInputEvent::MouseMove:
     case blink::WebInputEvent::MouseWheel:
-      return true;
     case blink::WebInputEvent::TouchMove:
-      // TouchMoves that are blocking end up blocking scroll. Do not treat
-      // them as continuous events otherwise we will end up waiting up to an
-      // additional frame.
-      return static_cast<const blink::WebTouchEvent&>(event->event())
-                 .dispatchType != blink::WebInputEvent::Blocking;
+      return true;
     default:
       return false;
   }
@@ -125,11 +120,6 @@
       }
     }
 
-    // If handling rAF aligned touch input ACK non-cancelable events right
-    // away.
-    if (!non_blocking && IsRafAlignedEvent(*touch_event))
-      non_blocking = true;
-
     if (enable_non_blocking_due_to_main_thread_responsiveness_flag_ &&
         touch_event->dispatchType == blink::WebInputEvent::Blocking) {
       bool passive_due_to_unresponsive_main =
@@ -140,7 +130,12 @@
         non_blocking = true;
       }
     }
+    // If the event is non-cancelable ACK it right away.
+    if (!non_blocking &&
+        touch_event->dispatchType != blink::WebInputEvent::Blocking)
+      non_blocking = true;
   }
+
   if (is_wheel && non_blocking) {
     // Adjust the |dispatchType| on the event since the compositor
     // determined all event listeners are passive.
@@ -340,12 +335,7 @@
     case blink::WebInputEvent::MouseWheel:
       return handle_raf_aligned_mouse_input_;
     case blink::WebInputEvent::TouchMove:
-      // TouchMoves that are blocking end up blocking scroll. Do not treat
-      // them as continuous events otherwise we will end up waiting up to an
-      // additional frame.
-      return static_cast<const blink::WebTouchEvent&>(event).dispatchType !=
-                 blink::WebInputEvent::Blocking &&
-             handle_raf_aligned_touch_input_;
+      return handle_raf_aligned_touch_input_;
     default:
       return false;
   }
diff --git a/content/renderer/input/main_thread_event_queue_unittest.cc b/content/renderer/input/main_thread_event_queue_unittest.cc
index b05d18f..b2585db 100644
--- a/content/renderer/input/main_thread_event_queue_unittest.cc
+++ b/content/renderer/input/main_thread_event_queue_unittest.cc
@@ -266,7 +266,7 @@
   HandleEvent(kEvents[3], INPUT_EVENT_ACK_STATE_SET_NON_BLOCKING);
   EXPECT_EQ(1u, event_queue().size());
   RunPendingTasksWithSimulatedRaf();
-  histogram_tester.ExpectUniqueSample(kCoalescedCountHistogram, 2, 1);
+  histogram_tester.ExpectUniqueSample(kCoalescedCountHistogram, 2, 2);
 }
 
 TEST_P(MainThreadEventQueueTest, InterleavedEvents) {
@@ -427,7 +427,7 @@
 
   EXPECT_EQ(3u, event_queue().size());
   EXPECT_TRUE(main_task_runner_->HasPendingTask());
-  EXPECT_FALSE(needs_main_frame_);
+  EXPECT_TRUE(needs_main_frame_);
   main_task_runner_->RunUntilIdle();
 }
 
@@ -455,7 +455,7 @@
   EXPECT_TRUE(needs_main_frame_);
   HandleEvent(kEvents[1], INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
   EXPECT_EQ(1u, event_queue().size());
-  EXPECT_TRUE(main_task_runner_->HasPendingTask());
+  EXPECT_FALSE(main_task_runner_->HasPendingTask());
   EXPECT_TRUE(needs_main_frame_);
   RunPendingTasksWithSimulatedRaf();
   EXPECT_EQ(0u, event_queue().size());
diff --git a/content/test/BUILD.gn b/content/test/BUILD.gn
index d954d36..a24f66fe 100644
--- a/content/test/BUILD.gn
+++ b/content/test/BUILD.gn
@@ -1435,7 +1435,7 @@
     "//media/capture",
     "//media/capture/mojo:capture_types",
     "//media/midi:midi",
-    "//media/midi:mojo_cpp_sources",
+    "//media/midi:mojo",
     "//mojo/edk/test:test_support",
     "//mojo/public/cpp/bindings",
     "//net:extras",
diff --git a/device/gamepad/gamepad_provider.cc b/device/gamepad/gamepad_provider.cc
index 470bf45..79829184 100644
--- a/device/gamepad/gamepad_provider.cc
+++ b/device/gamepad/gamepad_provider.cc
@@ -265,13 +265,10 @@
     // Acquire the SeqLock. There is only ever one writer to this data.
     // See gamepad_shared_buffer.h.
     gamepad_shared_buffer_->WriteBegin();
-    buffer->length = 0;
     for (unsigned i = 0; i < WebGamepads::itemsLengthCap; ++i) {
       PadState& state = pad_states_.get()[i];
       // Must run through the map+sanitize here or CheckForUserGesture may fail.
       MapAndSanitizeGamepadData(&state, &buffer->items[i], sanitize_);
-      if (state.active_state)
-        buffer->length++;
     }
     gamepad_shared_buffer_->WriteEnd();
   }
diff --git a/device/gamepad/gamepad_provider_unittest.cc b/device/gamepad/gamepad_provider_unittest.cc
index 03b12638..e534d04 100644
--- a/device/gamepad/gamepad_provider_unittest.cc
+++ b/device/gamepad/gamepad_provider_unittest.cc
@@ -80,7 +80,6 @@
 TEST_F(GamepadProviderTest, MAYBE_PollingAccess) {
   WebGamepads test_data;
   memset(&test_data, 0, sizeof(WebGamepads));
-  test_data.length = 1;
   test_data.items[0].connected = true;
   test_data.items[0].timestamp = 0;
   test_data.items[0].buttonsLength = 1;
@@ -110,7 +109,6 @@
   WebGamepads output;
   ReadGamepadHardwareBuffer(buffer, &output);
 
-  EXPECT_EQ(1u, output.length);
   EXPECT_EQ(1u, output.items[0].buttonsLength);
   EXPECT_EQ(1.f, output.items[0].buttons[0].value);
   EXPECT_EQ(true, output.items[0].buttons[0].pressed);
@@ -119,10 +117,73 @@
   EXPECT_EQ(0.5f, output.items[0].axes[1]);
 }
 
+// http://crbug.com/106163, crbug.com/147549
+#if defined(OS_ANDROID)
+#define MAYBE_ConnectDisconnectMultiple DISABLED_ConnectDisconnectMultiple
+#else
+#define MAYBE_ConnectDisconnectMultiple ConnectDisconnectMultiple
+#endif
+TEST_F(GamepadProviderTest, MAYBE_ConnectDisconnectMultiple) {
+  WebGamepads test_data;
+  test_data.items[0].connected = true;
+  test_data.items[0].timestamp = 0;
+  test_data.items[0].axesLength = 2;
+  test_data.items[0].axes[0] = -1.f;
+  test_data.items[0].axes[1] = .5f;
+
+  test_data.items[1].connected = true;
+  test_data.items[1].timestamp = 0;
+  test_data.items[1].axesLength = 2;
+  test_data.items[1].axes[0] = 1.f;
+  test_data.items[1].axes[1] = -.5f;
+
+  WebGamepads test_data_onedisconnected;
+  test_data_onedisconnected.items[1].connected = true;
+  test_data_onedisconnected.items[1].timestamp = 0;
+  test_data_onedisconnected.items[1].axesLength = 2;
+  test_data_onedisconnected.items[1].axes[0] = 1.f;
+  test_data_onedisconnected.items[1].axes[1] = -.5f;
+
+  GamepadProvider* provider = CreateProvider(test_data);
+  provider->SetSanitizationEnabled(false);
+  provider->Resume();
+
+  base::RunLoop().RunUntilIdle();
+
+  mock_data_fetcher_->WaitForDataRead();
+
+  // Renderer-side, pull data out of poll buffer.
+  base::SharedMemoryHandle handle = provider->GetSharedMemoryHandleForProcess(
+      base::GetCurrentProcessHandle());
+  std::unique_ptr<base::SharedMemory> shared_memory(
+      new base::SharedMemory(handle, true));
+  EXPECT_TRUE(shared_memory->Map(sizeof(GamepadHardwareBuffer)));
+
+  GamepadHardwareBuffer* buffer =
+      static_cast<GamepadHardwareBuffer*>(shared_memory->memory());
+  WebGamepads output;
+  ReadGamepadHardwareBuffer(buffer, &output);
+
+  EXPECT_EQ(2u, output.items[0].axesLength);
+  EXPECT_EQ(-1.f, output.items[0].axes[0]);
+  EXPECT_EQ(0.5f, output.items[0].axes[1]);
+  EXPECT_EQ(2u, output.items[1].axesLength);
+  EXPECT_EQ(1.f, output.items[1].axes[0]);
+  EXPECT_EQ(-0.5f, output.items[1].axes[1]);
+
+  mock_data_fetcher_->SetTestData(test_data_onedisconnected);
+  mock_data_fetcher_->WaitForDataReadAndCallbacksIssued();
+  ReadGamepadHardwareBuffer(buffer, &output);
+
+  EXPECT_EQ(0u, output.items[0].axesLength);
+  EXPECT_EQ(2u, output.items[1].axesLength);
+  EXPECT_EQ(1.f, output.items[1].axes[0]);
+  EXPECT_EQ(-0.5f, output.items[1].axes[1]);
+}
+
 // Tests that waiting for a user gesture works properly.
 TEST_F(GamepadProviderTest, UserGesture) {
   WebGamepads no_button_data;
-  no_button_data.length = 1;
   no_button_data.items[0].connected = true;
   no_button_data.items[0].timestamp = 0;
   no_button_data.items[0].buttonsLength = 1;
@@ -170,7 +231,6 @@
 // Tests that waiting for a user gesture works properly.
 TEST_F(GamepadProviderTest, MAYBE_Sanitization) {
   WebGamepads active_data;
-  active_data.length = 1;
   active_data.items[0].connected = true;
   active_data.items[0].timestamp = 0;
   active_data.items[0].buttonsLength = 1;
@@ -180,7 +240,6 @@
   active_data.items[0].axes[0] = -1.f;
 
   WebGamepads zero_data;
-  zero_data.length = 1;
   zero_data.items[0].connected = true;
   zero_data.items[0].timestamp = 0;
   zero_data.items[0].buttonsLength = 1;
@@ -212,7 +271,6 @@
 
   // Initial data should all be zeroed out due to sanitization, even though the
   // gamepad reported input
-  EXPECT_EQ(1u, output.length);
   EXPECT_EQ(1u, output.items[0].buttonsLength);
   EXPECT_EQ(0.f, output.items[0].buttons[0].value);
   EXPECT_FALSE(output.items[0].buttons[0].pressed);
@@ -227,7 +285,6 @@
   ReadGamepadHardwareBuffer(buffer, &output);
 
   // Should still read zero, which is now an accurate reflection of the data
-  EXPECT_EQ(1u, output.length);
   EXPECT_EQ(1u, output.items[0].buttonsLength);
   EXPECT_EQ(0.f, output.items[0].buttons[0].value);
   EXPECT_FALSE(output.items[0].buttons[0].pressed);
@@ -242,7 +299,6 @@
   ReadGamepadHardwareBuffer(buffer, &output);
 
   // Should now accurately reflect the reported data.
-  EXPECT_EQ(1u, output.length);
   EXPECT_EQ(1u, output.items[0].buttonsLength);
   EXPECT_EQ(1.f, output.items[0].buttons[0].value);
   EXPECT_TRUE(output.items[0].buttons[0].pressed);
diff --git a/device/serial/serial_device_enumerator_mac.cc b/device/serial/serial_device_enumerator_mac.cc
index 4bf3e5ed..3d82328 100644
--- a/device/serial/serial_device_enumerator_mac.cc
+++ b/device/serial/serial_device_enumerator_mac.cc
@@ -10,6 +10,7 @@
 
 #include <algorithm>
 #include <memory>
+#include <unordered_set>
 #include <utility>
 
 #include "base/files/file_enumerator.h"
@@ -214,30 +215,27 @@
 SerialDeviceEnumeratorMac::~SerialDeviceEnumeratorMac() {}
 
 mojo::Array<serial::DeviceInfoPtr> SerialDeviceEnumeratorMac::GetDevices() {
-  mojo::Array<serial::DeviceInfoPtr> newDevices = GetDevicesNew();
-  mojo::Array<serial::DeviceInfoPtr> oldDevices = GetDevicesOld();
+  mojo::Array<serial::DeviceInfoPtr> devices = GetDevicesNew();
+  mojo::Array<serial::DeviceInfoPtr> old_devices = GetDevicesOld();
 
   UMA_HISTOGRAM_SPARSE_SLOWLY(
       "Hardware.Serial.NewMinusOldDeviceListSize",
-      Clamp(newDevices.size() - oldDevices.size(), -10, 10));
+      Clamp(devices.size() - old_devices.size(), -10, 10));
 
   // Add devices found from both the new and old methods of enumeration. If a
   // device is found using both the new and the old enumeration method, then we
   // take the device from the new enumeration method because it's able to
   // collect more information. We do this by inserting the new devices first,
   // because insertions are ignored if the key already exists.
-  mojo::Map<mojo::String, serial::DeviceInfoPtr> deviceMap;
-  for (unsigned long i = 0; i < newDevices.size(); i++) {
-    deviceMap.insert(newDevices[i]->path, newDevices[i].Clone());
+  std::unordered_set<std::string> devices_seen;
+  for (const auto& device : devices) {
+    bool inserted = devices_seen.insert(device->path).second;
+    DCHECK(inserted);
   }
-  for (unsigned long i = 0; i < oldDevices.size(); i++) {
-    deviceMap.insert(oldDevices[i]->path, oldDevices[i].Clone());
+  for (auto& device : old_devices) {
+    if (devices_seen.insert(device->path).second)
+      devices.push_back(std::move(device));
   }
-
-  mojo::Array<mojo::String> paths;
-  mojo::Array<serial::DeviceInfoPtr> devices;
-  deviceMap.DecomposeMapTo(&paths, &devices);
-
   return devices;
 }
 
diff --git a/device/serial/serial_device_enumerator_win.cc b/device/serial/serial_device_enumerator_win.cc
index 149331d..aee8e36 100644
--- a/device/serial/serial_device_enumerator_win.cc
+++ b/device/serial/serial_device_enumerator_win.cc
@@ -10,6 +10,7 @@
 #include <windows.h>
 
 #include <memory>
+#include <unordered_set>
 
 #include "base/metrics/histogram_macros.h"
 #include "base/strings/string_number_conversions.h"
@@ -164,30 +165,27 @@
 SerialDeviceEnumeratorWin::~SerialDeviceEnumeratorWin() {}
 
 mojo::Array<serial::DeviceInfoPtr> SerialDeviceEnumeratorWin::GetDevices() {
-  mojo::Array<serial::DeviceInfoPtr> newDevices = GetDevicesNew();
-  mojo::Array<serial::DeviceInfoPtr> oldDevices = GetDevicesOld();
+  mojo::Array<serial::DeviceInfoPtr> devices = GetDevicesNew();
+  mojo::Array<serial::DeviceInfoPtr> old_devices = GetDevicesOld();
 
   UMA_HISTOGRAM_SPARSE_SLOWLY(
       "Hardware.Serial.NewMinusOldDeviceListSize",
-      Clamp((int)newDevices.size() - (int)oldDevices.size(), -10, 10));
+      Clamp(devices.size() - old_devices.size(), -10, 10));
 
   // Add devices found from both the new and old methods of enumeration. If a
   // device is found using both the new and the old enumeration method, then we
   // take the device from the new enumeration method because it's able to
   // collect more information. We do this by inserting the new devices first,
   // because insertions are ignored if the key already exists.
-  mojo::Map<mojo::String, serial::DeviceInfoPtr> deviceMap;
-  for (unsigned long i = 0; i < newDevices.size(); i++) {
-    deviceMap.insert(newDevices[i]->path, newDevices[i].Clone());
+  std::unordered_set<std::string> devices_seen;
+  for (const auto& device : devices) {
+    bool inserted = devices_seen.insert(device->path).second;
+    DCHECK(inserted);
   }
-  for (unsigned long i = 0; i < oldDevices.size(); i++) {
-    deviceMap.insert(oldDevices[i]->path, oldDevices[i].Clone());
+  for (auto& device : old_devices) {
+    if (devices_seen.insert(device->path).second)
+      devices.push_back(std::move(device));
   }
-
-  mojo::Array<mojo::String> paths;
-  mojo::Array<serial::DeviceInfoPtr> devices;
-  deviceMap.DecomposeMapTo(&paths, &devices);
-
   return devices;
 }
 
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_unittest_framebuffers.cc b/gpu/command_buffer/service/gles2_cmd_decoder_unittest_framebuffers.cc
index 40ca073a..3600c198 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder_unittest_framebuffers.cc
+++ b/gpu/command_buffer/service/gles2_cmd_decoder_unittest_framebuffers.cc
@@ -3397,7 +3397,8 @@
   // EXPECT_EQ can't be used to compare function pointers
   EXPECT_TRUE(
       gl::MockGLInterface::GetGLProcAddress("glInvalidateFramebuffer") !=
-      gl::g_driver_gl.fn.glDiscardFramebufferEXTFn);
+      reinterpret_cast<gl::GLFunctionPointerType>(
+          gl::g_driver_gl.fn.glDiscardFramebufferEXTFn));
   EXPECT_TRUE(
       gl::MockGLInterface::GetGLProcAddress("glInvalidateFramebuffer") !=
       gl::MockGLInterface::GetGLProcAddress("glDiscardFramebufferEXT"));
@@ -3447,7 +3448,8 @@
   // EXPECT_EQ can't be used to compare function pointers
   EXPECT_TRUE(
       gl::MockGLInterface::GetGLProcAddress("glDiscardFramebufferEXT") ==
-      gl::g_driver_gl.fn.glDiscardFramebufferEXTFn);
+      reinterpret_cast<gl::GLFunctionPointerType>(
+          gl::g_driver_gl.fn.glDiscardFramebufferEXTFn));
 
   const GLenum target = GL_FRAMEBUFFER;
   const GLsizei count = 1;
@@ -3489,7 +3491,8 @@
   // EXPECT_EQ can't be used to compare function pointers.
   EXPECT_TRUE(
       gl::MockGLInterface::GetGLProcAddress("glDiscardFramebufferEXT") ==
-      gl::g_driver_gl.fn.glDiscardFramebufferEXTFn);
+      reinterpret_cast<gl::GLFunctionPointerType>(
+          gl::g_driver_gl.fn.glDiscardFramebufferEXTFn));
 
   const GLenum target = GL_FRAMEBUFFER;
   const GLsizei count = 1;
@@ -3712,7 +3715,8 @@
   // and the framebuffer as incomplete.
   EXPECT_TRUE(
       gl::MockGLInterface::GetGLProcAddress("glDiscardFramebufferEXT") ==
-      gl::g_driver_gl.fn.glDiscardFramebufferEXTFn);
+      reinterpret_cast<gl::GLFunctionPointerType>(
+          gl::g_driver_gl.fn.glDiscardFramebufferEXTFn));
 
   const GLenum target = GL_FRAMEBUFFER;
   const GLsizei count = 1;
diff --git a/media/base/pipeline_impl.cc b/media/base/pipeline_impl.cc
index 85d470a..85b54ec 100644
--- a/media/base/pipeline_impl.cc
+++ b/media/base/pipeline_impl.cc
@@ -958,6 +958,7 @@
   client_ = client;
   seek_cb_ = seek_cb;
   last_media_time_ = base::TimeDelta();
+  seek_time_ = kNoTimestamp;
 
   std::unique_ptr<TextRenderer> text_renderer;
   if (base::CommandLine::ForCurrentProcess()->HasSwitch(
@@ -1039,6 +1040,7 @@
 
   DCHECK(seek_cb_.is_null());
   seek_cb_ = seek_cb;
+  seek_time_ = time;
   last_media_time_ = base::TimeDelta();
   media_task_runner_->PostTask(
       FROM_HERE, base::Bind(&RendererWrapper::Seek,
@@ -1069,6 +1071,7 @@
   DCHECK(IsRunning());
   DCHECK(seek_cb_.is_null());
   seek_cb_ = seek_cb;
+  seek_time_ = time;
   last_media_time_ = base::TimeDelta();
 
   media_task_runner_->PostTask(
@@ -1123,6 +1126,14 @@
 base::TimeDelta PipelineImpl::GetMediaTime() const {
   DCHECK(thread_checker_.CalledOnValidThread());
 
+  // Don't trust renderer time during a pending seek. Renderer may return
+  // pre-seek time which may corrupt |last_media_time_| used for clamping.
+  if (seek_time_ != kNoTimestamp) {
+    DVLOG(3) << __func__ << ": (seeking) " << seek_time_.InMilliseconds()
+             << " ms";
+    return seek_time_;
+  }
+
   base::TimeDelta media_time = renderer_wrapper_->GetMediaTime();
 
   // Clamp current media time to the last reported value, this prevents higher
@@ -1301,6 +1312,8 @@
   DCHECK(thread_checker_.CalledOnValidThread());
   DCHECK(IsRunning());
 
+  seek_time_ = kNoTimestamp;
+
   DCHECK(!seek_cb_.is_null());
   base::ResetAndReturn(&seek_cb_).Run(PIPELINE_OK);
 }
diff --git a/media/base/pipeline_impl.h b/media/base/pipeline_impl.h
index 624e3e7..0e6d929 100644
--- a/media/base/pipeline_impl.h
+++ b/media/base/pipeline_impl.h
@@ -173,6 +173,11 @@
   // reported to JavaScript from going backwards in time.
   mutable base::TimeDelta last_media_time_;
 
+  // Set by Seek(), used in place of asking the renderer for current media time
+  // while a seek is pending. Renderer's time cannot be trusted until the seek
+  // has completed.
+  base::TimeDelta seek_time_;
+
   base::ThreadChecker thread_checker_;
   base::WeakPtrFactory<PipelineImpl> weak_factory_;
 
diff --git a/media/base/pipeline_impl_unittest.cc b/media/base/pipeline_impl_unittest.cc
index 424450ea..7f0be87 100644
--- a/media/base/pipeline_impl_unittest.cc
+++ b/media/base/pipeline_impl_unittest.cc
@@ -824,6 +824,43 @@
   EXPECT_EQ(kMediaTime, pipeline_->GetMediaTime());
 }
 
+// Seeking posts a task from main thread to media thread to seek the renderer,
+// resetting its internal clock. Calling GetMediaTime() should be safe even
+// when the renderer has not performed the seek (simulated by its continuing
+// to return the pre-seek time). Verifies fix for http://crbug.com/675556
+TEST_F(PipelineImplTest, GetMediaTimeAfterSeek) {
+  CreateAudioStream();
+  MockDemuxerStreamVector streams;
+  streams.push_back(audio_stream());
+  SetDemuxerExpectations(&streams);
+  SetRendererExpectations();
+  StartPipelineAndExpect(PIPELINE_OK);
+
+  // Pipeline should report the same media time returned by the renderer.
+  base::TimeDelta kMediaTime = base::TimeDelta::FromSeconds(2);
+  EXPECT_CALL(*renderer_, GetMediaTime()).WillRepeatedly(Return(kMediaTime));
+  EXPECT_EQ(kMediaTime, pipeline_->GetMediaTime());
+
+  // Seek backward 1 second. Do not run RunLoop to ensure renderer is not yet
+  // notified of the seek (via media thread).
+  base::TimeDelta kSeekTime = kMediaTime - base::TimeDelta::FromSeconds(1);
+  ExpectSeek(kSeekTime, false);
+  pipeline_->Seek(kSeekTime, base::Bind(&CallbackHelper::OnSeek,
+                                        base::Unretained(&callbacks_)));
+
+  // Verify pipeline returns the seek time in spite of renderer returning the
+  // stale media time.
+  EXPECT_EQ(kSeekTime, pipeline_->GetMediaTime());
+  EXPECT_EQ(kMediaTime, renderer_->GetMediaTime());
+
+  // Allow seek task to post to the renderer.
+  base::RunLoop().RunUntilIdle();
+
+  // With seek completed, pipeline should again return the renderer's media time
+  // (as long as media time is moving forward).
+  EXPECT_EQ(kMediaTime, pipeline_->GetMediaTime());
+}
+
 class PipelineTeardownTest : public PipelineImplTest {
  public:
   enum TeardownState {
diff --git a/media/gpu/media_foundation_video_encode_accelerator_win.cc b/media/gpu/media_foundation_video_encode_accelerator_win.cc
index a43e2ad..17ba2b2 100644
--- a/media/gpu/media_foundation_video_encode_accelerator_win.cc
+++ b/media/gpu/media_foundation_video_encode_accelerator_win.cc
@@ -34,8 +34,8 @@
 const int32_t kDefaultTargetBitrate = 5000000;
 const size_t kMaxFrameRateNumerator = 30;
 const size_t kMaxFrameRateDenominator = 1;
-const size_t kMaxResolutionWidth = 3840;
-const size_t kMaxResolutionHeight = 2176;
+const size_t kMaxResolutionWidth = 1920;
+const size_t kMaxResolutionHeight = 1088;
 const size_t kNumInputBuffers = 3;
 // Media Foundation uses 100 nanosecond units for time, see
 // https://msdn.microsoft.com/en-us/library/windows/desktop/ms697282(v=vs.85).aspx
@@ -46,6 +46,9 @@
     L"mf.dll", L"mfplat.dll",
 };
 
+// Resolutions that some platforms support, should be listed in ascending order.
+constexpr const gfx::Size kOptionalMaxResolutions[] = {gfx::Size(3840, 2176)};
+
 }  // namespace
 
 class MediaFoundationVideoEncodeAccelerator::EncodeOutput {
@@ -101,7 +104,6 @@
   DCHECK(main_client_task_runner_->BelongsToCurrentThread());
 
   SupportedProfiles profiles;
-
   target_bitrate_ = kDefaultTargetBitrate;
   frame_rate_ = kMaxFrameRateNumerator / kMaxFrameRateDenominator;
   input_visible_size_ = gfx::Size(kMaxResolutionWidth, kMaxResolutionHeight);
@@ -112,6 +114,14 @@
         << "Hardware encode acceleration is not available on this platform.";
     return profiles;
   }
+
+  gfx::Size highest_supported_resolution = input_visible_size_;
+  for (const auto& resolution : kOptionalMaxResolutions) {
+    DCHECK_GT(resolution.GetArea(), highest_supported_resolution.GetArea());
+    if (!IsResolutionSupported(resolution))
+      break;
+    highest_supported_resolution = resolution;
+  }
   ReleaseEncoderResources();
 
   SupportedProfile profile;
@@ -120,7 +130,7 @@
   profile.profile = H264PROFILE_BASELINE;
   profile.max_framerate_numerator = kMaxFrameRateNumerator;
   profile.max_framerate_denominator = kMaxFrameRateDenominator;
-  profile.max_resolution = gfx::Size(kMaxResolutionWidth, kMaxResolutionHeight);
+  profile.max_resolution = highest_supported_resolution;
   profiles.push_back(profile);
   return profiles;
 }
@@ -449,6 +459,28 @@
   return SUCCEEDED(hr);
 }
 
+bool MediaFoundationVideoEncodeAccelerator::IsResolutionSupported(
+    const gfx::Size& resolution) {
+  DCHECK(main_client_task_runner_->BelongsToCurrentThread());
+  DCHECK(encoder_);
+
+  HRESULT hr =
+      MFSetAttributeSize(imf_output_media_type_.get(), MF_MT_FRAME_SIZE,
+                         resolution.width(), resolution.height());
+  RETURN_ON_HR_FAILURE(hr, "Couldn't set frame size", false);
+  hr = encoder_->SetOutputType(output_stream_id_, imf_output_media_type_.get(),
+                               0);
+  RETURN_ON_HR_FAILURE(hr, "Couldn't set output media type", false);
+
+  hr = MFSetAttributeSize(imf_input_media_type_.get(), MF_MT_FRAME_SIZE,
+                          resolution.width(), resolution.height());
+  RETURN_ON_HR_FAILURE(hr, "Couldn't set frame size", false);
+  hr = encoder_->SetInputType(input_stream_id_, imf_input_media_type_.get(), 0);
+  RETURN_ON_HR_FAILURE(hr, "Couldn't set input media type", false);
+
+  return true;
+}
+
 void MediaFoundationVideoEncodeAccelerator::NotifyError(
     VideoEncodeAccelerator::Error error) {
   DCHECK(encoder_thread_task_runner_->BelongsToCurrentThread());
diff --git a/media/gpu/media_foundation_video_encode_accelerator_win.h b/media/gpu/media_foundation_video_encode_accelerator_win.h
index cd67630..1720a78b 100644
--- a/media/gpu/media_foundation_video_encode_accelerator_win.h
+++ b/media/gpu/media_foundation_video_encode_accelerator_win.h
@@ -76,6 +76,10 @@
   // Initializes encoder parameters for real-time use.
   bool SetEncoderModes();
 
+  // Returns true if we can initialize input and output samples with the given
+  // frame size, otherwise false.
+  bool IsResolutionSupported(const gfx::Size& size);
+
   // Helper function to notify the client of an error on
   // |main_client_task_runner_|.
   void NotifyError(VideoEncodeAccelerator::Error error);
diff --git a/mojo/public/cpp/bindings/BUILD.gn b/mojo/public/cpp/bindings/BUILD.gn
index 1b4dfa6f..4f436d50a 100644
--- a/mojo/public/cpp/bindings/BUILD.gn
+++ b/mojo/public/cpp/bindings/BUILD.gn
@@ -116,7 +116,6 @@
     "map.h",
     "map_data_view.h",
     "map_traits.h",
-    "map_traits_standard.h",
     "map_traits_stl.h",
     "message.h",
     "message_header_validator.h",
@@ -128,7 +127,6 @@
     "pipe_control_message_handler_delegate.h",
     "pipe_control_message_proxy.h",
     "scoped_interface_endpoint_handle.h",
-    "stl_converters.h",
     "string.h",
     "string_data_view.h",
     "string_traits.h",
diff --git a/mojo/public/cpp/bindings/lib/array_serialization.h b/mojo/public/cpp/bindings/lib/array_serialization.h
index 9e68f452..acfd4e3e 100644
--- a/mojo/public/cpp/bindings/lib/array_serialization.h
+++ b/mojo/public/cpp/bindings/lib/array_serialization.h
@@ -19,7 +19,6 @@
 #include "mojo/public/cpp/bindings/lib/serialization_forward.h"
 #include "mojo/public/cpp/bindings/lib/template_util.h"
 #include "mojo/public/cpp/bindings/lib/validation_errors.h"
-#include "mojo/public/cpp/bindings/map.h"
 
 namespace mojo {
 namespace internal {
diff --git a/mojo/public/cpp/bindings/lib/serialization.h b/mojo/public/cpp/bindings/lib/serialization.h
index a570e2205..8fac6396 100644
--- a/mojo/public/cpp/bindings/lib/serialization.h
+++ b/mojo/public/cpp/bindings/lib/serialization.h
@@ -18,7 +18,6 @@
 #include "mojo/public/cpp/bindings/lib/native_struct_serialization.h"
 #include "mojo/public/cpp/bindings/lib/string_serialization.h"
 #include "mojo/public/cpp/bindings/lib/template_util.h"
-#include "mojo/public/cpp/bindings/map_traits_standard.h"
 #include "mojo/public/cpp/bindings/map_traits_stl.h"
 #include "mojo/public/cpp/bindings/string_traits_standard.h"
 #include "mojo/public/cpp/bindings/string_traits_stl.h"
diff --git a/mojo/public/cpp/bindings/map.h b/mojo/public/cpp/bindings/map.h
index ec40df3..c1ba075 100644
--- a/mojo/public/cpp/bindings/map.h
+++ b/mojo/public/cpp/bindings/map.h
@@ -5,300 +5,12 @@
 #ifndef MOJO_PUBLIC_CPP_BINDINGS_MAP_H_
 #define MOJO_PUBLIC_CPP_BINDINGS_MAP_H_
 
-#include <stddef.h>
 #include <map>
 #include <unordered_map>
 #include <utility>
 
-#include "base/logging.h"
-#include "base/macros.h"
-#include "mojo/public/cpp/bindings/array.h"
-#include "mojo/public/cpp/bindings/lib/map_data_internal.h"
-#include "mojo/public/cpp/bindings/lib/template_util.h"
-#include "mojo/public/cpp/bindings/type_converter.h"
-
 namespace mojo {
 
-// A move-only map that can handle move-only values. Map has the following
-// characteristics:
-//   - The map itself can be null, and this is distinct from empty.
-//   - Keys must not be move-only.
-//   - The Key-type's "<" operator is used to sort the entries, and also is
-//     used to determine equality of the key values.
-//   - There can only be one entry per unique key.
-//   - Values of move-only types will be moved into the Map when they are added
-//     using the insert() method.
-template <typename K, typename V>
-class Map {
- public:
-  using Key = K;
-  using Value = V;
-
-  // Map keys cannot be move only classes.
-  static_assert(!internal::IsMoveOnlyType<Key>::value,
-                "Map keys cannot be move only types.");
-
-  using Iterator = typename std::map<Key, Value>::iterator;
-  using ConstIterator = typename std::map<Key, Value>::const_iterator;
-
-  // Constructs an empty map.
-  Map() : is_null_(false) {}
-  // Constructs a null map.
-  Map(std::nullptr_t null_pointer) : is_null_(true) {}
-
-  // Constructs a non-null Map containing the specified |keys| mapped to the
-  // corresponding |values|.
-  Map(mojo::Array<Key> keys, mojo::Array<Value> values) : is_null_(false) {
-    DCHECK(keys.size() == values.size());
-    for (size_t i = 0; i < keys.size(); ++i)
-      map_.insert(std::make_pair(keys[i], std::move(values[i])));
-  }
-
-  ~Map() {}
-
-  Map(std::map<Key, Value>&& other) : map_(std::move(other)), is_null_(false) {}
-  Map(Map&& other) : is_null_(true) { Take(&other); }
-
-  Map& operator=(std::map<Key, Value>&& other) {
-    is_null_ = false;
-    map_ = std::move(other);
-    return *this;
-  }
-  Map& operator=(Map&& other) {
-    Take(&other);
-    return *this;
-  }
-
-  Map& operator=(std::nullptr_t null_pointer) {
-    is_null_ = true;
-    map_.clear();
-    return *this;
-  }
-
-  // Copies the contents of some other type of map into a new Map using a
-  // TypeConverter. A TypeConverter for std::map to Map is defined below.
-  template <typename U>
-  static Map From(const U& other) {
-    return TypeConverter<Map, U>::Convert(other);
-  }
-
-  // Copies the contents of the Map into some other type of map. A TypeConverter
-  // for Map to std::map is defined below.
-  template <typename U>
-  U To() const {
-    return TypeConverter<U, Map>::Convert(*this);
-  }
-
-  // Indicates whether the map is null (which is distinct from empty).
-  bool is_null() const { return is_null_; }
-
-  // Indicates whether the map is empty (which is distinct from null).
-  bool empty() const { return map_.empty() && !is_null_; }
-
-  // Indicates the number of keys in the map, which will be zero if the map is
-  // null.
-  size_t size() const { return map_.size(); }
-
-  // Inserts a key-value pair into the map. Like std::map, this does not insert
-  // |value| if |key| is already a member of the map.
-  void insert(const Key& key, const Value& value) {
-    is_null_ = false;
-    map_.insert(std::make_pair(key, value));
-  }
-  void insert(const Key& key, Value&& value) {
-    is_null_ = false;
-    map_.insert(std::make_pair(key, std::move(value)));
-  }
-
-  // Returns a reference to the value associated with the specified key,
-  // crashing the process if the key is not present in the map.
-  Value& at(const Key& key) { return map_.at(key); }
-  const Value& at(const Key& key) const { return map_.at(key); }
-
-  // Returns a reference to the value associated with the specified key,
-  // creating a new entry if the key is not already present in the map. A
-  // newly-created value will be value-initialized (meaning that it will be
-  // initialized by the default constructor of the value type, if any, or else
-  // will be zero-initialized).
-  Value& operator[](const Key& key) {
-    is_null_ = false;
-    return map_[key];
-  }
-
-  // Sets the map to empty (even if previously it was null).
-  void SetToEmpty() {
-    is_null_ = false;
-    map_.clear();
-  }
-
-  // Returns a const reference to the std::map managed by this class. If this
-  // object is null, the return value will be an empty map.
-  const std::map<Key, Value>& storage() const { return map_; }
-
-  // Passes the underlying storage and resets this map to null.
-  std::map<Key, Value> PassStorage() {
-    is_null_ = true;
-    return std::move(map_);
-  }
-
-  operator const std::map<Key, Value>&() const { return map_; }
-
-  // Swaps the contents of this Map with another Map of the same type (including
-  // nullness).
-  void Swap(Map<Key, Value>* other) {
-    std::swap(is_null_, other->is_null_);
-    map_.swap(other->map_);
-  }
-
-  // Swaps the contents of this Map with an std::map containing keys and values
-  // of the same type. Since std::map cannot represent the null state, the
-  // std::map will be empty if Map is null. The Map will always be left in a
-  // non-null state.
-  void Swap(std::map<Key, Value>* other) {
-    is_null_ = false;
-    map_.swap(*other);
-  }
-
-  // Removes all contents from the Map and places them into parallel key/value
-  // arrays. Each key will be copied from the source to the destination, and
-  // values will be copied unless their type is designated move-only, in which
-  // case they will be moved. Either way, the Map will be left in a null state.
-  void DecomposeMapTo(mojo::Array<Key>* keys, mojo::Array<Value>* values) {
-    std::vector<Key> key_vector;
-    key_vector.reserve(map_.size());
-    std::vector<Value> value_vector;
-    value_vector.reserve(map_.size());
-
-    for (auto& entry : map_) {
-      key_vector.push_back(entry.first);
-      value_vector.push_back(std::move(entry.second));
-    }
-
-    map_.clear();
-    is_null_ = true;
-
-    keys->Swap(&key_vector);
-    values->Swap(&value_vector);
-  }
-
-  // Returns a new Map that contains a copy of the contents of this map. If the
-  // key/value type defines a Clone() method, it will be used; otherwise copy
-  // constructor/assignment will be used.
-  //
-  // Please note that calling this method will fail compilation if the key/value
-  // type cannot be cloned (which usually means that it is a Mojo handle type or
-  // a type containing Mojo handles).
-  Map Clone() const {
-    Map result;
-    result.is_null_ = is_null_;
-    for (auto it = map_.begin(); it != map_.end(); ++it) {
-      result.map_.insert(std::make_pair(internal::Clone(it->first),
-                                        internal::Clone(it->second)));
-    }
-    return result;
-  }
-
-  // Indicates whether the contents of this map are equal to those of another
-  // Map (including nullness). If the key/value type defines an Equals() method,
-  // it will be used; otherwise == operator will be used.
-  bool Equals(const Map& other) const {
-    if (is_null() != other.is_null())
-      return false;
-    if (size() != other.size())
-      return false;
-    auto i = begin();
-    auto j = other.begin();
-    while (i != end()) {
-      if (!internal::Equals(i->first, j->first))
-        return false;
-      if (!internal::Equals(i->second, j->second))
-        return false;
-      ++i;
-      ++j;
-    }
-    return true;
-  }
-
-  // Provide read-only iteration over map members in a way similar to STL
-  // collections.
-  ConstIterator begin() const { return map_.begin(); }
-  Iterator begin() { return map_.begin(); }
-
-  ConstIterator end() const { return map_.end(); }
-  Iterator end() { return map_.end(); }
-
-  // Returns the iterator pointing to the entry for |key|, if present, or else
-  // returns end().
-  ConstIterator find(const Key& key) const { return map_.find(key); }
-  Iterator find(const Key& key) { return map_.find(key); }
-
- private:
-  typedef std::map<Key, Value> Map::*Testable;
-
- public:
-  // The Map may be used in boolean expressions to determine if it is non-null,
-  // but is not implicitly convertible to an actual bool value (which would be
-  // dangerous).
-  operator Testable() const { return is_null_ ? 0 : &Map::map_; }
-
- private:
-  // Forbid the == and != operators explicitly, otherwise Map will be converted
-  // to Testable to do == or != comparison.
-  template <typename T, typename U>
-  bool operator==(const Map<T, U>& other) const = delete;
-  template <typename T, typename U>
-  bool operator!=(const Map<T, U>& other) const = delete;
-
-  void Take(Map* other) {
-    operator=(nullptr);
-    Swap(other);
-  }
-
-  std::map<Key, Value> map_;
-  bool is_null_;
-
-  DISALLOW_COPY_AND_ASSIGN(Map);
-};
-
-// Copies the contents of an std::map to a new Map, optionally changing the
-// types of the keys and values along the way using TypeConverter.
-template <typename MojoKey,
-          typename MojoValue,
-          typename STLKey,
-          typename STLValue>
-struct TypeConverter<Map<MojoKey, MojoValue>, std::map<STLKey, STLValue>> {
-  static Map<MojoKey, MojoValue> Convert(
-      const std::map<STLKey, STLValue>& input) {
-    Map<MojoKey, MojoValue> result;
-    for (auto& pair : input) {
-      result.insert(TypeConverter<MojoKey, STLKey>::Convert(pair.first),
-                    TypeConverter<MojoValue, STLValue>::Convert(pair.second));
-    }
-    return result;
-  }
-};
-
-// Copies the contents of a Map to an std::map, optionally changing the types of
-// the keys and values along the way using TypeConverter.
-template <typename MojoKey,
-          typename MojoValue,
-          typename STLKey,
-          typename STLValue>
-struct TypeConverter<std::map<STLKey, STLValue>, Map<MojoKey, MojoValue>> {
-  static std::map<STLKey, STLValue> Convert(
-      const Map<MojoKey, MojoValue>& input) {
-    std::map<STLKey, STLValue> result;
-    if (!input.is_null()) {
-      for (auto it = input.begin(); it != input.end(); ++it) {
-        result.insert(std::make_pair(
-            TypeConverter<STLKey, MojoKey>::Convert(it->first),
-            TypeConverter<STLValue, MojoValue>::Convert(it->second)));
-      }
-    }
-    return result;
-  }
-};
-
 // TODO(yzshen): These conversion functions should be removed and callsites
 // should be revisited and changed to use the same map type.
 template <typename Key, typename Value>
diff --git a/mojo/public/cpp/bindings/map_traits_standard.h b/mojo/public/cpp/bindings/map_traits_standard.h
deleted file mode 100644
index 0c76890..0000000
--- a/mojo/public/cpp/bindings/map_traits_standard.h
+++ /dev/null
@@ -1,53 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef MOJO_PUBLIC_CPP_BINDINGS_MAP_TRAITS_STANDARD_H_
-#define MOJO_PUBLIC_CPP_BINDINGS_MAP_TRAITS_STANDARD_H_
-
-#include "mojo/public/cpp/bindings/map.h"
-#include "mojo/public/cpp/bindings/map_traits.h"
-
-namespace mojo {
-
-template <typename K, typename V>
-struct MapTraits<Map<K, V>> {
-  using Key = K;
-  using Value = V;
-  using Iterator = typename Map<K, V>::Iterator;
-  using ConstIterator = typename Map<K, V>::ConstIterator;
-
-  static bool IsNull(const Map<K, V>& input) { return input.is_null(); }
-  static void SetToNull(Map<K, V>* output) { *output = nullptr; }
-
-  static size_t GetSize(const Map<K, V>& input) { return input.size(); }
-
-  static ConstIterator GetBegin(const Map<K, V>& input) {
-    return input.begin();
-  }
-  static Iterator GetBegin(Map<K, V>& input) { return input.begin(); }
-
-  static void AdvanceIterator(ConstIterator& iterator) { iterator++; }
-  static void AdvanceIterator(Iterator& iterator) { iterator++; }
-
-  static const K& GetKey(Iterator& iterator) { return iterator->first; }
-  static const K& GetKey(ConstIterator& iterator) { return iterator->first; }
-
-  static V& GetValue(Iterator& iterator) { return iterator->second; }
-  static const V& GetValue(ConstIterator& iterator) { return iterator->second; }
-
-  static bool Insert(Map<K, V>& input, const K& key, V&& value) {
-    input.insert(key, std::forward<V>(value));
-    return true;
-  }
-  static bool Insert(Map<K, V>& input, const K& key, const V& value) {
-    input.insert(key, value);
-    return true;
-  }
-
-  static void SetToEmpty(Map<K, V>* output) { output->SetToEmpty(); }
-};
-
-}  // namespace mojo
-
-#endif  // MOJO_PUBLIC_CPP_BINDINGS_MAP_TRAITS_STANDARD_H_
diff --git a/mojo/public/cpp/bindings/stl_converters.h b/mojo/public/cpp/bindings/stl_converters.h
deleted file mode 100644
index 92b2924e..0000000
--- a/mojo/public/cpp/bindings/stl_converters.h
+++ /dev/null
@@ -1,245 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef MOJO_PUBLIC_CPP_BINDINGS_STL_CONVERTERS_H_
-#define MOJO_PUBLIC_CPP_BINDINGS_STL_CONVERTERS_H_
-
-#include <map>
-#include <string>
-#include <type_traits>
-#include <vector>
-
-#include "mojo/public/cpp/bindings/array.h"
-#include "mojo/public/cpp/bindings/map.h"
-#include "mojo/public/cpp/bindings/string.h"
-
-// Two functions are defined to facilitate conversion between
-// mojo::Array/Map/String and std::vector/map/string: mojo::UnwrapToSTLType()
-// recursively convert mojo types to STL types; mojo::WrapSTLType() does the
-// opposite. For example:
-//   mojo::Array<mojo::Map<mojo::String, mojo::Array<int32_t>>> mojo_obj;
-//
-//   std::vector<std::map<std::string, std::vector<int32_t>>> stl_obj =
-//       mojo::UnwrapToSTLType(std::move(mojo_obj));
-//
-//   mojo_obj = mojo::WrapSTLType(std::move(stl_obj));
-//
-// Notes:
-//   - The conversion moves as much contents as possible. The two functions both
-//     take an rvalue ref as input in order to avoid accidental copies.
-//   - Because std::vector/map/string cannot express null, UnwrapToSTLType()
-//     converts null mojo::Array/Map/String to empty.
-//   - The recursive conversion stops at any types that are not the types listed
-//     above. For example, unwrapping mojo::Array<StructContainingMojoMap> will
-//     result in std::vector<StructContainingMojoMap>. It won't convert
-//     mojo::Map inside the struct.
-
-namespace mojo {
-namespace internal {
-
-template <typename T>
-struct UnwrapTraits;
-
-template <typename T>
-struct UnwrapShouldGoDeeper {
- public:
-  static const bool value =
-      !std::is_same<T, typename UnwrapTraits<T>::Type>::value;
-};
-
-template <typename T>
-struct UnwrapTraits {
- public:
-  using Type = T;
-  static Type Unwrap(T input) { return input; }
-};
-
-template <typename T>
-struct UnwrapTraits<Array<T>> {
- public:
-  using Type = std::vector<typename UnwrapTraits<T>::Type>;
-
-  static Type Unwrap(Array<T> input) {
-    return Helper<T>::Run(std::move(input));
-  }
-
- private:
-  template <typename U, bool should_go_deeper = UnwrapShouldGoDeeper<U>::value>
-  struct Helper {};
-
-  template <typename U>
-  struct Helper<U, true> {
-   public:
-    static Type Run(Array<T> input) {
-      Type output;
-      output.reserve(input.size());
-      for (size_t i = 0; i < input.size(); ++i)
-        output.push_back(UnwrapTraits<T>::Unwrap(std::move(input[i])));
-      return output;
-    }
-  };
-
-  template <typename U>
-  struct Helper<U, false> {
-   public:
-    static Type Run(Array<T> input) { return input.PassStorage(); }
-  };
-};
-
-template <typename K, typename V>
-struct UnwrapTraits<Map<K, V>> {
- public:
-  using Type =
-      std::map<typename UnwrapTraits<K>::Type, typename UnwrapTraits<V>::Type>;
-
-  static Type Unwrap(Map<K, V> input) {
-    return Helper<K, V>::Run(std::move(input));
-  }
-
- private:
-  template <typename X,
-            typename Y,
-            bool should_go_deeper = UnwrapShouldGoDeeper<X>::value ||
-                                    UnwrapShouldGoDeeper<Y>::value>
-  struct Helper {};
-
-  template <typename X, typename Y>
-  struct Helper<X, Y, true> {
-   public:
-    static Type Run(Map<K, V> input) {
-      std::map<K, V> input_storage = input.PassStorage();
-      Type output;
-      for (auto& pair : input_storage) {
-        output.insert(
-            std::make_pair(UnwrapTraits<K>::Unwrap(pair.first),
-                           UnwrapTraits<V>::Unwrap(std::move(pair.second))));
-      }
-      return output;
-    }
-  };
-
-  template <typename X, typename Y>
-  struct Helper<X, Y, false> {
-   public:
-    static Type Run(Map<K, V> input) { return input.PassStorage(); }
-  };
-};
-
-template <>
-struct UnwrapTraits<String> {
- public:
-  using Type = std::string;
-
-  static std::string Unwrap(const String& input) { return input; }
-};
-
-template <typename T>
-struct WrapTraits;
-
-template <typename T>
-struct WrapShouldGoDeeper {
- public:
-  static const bool value =
-      !std::is_same<T, typename WrapTraits<T>::Type>::value;
-};
-
-template <typename T>
-struct WrapTraits {
- public:
-  using Type = T;
-
-  static T Wrap(T input) { return input; }
-};
-
-template <typename T>
-struct WrapTraits<std::vector<T>> {
- public:
-  using Type = Array<typename WrapTraits<T>::Type>;
-
-  static Type Wrap(std::vector<T> input) {
-    return Helper<T>::Run(std::move(input));
-  }
-
- private:
-  template <typename U, bool should_go_deeper = WrapShouldGoDeeper<U>::value>
-  struct Helper {};
-
-  template <typename U>
-  struct Helper<U, true> {
-   public:
-    static Type Run(std::vector<T> input) {
-      std::vector<typename WrapTraits<T>::Type> output_storage;
-      output_storage.reserve(input.size());
-      for (auto& element : input)
-        output_storage.push_back(WrapTraits<T>::Wrap(std::move(element)));
-      return Type(std::move(output_storage));
-    }
-  };
-
-  template <typename U>
-  struct Helper<U, false> {
-   public:
-    static Type Run(std::vector<T> input) { return Type(std::move(input)); }
-  };
-};
-
-template <typename K, typename V>
-struct WrapTraits<std::map<K, V>> {
- public:
-  using Type = Map<typename WrapTraits<K>::Type, typename WrapTraits<V>::Type>;
-
-  static Type Wrap(std::map<K, V> input) {
-    return Helper<K, V>::Run(std::move(input));
-  }
-
- private:
-  template <typename X,
-            typename Y,
-            bool should_go_deeper =
-                WrapShouldGoDeeper<X>::value || WrapShouldGoDeeper<Y>::value>
-  struct Helper {};
-
-  template <typename X, typename Y>
-  struct Helper<X, Y, true> {
-   public:
-    static Type Run(std::map<K, V> input) {
-      Type output;
-      for (auto& pair : input) {
-        output.insert(WrapTraits<K>::Wrap(pair.first),
-                      WrapTraits<V>::Wrap(std::move(pair.second)));
-      }
-      return output;
-    }
-  };
-
-  template <typename X, typename Y>
-  struct Helper<X, Y, false> {
-   public:
-    static Type Run(std::map<K, V> input) { return Type(std::move(input)); }
-  };
-};
-
-template <>
-struct WrapTraits<std::string> {
- public:
-  using Type = String;
-
-  static String Wrap(const std::string& input) { return input; }
-};
-
-}  // namespace internal
-
-template <typename T>
-typename internal::UnwrapTraits<T>::Type UnwrapToSTLType(T&& input) {
-  return internal::UnwrapTraits<T>::Unwrap(std::move(input));
-}
-
-template <typename T>
-typename internal::WrapTraits<T>::Type WrapSTLType(T&& input) {
-  return internal::WrapTraits<T>::Wrap(std::move(input));
-}
-
-}  // namespace mojo
-
-#endif  // MOJO_PUBLIC_CPP_BINDINGS_STL_CONVERTERS_H_
diff --git a/mojo/public/cpp/bindings/struct_traits.h b/mojo/public/cpp/bindings/struct_traits.h
index d570eac..a8aaebc 100644
--- a/mojo/public/cpp/bindings/struct_traits.h
+++ b/mojo/public/cpp/bindings/struct_traits.h
@@ -38,7 +38,7 @@
 //
 //        - map:
 //          Value or reference of any type that has a MapTraits defined.
-//          Supported by default: std::map, std::unordered_map, mojo::Map,
+//          Supported by default: std::map, std::unordered_map,
 //          WTF::HashMap (in blink).
 //
 //        - struct:
diff --git a/mojo/public/cpp/bindings/tests/BUILD.gn b/mojo/public/cpp/bindings/tests/BUILD.gn
index 2e61173..7b5db260 100644
--- a/mojo/public/cpp/bindings/tests/BUILD.gn
+++ b/mojo/public/cpp/bindings/tests/BUILD.gn
@@ -23,8 +23,6 @@
     "handle_passing_unittest.cc",
     "hash_unittest.cc",
     "interface_ptr_unittest.cc",
-    "map_common_test.h",
-    "map_unittest.cc",
     "message_queue.cc",
     "message_queue.h",
     "multiplex_router_unittest.cc",
@@ -34,7 +32,6 @@
     "router_test_util.h",
     "sample_service_unittest.cc",
     "serialization_warning_unittest.cc",
-    "stl_converters_unittest.cc",
     "string_unittest.cc",
     "struct_unittest.cc",
     "sync_method_unittest.cc",
diff --git a/mojo/public/cpp/bindings/tests/equals_unittest.cc b/mojo/public/cpp/bindings/tests/equals_unittest.cc
index 466b58d..4862b93f 100644
--- a/mojo/public/cpp/bindings/tests/equals_unittest.cc
+++ b/mojo/public/cpp/bindings/tests/equals_unittest.cc
@@ -84,37 +84,6 @@
   EXPECT_TRUE(n1.Equals(n2));
 }
 
-TEST_F(EqualsTest, Map) {
-  auto n1(NamedRegion::New());
-  n1->name.emplace("foo");
-  n1->rects.emplace();
-  n1->rects->push_back(CreateRect());
-
-  Map<std::string, NamedRegionPtr> m1;
-  m1.insert("foo", std::move(n1));
-
-  decltype(m1) m2;
-  EXPECT_FALSE(m1.Equals(m2));
-
-  m2.insert("bar", m1.at("foo").Clone());
-  EXPECT_FALSE(m1.Equals(m2));
-
-  m2 = m1.Clone();
-  m2.at("foo")->name.emplace("monkey");
-  EXPECT_FALSE(m1.Equals(m2));
-
-  m2 = m1.Clone();
-  m2.at("foo")->rects->push_back(Rect::New());
-  EXPECT_FALSE(m1.Equals(m2));
-
-  m2.at("foo")->rects->resize(1);
-  (*m2.at("foo")->rects)[0]->width = 1;
-  EXPECT_FALSE(m1.Equals(m2));
-
-  m2 = m1.Clone();
-  EXPECT_TRUE(m1.Equals(m2));
-}
-
 TEST_F(EqualsTest, InterfacePtr) {
   base::MessageLoop message_loop;
 
diff --git a/mojo/public/cpp/bindings/tests/map_common_test.h b/mojo/public/cpp/bindings/tests/map_common_test.h
deleted file mode 100644
index 67fc00c..0000000
--- a/mojo/public/cpp/bindings/tests/map_common_test.h
+++ /dev/null
@@ -1,231 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "mojo/public/cpp/bindings/array.h"
-#include "mojo/public/cpp/bindings/lib/fixed_buffer.h"
-#include "mojo/public/cpp/bindings/lib/serialization.h"
-#include "mojo/public/cpp/bindings/lib/validate_params.h"
-#include "mojo/public/cpp/bindings/map.h"
-#include "mojo/public/cpp/bindings/string.h"
-#include "mojo/public/cpp/bindings/tests/container_test_util.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace WTF {
-class String;
-}
-
-namespace mojo {
-
-template <typename T>
-class WTFArray;
-
-template <typename K, typename V>
-class WTFMap;
-
-namespace test {
-namespace {
-
-struct StringIntData {
-  const char* string_data;
-  int int_data;
-} kStringIntData[] = {
-    {"one", 1},
-    {"two", 2},
-    {"three", 3},
-    {"four", 4},
-};
-
-const size_t kStringIntDataSize = 4;
-
-}  // namespace
-
-template <template <typename...> class MapType>
-struct TypeTraits;
-
-template <>
-struct TypeTraits<Map> {
-  using StringType = mojo::String;
-  template <typename T>
-  using ArrayType = Array<T>;
-};
-
-template <>
-struct TypeTraits<WTFMap> {
-  using StringType = WTF::String;
-  template <typename T>
-  using ArrayType = WTFArray<T>;
-};
-
-// Common tests for both mojo::Map and mojo::WTFMap.
-template <template <typename...> class MapType>
-class MapCommonTest {
- public:
-  using StringType = typename TypeTraits<MapType>::StringType;
-  template <typename T>
-  using ArrayType = typename TypeTraits<MapType>::template ArrayType<T>;
-
-  // Tests null and empty maps.
-  static void NullAndEmpty() {
-    MapType<char, char> map0;
-    EXPECT_TRUE(map0.empty());
-    EXPECT_FALSE(map0.is_null());
-    map0 = nullptr;
-    EXPECT_TRUE(map0.is_null());
-    EXPECT_FALSE(map0.empty());
-
-    MapType<char, char> map1(nullptr);
-    EXPECT_TRUE(map1.is_null());
-    EXPECT_FALSE(map1.empty());
-    map1.SetToEmpty();
-    EXPECT_TRUE(map1.empty());
-    EXPECT_FALSE(map1.is_null());
-  }
-
-  // Tests that basic Map operations work.
-  static void InsertWorks() {
-    MapType<StringType, int> map;
-    for (size_t i = 0; i < kStringIntDataSize; ++i)
-      map.insert(kStringIntData[i].string_data, kStringIntData[i].int_data);
-
-    for (size_t i = 0; i < kStringIntDataSize; ++i) {
-      EXPECT_EQ(kStringIntData[i].int_data,
-                map.at(kStringIntData[i].string_data));
-    }
-  }
-
-  static void TestIndexOperator() {
-    MapType<StringType, int> map;
-    for (size_t i = 0; i < kStringIntDataSize; ++i)
-      map[kStringIntData[i].string_data] = kStringIntData[i].int_data;
-
-    for (size_t i = 0; i < kStringIntDataSize; ++i) {
-      EXPECT_EQ(kStringIntData[i].int_data,
-                map.at(kStringIntData[i].string_data));
-    }
-  }
-
-  static void TestIndexOperatorAsRValue() {
-    MapType<StringType, int> map;
-    for (size_t i = 0; i < kStringIntDataSize; ++i)
-      map.insert(kStringIntData[i].string_data, kStringIntData[i].int_data);
-
-    for (size_t i = 0; i < kStringIntDataSize; ++i) {
-      EXPECT_EQ(kStringIntData[i].int_data, map[kStringIntData[i].string_data]);
-    }
-  }
-
-  static void TestIndexOperatorMoveOnly() {
-    ASSERT_EQ(0u, MoveOnlyType::num_instances());
-    MapType<StringType, ArrayType<int32_t>> map;
-
-    for (size_t i = 0; i < kStringIntDataSize; ++i) {
-      const char* key = kStringIntData[i].string_data;
-      ArrayType<int32_t> array(1);
-      array[0] = kStringIntData[i].int_data;
-      map[key] = std::move(array);
-      EXPECT_TRUE(map);
-    }
-
-    // We now read back that data, to test the behavior of operator[].
-    for (size_t i = 0; i < kStringIntDataSize; ++i) {
-      auto& value = map[kStringIntData[i].string_data];
-      ASSERT_EQ(1u, value.size());
-      EXPECT_EQ(kStringIntData[i].int_data, value[0]);
-    }
-  }
-
-  static void MapArrayClone() {
-    MapType<StringType, ArrayType<StringType>> m;
-    for (size_t i = 0; i < kStringIntDataSize; ++i) {
-      ArrayType<StringType> s(1);
-      s[0] = StringType(kStringIntData[i].string_data);
-      m.insert(kStringIntData[i].string_data, std::move(s));
-    }
-
-    MapType<StringType, ArrayType<StringType>> m2 = m.Clone();
-
-    for (size_t i = 0; i < kStringIntDataSize; ++i) {
-      ASSERT_NE(m2.end(), m2.find(kStringIntData[i].string_data));
-      ASSERT_EQ(1u, m2[kStringIntData[i].string_data].size());
-      EXPECT_EQ(StringType(kStringIntData[i].string_data),
-                m2[kStringIntData[i].string_data][0]);
-    }
-  }
-
-  static void ArrayOfMap() {
-    {
-      using MojomType = ArrayDataView<MapDataView<int32_t, int8_t>>;
-      using UserType = ArrayType<MapType<int32_t, int8_t>>;
-
-      UserType array(1);
-      array[0].insert(1, 42);
-
-      mojo::internal::SerializationContext context;
-      size_t size =
-          mojo::internal::PrepareToSerialize<MojomType>(array, &context);
-      mojo::internal::FixedBufferForTesting buf(size);
-      typename mojo::internal::MojomTypeTraits<MojomType>::Data* data;
-      mojo::internal::ContainerValidateParams validate_params(
-          0, false,
-          new mojo::internal::ContainerValidateParams(
-              new mojo::internal::ContainerValidateParams(0, false, nullptr),
-              new mojo::internal::ContainerValidateParams(0, false, nullptr)));
-
-      mojo::internal::Serialize<MojomType>(array, &buf, &data, &validate_params,
-                                           &context);
-
-      UserType deserialized_array;
-      mojo::internal::Deserialize<MojomType>(data, &deserialized_array,
-                                             &context);
-
-      ASSERT_EQ(1u, deserialized_array.size());
-      ASSERT_EQ(1u, deserialized_array[0].size());
-      ASSERT_EQ(42, deserialized_array[0].at(1));
-    }
-
-    {
-      using MojomType =
-          ArrayDataView<MapDataView<StringDataView, ArrayDataView<bool>>>;
-      using UserType = ArrayType<MapType<StringType, ArrayType<bool>>>;
-
-      UserType array(1);
-      ArrayType<bool> map_value(2);
-      map_value[0] = false;
-      map_value[1] = true;
-      array[0].insert("hello world", std::move(map_value));
-
-      mojo::internal::SerializationContext context;
-      size_t size =
-          mojo::internal::PrepareToSerialize<MojomType>(array, &context);
-      mojo::internal::FixedBufferForTesting buf(size);
-      typename mojo::internal::MojomTypeTraits<MojomType>::Data* data;
-      mojo::internal::ContainerValidateParams validate_params(
-          0, false,
-          new mojo::internal::ContainerValidateParams(
-              new mojo::internal::ContainerValidateParams(
-                  0, false, new mojo::internal::ContainerValidateParams(
-                                0, false, nullptr)),
-              new mojo::internal::ContainerValidateParams(
-                  0, false, new mojo::internal::ContainerValidateParams(
-                                0, false, nullptr))));
-      mojo::internal::Serialize<MojomType>(array, &buf, &data, &validate_params,
-                                           &context);
-
-      UserType deserialized_array;
-      mojo::internal::Deserialize<MojomType>(data, &deserialized_array,
-                                             &context);
-
-      ASSERT_EQ(1u, deserialized_array.size());
-      ASSERT_EQ(1u, deserialized_array[0].size());
-      ASSERT_FALSE(deserialized_array[0].at("hello world")[0]);
-      ASSERT_TRUE(deserialized_array[0].at("hello world")[1]);
-    }
-  }
-};
-
-#define MAP_COMMON_TEST(MapType, test_name) \
-  TEST_F(MapType##Test, test_name) { MapCommonTest<MapType>::test_name(); }
-
-}  // namespace test
-}  // namespace mojo
diff --git a/mojo/public/cpp/bindings/tests/map_unittest.cc b/mojo/public/cpp/bindings/tests/map_unittest.cc
deleted file mode 100644
index e4c612a..0000000
--- a/mojo/public/cpp/bindings/tests/map_unittest.cc
+++ /dev/null
@@ -1,271 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "mojo/public/cpp/bindings/map.h"
-
-#include <stddef.h>
-#include <stdint.h>
-#include <unordered_map>
-#include <utility>
-
-#include "mojo/public/cpp/bindings/array.h"
-#include "mojo/public/cpp/bindings/string.h"
-#include "mojo/public/cpp/bindings/tests/container_test_util.h"
-#include "mojo/public/cpp/bindings/tests/map_common_test.h"
-#include "mojo/public/cpp/bindings/tests/rect_chromium.h"
-#include "mojo/public/interfaces/bindings/tests/rect.mojom.h"
-#include "mojo/public/interfaces/bindings/tests/test_structs.mojom.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace mojo {
-namespace test {
-
-namespace {
-
-using MapTest = testing::Test;
-
-MAP_COMMON_TEST(Map, NullAndEmpty)
-MAP_COMMON_TEST(Map, InsertWorks)
-MAP_COMMON_TEST(Map, TestIndexOperator)
-MAP_COMMON_TEST(Map, TestIndexOperatorAsRValue)
-MAP_COMMON_TEST(Map, TestIndexOperatorMoveOnly)
-MAP_COMMON_TEST(Map, MapArrayClone)
-MAP_COMMON_TEST(Map, ArrayOfMap)
-
-TEST_F(MapTest, ConstructedFromArray) {
-  Array<String> keys(kStringIntDataSize);
-  Array<int> values(kStringIntDataSize);
-  for (size_t i = 0; i < kStringIntDataSize; ++i) {
-    keys[i] = kStringIntData[i].string_data;
-    values[i] = kStringIntData[i].int_data;
-  }
-
-  Map<String, int> map(std::move(keys), std::move(values));
-
-  for (size_t i = 0; i < kStringIntDataSize; ++i) {
-    EXPECT_EQ(kStringIntData[i].int_data,
-              map.at(mojo::String(kStringIntData[i].string_data)));
-  }
-}
-
-TEST_F(MapTest, DecomposeMapTo) {
-  Array<String> keys(kStringIntDataSize);
-  Array<int> values(kStringIntDataSize);
-  for (size_t i = 0; i < kStringIntDataSize; ++i) {
-    keys[i] = kStringIntData[i].string_data;
-    values[i] = kStringIntData[i].int_data;
-  }
-
-  Map<String, int> map(std::move(keys), std::move(values));
-  EXPECT_EQ(kStringIntDataSize, map.size());
-
-  Array<String> keys2;
-  Array<int> values2;
-  map.DecomposeMapTo(&keys2, &values2);
-  EXPECT_EQ(0u, map.size());
-
-  EXPECT_EQ(kStringIntDataSize, keys2.size());
-  EXPECT_EQ(kStringIntDataSize, values2.size());
-
-  for (size_t i = 0; i < kStringIntDataSize; ++i) {
-    // We are not guaranteed that the copies have the same sorting as the
-    // originals.
-    String key = kStringIntData[i].string_data;
-    int value = kStringIntData[i].int_data;
-
-    bool found = false;
-    for (size_t j = 0; j < keys2.size(); ++j) {
-      if (keys2[j] == key) {
-        EXPECT_EQ(value, values2[j]);
-        found = true;
-        break;
-      }
-    }
-
-    EXPECT_TRUE(found);
-  }
-}
-
-TEST_F(MapTest, Insert_Copyable) {
-  ASSERT_EQ(0u, CopyableType::num_instances());
-  mojo::Map<mojo::String, CopyableType> map;
-  std::vector<CopyableType*> value_ptrs;
-
-  for (size_t i = 0; i < kStringIntDataSize; ++i) {
-    const char* key = kStringIntData[i].string_data;
-    CopyableType value;
-    value_ptrs.push_back(value.ptr());
-    map.insert(key, value);
-    ASSERT_EQ(i + 1, map.size());
-    ASSERT_EQ(i + 1, value_ptrs.size());
-    EXPECT_EQ(map.size() + 1, CopyableType::num_instances());
-    EXPECT_TRUE(map.at(key).copied());
-    EXPECT_EQ(value_ptrs[i], map.at(key).ptr());
-    map.at(key).ResetCopied();
-    EXPECT_TRUE(map);
-  }
-
-  // std::map doesn't have a capacity() method like std::vector so this test is
-  // a lot more boring.
-
-  map = nullptr;
-  EXPECT_EQ(0u, CopyableType::num_instances());
-}
-
-TEST_F(MapTest, Insert_MoveOnly) {
-  ASSERT_EQ(0u, MoveOnlyType::num_instances());
-  mojo::Map<mojo::String, MoveOnlyType> map;
-  std::vector<MoveOnlyType*> value_ptrs;
-
-  for (size_t i = 0; i < kStringIntDataSize; ++i) {
-    const char* key = kStringIntData[i].string_data;
-    MoveOnlyType value;
-    value_ptrs.push_back(value.ptr());
-    map.insert(key, std::move(value));
-    ASSERT_EQ(i + 1, map.size());
-    ASSERT_EQ(i + 1, value_ptrs.size());
-    EXPECT_EQ(map.size() + 1, MoveOnlyType::num_instances());
-    EXPECT_TRUE(map.at(key).moved());
-    EXPECT_EQ(value_ptrs[i], map.at(key).ptr());
-    map.at(key).ResetMoved();
-    EXPECT_TRUE(map);
-  }
-
-  // std::map doesn't have a capacity() method like std::vector so this test is
-  // a lot more boring.
-
-  map = nullptr;
-  EXPECT_EQ(0u, MoveOnlyType::num_instances());
-}
-
-TEST_F(MapTest, IndexOperator_MoveOnly) {
-  ASSERT_EQ(0u, MoveOnlyType::num_instances());
-  mojo::Map<mojo::String, MoveOnlyType> map;
-  std::vector<MoveOnlyType*> value_ptrs;
-
-  for (size_t i = 0; i < kStringIntDataSize; ++i) {
-    const char* key = kStringIntData[i].string_data;
-    MoveOnlyType value;
-    value_ptrs.push_back(value.ptr());
-    map[key] = std::move(value);
-    ASSERT_EQ(i + 1, map.size());
-    ASSERT_EQ(i + 1, value_ptrs.size());
-    EXPECT_EQ(map.size() + 1, MoveOnlyType::num_instances());
-    EXPECT_TRUE(map.at(key).moved());
-    EXPECT_EQ(value_ptrs[i], map.at(key).ptr());
-    map.at(key).ResetMoved();
-    EXPECT_TRUE(map);
-  }
-
-  // std::map doesn't have a capacity() method like std::vector so this test is
-  // a lot more boring.
-
-  map = nullptr;
-  EXPECT_EQ(0u, MoveOnlyType::num_instances());
-}
-
-TEST_F(MapTest, STLToMojo) {
-  std::map<std::string, int> stl_data;
-  for (size_t i = 0; i < kStringIntDataSize; ++i)
-    stl_data[kStringIntData[i].string_data] = kStringIntData[i].int_data;
-
-  Map<String, int32_t> mojo_data = Map<String, int32_t>::From(stl_data);
-  for (size_t i = 0; i < kStringIntDataSize; ++i) {
-    EXPECT_EQ(kStringIntData[i].int_data,
-              mojo_data.at(kStringIntData[i].string_data));
-  }
-}
-
-TEST_F(MapTest, MojoToSTL) {
-  Map<String, int32_t> mojo_map;
-  for (size_t i = 0; i < kStringIntDataSize; ++i)
-    mojo_map.insert(kStringIntData[i].string_data, kStringIntData[i].int_data);
-
-  std::map<std::string, int> stl_map =
-      mojo_map.To<std::map<std::string, int>>();
-  for (size_t i = 0; i < kStringIntDataSize; ++i) {
-    auto it = stl_map.find(kStringIntData[i].string_data);
-    ASSERT_TRUE(it != stl_map.end());
-    EXPECT_EQ(kStringIntData[i].int_data, it->second);
-  }
-}
-
-TEST_F(MapTest, MoveFromAndToSTLMap_Copyable) {
-  std::map<int32_t, CopyableType> map1;
-  map1.insert(std::make_pair(123, CopyableType()));
-  map1[123].ResetCopied();
-
-  Map<int32_t, CopyableType> mojo_map(std::move(map1));
-  ASSERT_EQ(1u, mojo_map.size());
-  ASSERT_NE(mojo_map.end(), mojo_map.find(123));
-  ASSERT_FALSE(mojo_map[123].copied());
-
-  std::map<int32_t, CopyableType> map2(mojo_map.PassStorage());
-  ASSERT_EQ(1u, map2.size());
-  ASSERT_NE(map2.end(), map2.find(123));
-  ASSERT_FALSE(map2[123].copied());
-
-  ASSERT_EQ(0u, mojo_map.size());
-  ASSERT_TRUE(mojo_map.is_null());
-}
-
-TEST_F(MapTest, MoveFromAndToSTLMap_MoveOnly) {
-  std::map<int32_t, MoveOnlyType> map1;
-  map1.insert(std::make_pair(123, MoveOnlyType()));
-
-  Map<int32_t, MoveOnlyType> mojo_map(std::move(map1));
-  ASSERT_EQ(1u, mojo_map.size());
-  ASSERT_NE(mojo_map.end(), mojo_map.find(123));
-
-  std::map<int32_t, MoveOnlyType> map2(mojo_map.PassStorage());
-  ASSERT_EQ(1u, map2.size());
-  ASSERT_NE(map2.end(), map2.find(123));
-
-  ASSERT_EQ(0u, mojo_map.size());
-  ASSERT_TRUE(mojo_map.is_null());
-}
-
-static RectPtr MakeRect(int32_t x, int32_t y, int32_t width, int32_t height) {
-  RectPtr rect_ptr = Rect::New();
-  rect_ptr->x = x;
-  rect_ptr->y = y;
-  rect_ptr->width = width;
-  rect_ptr->height = height;
-  return rect_ptr;
-}
-
-TEST_F(MapTest, StructKey) {
-  std::unordered_map<RectPtr, int32_t> map;
-  map.insert(std::make_pair(MakeRect(1, 2, 3, 4), 123));
-
-  RectPtr key = MakeRect(1, 2, 3, 4);
-  ASSERT_NE(map.end(), map.find(key));
-  ASSERT_EQ(123, map.find(key)->second);
-
-  map.erase(key);
-  ASSERT_EQ(0u, map.size());
-}
-
-static ContainsHashablePtr MakeContainsHashablePtr(RectChromium rect) {
-  ContainsHashablePtr ptr = ContainsHashable::New();
-  ptr->rect = rect;
-  return ptr;
-}
-
-TEST_F(MapTest, TypemappedStructKey) {
-  std::unordered_map<ContainsHashablePtr, int32_t> map;
-  map.insert(
-      std::make_pair(MakeContainsHashablePtr(RectChromium(1, 2, 3, 4)), 123));
-
-  ContainsHashablePtr key = MakeContainsHashablePtr(RectChromium(1, 2, 3, 4));
-  ASSERT_NE(map.end(), map.find(key));
-  ASSERT_EQ(123, map.find(key)->second);
-
-  map.erase(key);
-  ASSERT_EQ(0u, map.size());
-}
-
-}  // namespace
-}  // namespace test
-}  // namespace mojo
diff --git a/mojo/public/cpp/bindings/tests/stl_converters_unittest.cc b/mojo/public/cpp/bindings/tests/stl_converters_unittest.cc
deleted file mode 100644
index 92a31b35..0000000
--- a/mojo/public/cpp/bindings/tests/stl_converters_unittest.cc
+++ /dev/null
@@ -1,93 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "mojo/public/cpp/bindings/stl_converters.h"
-
-#include <map>
-#include <string>
-#include <vector>
-
-#include "mojo/public/cpp/bindings/array.h"
-#include "mojo/public/cpp/bindings/map.h"
-#include "mojo/public/cpp/bindings/string.h"
-#include "mojo/public/cpp/bindings/tests/container_test_util.h"
-#include "mojo/public/interfaces/bindings/tests/test_structs.mojom.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace mojo {
-namespace test {
-
-using STLConvertersTest = testing::Test;
-
-TEST_F(STLConvertersTest, AvoidUnnecessaryCopies) {
-  Array<CopyableType> mojo_array(1);
-  std::vector<CopyableType> stl_vector = UnwrapToSTLType(std::move(mojo_array));
-  ASSERT_EQ(1u, stl_vector.size());
-  ASSERT_FALSE(stl_vector[0].copied());
-  Array<CopyableType> mojo_array2 = WrapSTLType(std::move(stl_vector));
-  ASSERT_EQ(1u, mojo_array2.size());
-  ASSERT_FALSE(mojo_array2[0].copied());
-
-  Map<int32_t, CopyableType> mojo_map;
-  mojo_map.insert(42, CopyableType());
-  mojo_map[42].ResetCopied();
-  std::map<int32_t, CopyableType> stl_map =
-      UnwrapToSTLType(std::move(mojo_map));
-  ASSERT_EQ(1u, stl_map.size());
-  ASSERT_FALSE(stl_map[42].copied());
-  Map<int32_t, CopyableType> mojo_map2 = WrapSTLType(std::move(stl_map));
-  ASSERT_EQ(1u, mojo_map2.size());
-  ASSERT_FALSE(mojo_map2[42].copied());
-}
-
-TEST_F(STLConvertersTest, RecursiveConversion) {
-  Array<Map<String, Array<int32_t>>> mojo_obj(2);
-  mojo_obj[0] = nullptr;
-  mojo_obj[1]["hello"].push_back(123);
-  mojo_obj[1]["hello"].push_back(456);
-
-  std::vector<std::map<std::string, std::vector<int32_t>>> stl_obj =
-      UnwrapToSTLType(std::move(mojo_obj));
-
-  ASSERT_EQ(2u, stl_obj.size());
-  ASSERT_TRUE(stl_obj[0].empty());
-  ASSERT_EQ(1u, stl_obj[1].size());
-  ASSERT_EQ(2u, stl_obj[1]["hello"].size());
-  ASSERT_EQ(123, stl_obj[1]["hello"][0]);
-  ASSERT_EQ(456, stl_obj[1]["hello"][1]);
-
-  Array<Map<String, Array<int32_t>>> mojo_obj2 =
-      WrapSTLType(std::move(stl_obj));
-
-  ASSERT_EQ(2u, mojo_obj2.size());
-  ASSERT_EQ(0u, mojo_obj2[0].size());
-  // The null flag has been lost when converted to std::map.
-  ASSERT_FALSE(mojo_obj2[0].is_null());
-  ASSERT_EQ(1u, mojo_obj2[1].size());
-  ASSERT_EQ(2u, mojo_obj2[1]["hello"].size());
-  ASSERT_EQ(123, mojo_obj2[1]["hello"][0]);
-  ASSERT_EQ(456, mojo_obj2[1]["hello"][1]);
-}
-
-TEST_F(STLConvertersTest, StopsAtMojoStruct) {
-  Array<NamedRegionPtr> mojo_obj(1);
-  mojo_obj[0] = NamedRegion::New();
-  mojo_obj[0]->name.emplace("hello");
-  mojo_obj[0]->rects.emplace(3);
-
-  std::vector<NamedRegionPtr> stl_obj = UnwrapToSTLType(std::move(mojo_obj));
-
-  ASSERT_EQ(1u, stl_obj.size());
-  ASSERT_EQ("hello", stl_obj[0]->name.value());
-  ASSERT_EQ(3u, stl_obj[0]->rects->size());
-
-  Array<NamedRegionPtr> mojo_obj2 = WrapSTLType(std::move(stl_obj));
-
-  ASSERT_EQ(1u, mojo_obj2.size());
-  ASSERT_EQ("hello", mojo_obj2[0]->name.value());
-  ASSERT_EQ(3u, mojo_obj2[0]->rects->size());
-}
-
-}  // namespace test
-}  // namespace mojo
diff --git a/mojo/public/cpp/bindings/tests/union_unittest.cc b/mojo/public/cpp/bindings/tests/union_unittest.cc
index 29197be..1baccae 100644
--- a/mojo/public/cpp/bindings/tests/union_unittest.cc
+++ b/mojo/public/cpp/bindings/tests/union_unittest.cc
@@ -693,9 +693,9 @@
 TEST(UnionTest, PodUnionInMapSerialization) {
   using MojomType = MapDataView<StringDataView, PodUnionDataView>;
 
-  Map<String, PodUnionPtr> map;
-  map.insert("one", PodUnion::New());
-  map.insert("two", PodUnion::New());
+  std::unordered_map<std::string, PodUnionPtr> map;
+  map.insert(std::make_pair("one", PodUnion::New()));
+  map.insert(std::make_pair("two", PodUnion::New()));
 
   map["one"]->set_f_int8(8);
   map["two"]->set_f_int16(16);
@@ -713,7 +713,7 @@
   mojo::internal::Serialize<MojomType>(map, &buf, &data, &validate_params,
                                        &context);
 
-  Map<String, PodUnionPtr> map2;
+  std::unordered_map<std::string, PodUnionPtr> map2;
   mojo::internal::Deserialize<MojomType>(data, &map2, &context);
 
   EXPECT_EQ(8, map2["one"]->get_f_int8());
@@ -723,9 +723,9 @@
 TEST(UnionTest, PodUnionInMapSerializationWithNull) {
   using MojomType = MapDataView<StringDataView, PodUnionDataView>;
 
-  Map<String, PodUnionPtr> map;
-  map.insert("one", PodUnion::New());
-  map.insert("two", nullptr);
+  std::unordered_map<std::string, PodUnionPtr> map;
+  map.insert(std::make_pair("one", PodUnion::New()));
+  map.insert(std::make_pair("two", nullptr));
 
   map["one"]->set_f_int8(8);
 
@@ -741,7 +741,7 @@
   mojo::internal::Serialize<MojomType>(map, &buf, &data, &validate_params,
                                        &context);
 
-  Map<String, PodUnionPtr> map2;
+  std::unordered_map<std::string, PodUnionPtr> map2;
   mojo::internal::Deserialize<MojomType>(data, &map2, &context);
 
   EXPECT_EQ(8, map2["one"]->get_f_int8());
diff --git a/mojo/public/tools/bindings/generators/cpp_templates/module.h.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/module.h.tmpl
index 7d4cc7b..a2a18ed 100644
--- a/mojo/public/tools/bindings/generators/cpp_templates/module.h.tmpl
+++ b/mojo/public/tools/bindings/generators/cpp_templates/module.h.tmpl
@@ -49,7 +49,6 @@
 #include "mojo/public/cpp/bindings/lib/control_message_proxy.h"
 #include "mojo/public/cpp/bindings/lib/serialization.h"
 #include "mojo/public/cpp/bindings/lib/union_accessor.h"
-#include "mojo/public/cpp/bindings/map.h"
 #include "mojo/public/cpp/bindings/native_struct.h"
 #include "mojo/public/cpp/bindings/no_interface.h"
 #include "mojo/public/cpp/bindings/raw_ptr_impl_ref_traits.h"
diff --git a/mojo/public/tools/bindings/mojom.gni b/mojo/public/tools/bindings/mojom.gni
index a30e10c..1b1acc82 100644
--- a/mojo/public/tools/bindings/mojom.gni
+++ b/mojo/public/tools/bindings/mojom.gni
@@ -241,9 +241,6 @@
     type_mappings_path =
         "$target_gen_dir/${target_name}${variant_suffix}__type_mappings"
     active_typemaps = []
-    cpp_sources_suffix = "cpp_sources"
-    cpp_sources_target_name =
-        "${target_name}${variant_suffix}_${cpp_sources_suffix}"
     enabled_sources = []
     if (defined(invoker.sources)) {
       generator_cpp_outputs = []
@@ -424,37 +421,6 @@
       if (defined(invoker.sources) && !defined(bindings_configuration.variant)) {
         data = process_file_template(enabled_sources, generator_js_outputs)
       }
-
-      public_deps = [
-        ":${cpp_sources_target_name}",
-        "//mojo/public/cpp/bindings",
-      ]
-      if (defined(invoker.deps)) {
-        foreach(dep, invoker.deps) {
-          public_deps +=
-              [ get_label_info(dep, "label_no_toolchain") + variant_suffix ]
-        }
-      }
-      if (defined(invoker.public_deps)) {
-        foreach(dep, invoker.public_deps) {
-          public_deps +=
-              [ get_label_info(dep, "label_no_toolchain") + variant_suffix ]
-        }
-      }
-
-      deps = []
-      if (defined(invoker.sources)) {
-        public_deps += [ ":$generator_target_name" ]
-      }
-    }
-
-    # The generated C++ source files. The main reason to introduce this target
-    # is so that mojo/public/cpp/bindings can depend on mojom interfaces without
-    # circular dependencies. It means that the target is missing the dependency
-    # on mojo/public/cpp/bindings. No external targets should depend directly on
-    # this target *except* mojo/public/cpp/bindings and other *_cpp_sources
-    # targets.
-    source_set(cpp_sources_target_name) {
       defines = []
       if (defined(invoker.testonly)) {
         testonly = invoker.testonly
@@ -473,19 +439,20 @@
         "//mojo/public/interfaces/bindings:bindings__generator",
         "//mojo/public/interfaces/bindings:bindings_shared__generator",
       ]
-      if (enabled_sources != []) {
-        deps += [ ":$generator_target_name" ]
-      }
       public_deps = [
         ":$shared_cpp_sources_target_name",
         "//base",
+        "//mojo/public/cpp/bindings",
       ]
+      if (enabled_sources != []) {
+        public_deps += [ ":$generator_target_name" ]
+      }
       foreach(d, all_deps) {
         # Resolve the name, so that a target //mojo/something becomes
-        # //mojo/something:something and we can append cpp_sources_suffix to
+        # //mojo/something:something and we can append variant_suffix to
         # get the cpp dependency name.
         full_name = get_label_info("$d", "label_no_toolchain")
-        public_deps += [ "${full_name}${variant_suffix}_${cpp_sources_suffix}" ]
+        public_deps += [ "${full_name}${variant_suffix}" ]
       }
       foreach(typemap, active_typemaps) {
         if (defined(typemap.public_headers)) {
diff --git a/net/base/sdch_manager.cc b/net/base/sdch_manager.cc
index e57d73c..d577db84 100644
--- a/net/base/sdch_manager.cc
+++ b/net/base/sdch_manager.cc
@@ -13,7 +13,10 @@
 #include "base/metrics/histogram_macros.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/string_util.h"
+#include "base/strings/stringprintf.h"
 #include "base/time/default_clock.h"
+#include "base/trace_event/memory_allocator_dump.h"
+#include "base/trace_event/process_memory_dump.h"
 #include "base/values.h"
 #include "crypto/sha2.h"
 #include "net/base/parse_number.h"
@@ -324,6 +327,36 @@
   observers_.RemoveObserver(observer);
 }
 
+void SdchManager::DumpMemoryStats(
+    base::trace_event::ProcessMemoryDump* pmd,
+    const std::string& parent_dump_absolute_name) const {
+  // If there are no dictionaries stored, return early without creating a new
+  // MemoryAllocatorDump.
+  size_t total_count = dictionaries_.size();
+  if (total_count == 0)
+    return;
+  std::string name = base::StringPrintf("net/sdch_manager_%p", this);
+  base::trace_event::MemoryAllocatorDump* dump = pmd->GetAllocatorDump(name);
+  if (dump == nullptr) {
+    dump = pmd->CreateAllocatorDump(name);
+    size_t total_size = 0;
+    for (const auto& dictionary : dictionaries_) {
+      total_size += dictionary.second->data.text().size();
+    }
+    dump->AddScalar(base::trace_event::MemoryAllocatorDump::kNameSize,
+                    base::trace_event::MemoryAllocatorDump::kUnitsBytes,
+                    total_size);
+    dump->AddScalar(base::trace_event::MemoryAllocatorDump::kNameObjectCount,
+                    base::trace_event::MemoryAllocatorDump::kUnitsObjects,
+                    total_count);
+  }
+  // Create an empty row under parent's dump so size can be attributed correctly
+  // if |this| is shared between URLRequestContexts.
+  base::trace_event::MemoryAllocatorDump* empty_row_dump =
+      pmd->CreateAllocatorDump(parent_dump_absolute_name + "/sdch_manager");
+  pmd->AddOwnershipEdge(empty_row_dump->guid(), dump->guid());
+}
+
 SdchProblemCode SdchManager::AddSdchDictionary(
     const std::string& dictionary_text,
     const GURL& dictionary_url,
diff --git a/net/base/sdch_manager.h b/net/base/sdch_manager.h
index dcb4771..1db8105e 100644
--- a/net/base/sdch_manager.h
+++ b/net/base/sdch_manager.h
@@ -32,6 +32,10 @@
 
 namespace base {
 class Value;
+
+namespace trace_event {
+class ProcessMemoryDump;
+}
 }
 
 namespace net {
@@ -195,6 +199,11 @@
   void AddObserver(SdchObserver* observer);
   void RemoveObserver(SdchObserver* observer);
 
+  // Dumps memory allocation stats. |parent_dump_absolute_name| is the name
+  // used by the parent MemoryAllocatorDump in the memory dump hierarchy.
+  void DumpMemoryStats(base::trace_event::ProcessMemoryDump* pmd,
+                       const std::string& parent_dump_absolute_name) const;
+
   // Logs an SDCH failure to UMA and |netlog|.
   static void LogSdchProblem(NetLogWithSource netlog, SdchProblemCode problem);
 
diff --git a/net/base/sdch_manager_unittest.cc b/net/base/sdch_manager_unittest.cc
index bfdb78f..31162841 100644
--- a/net/base/sdch_manager_unittest.cc
+++ b/net/base/sdch_manager_unittest.cc
@@ -11,8 +11,12 @@
 
 #include "base/logging.h"
 #include "base/macros.h"
+#include "base/strings/string_number_conversions.h"
 #include "base/strings/stringprintf.h"
 #include "base/test/simple_test_clock.h"
+#include "base/trace_event/memory_allocator_dump.h"
+#include "base/trace_event/process_memory_dump.h"
+#include "base/trace_event/trace_event_argument.h"
 #include "net/base/sdch_observer.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "url/gurl.h"
@@ -636,4 +640,58 @@
   sdch_manager()->RemoveObserver(&observer);
 }
 
+TEST_F(SdchManagerTest, DumpMemoryStats) {
+  MockSdchObserver observer;
+  sdch_manager()->AddObserver(&observer);
+
+  std::string dictionary_domain("x.y.z.google.com");
+  GURL target_gurl("http://" + dictionary_domain);
+  std::string dictionary_text(NewSdchDictionary(dictionary_domain));
+  std::string client_hash;
+  std::string server_hash;
+  SdchManager::GenerateHash(dictionary_text, &client_hash, &server_hash);
+  EXPECT_TRUE(AddSdchDictionary(dictionary_text, target_gurl));
+  EXPECT_EQ(1, observer.dictionary_added_notifications());
+  EXPECT_EQ(target_gurl, observer.last_dictionary_url());
+  EXPECT_EQ(server_hash, observer.last_server_hash());
+
+  base::trace_event::MemoryDumpArgs dump_args = {
+      base::trace_event::MemoryDumpLevelOfDetail::DETAILED};
+  std::unique_ptr<base::trace_event::ProcessMemoryDump> pmd(
+      new base::trace_event::ProcessMemoryDump(nullptr, dump_args));
+
+  base::trace_event::MemoryAllocatorDump* parent =
+      pmd->CreateAllocatorDump("parent");
+  sdch_manager()->DumpMemoryStats(pmd.get(), parent->absolute_name());
+
+  const base::trace_event::MemoryAllocatorDump* sub_dump =
+      pmd->GetAllocatorDump("parent/sdch_manager");
+  ASSERT_NE(nullptr, sub_dump);
+  const base::trace_event::MemoryAllocatorDump* dump = pmd->GetAllocatorDump(
+      base::StringPrintf("net/sdch_manager_%p", sdch_manager()));
+  std::unique_ptr<base::Value> raw_attrs =
+      dump->attributes_for_testing()->ToBaseValue();
+  base::DictionaryValue* attrs;
+  ASSERT_TRUE(raw_attrs->GetAsDictionary(&attrs));
+  base::DictionaryValue* size_attrs;
+  ASSERT_TRUE(attrs->GetDictionary(
+      base::trace_event::MemoryAllocatorDump::kNameSize, &size_attrs));
+  size_t offset = dictionary_text.find("\n\n") + 2;
+  std::string size;
+  ASSERT_TRUE(size_attrs->GetString("value", &size));
+  int actual_size;
+  ASSERT_TRUE(base::HexStringToInt(size, &actual_size));
+  EXPECT_EQ(dictionary_text.size() - offset, static_cast<size_t>(actual_size));
+
+  base::DictionaryValue* count_attrs;
+  ASSERT_TRUE(attrs->GetDictionary(
+      base::trace_event::MemoryAllocatorDump::kNameObjectCount, &count_attrs));
+  std::string count;
+  ASSERT_TRUE(count_attrs->GetString("value", &count));
+  // One dictionary.
+  EXPECT_EQ("1", count);
+
+  sdch_manager()->RemoveObserver(&observer);
+}
+
 }  // namespace net
diff --git a/net/ssl/ssl_config.cc b/net/ssl/ssl_config.cc
index 7a4f337..3275ac4 100644
--- a/net/ssl/ssl_config.cc
+++ b/net/ssl/ssl_config.cc
@@ -23,7 +23,7 @@
 SSLConfig::SSLConfig()
     : rev_checking_enabled(false),
       rev_checking_required_local_anchors(false),
-      sha1_local_anchors_enabled(false),
+      sha1_local_anchors_enabled(true),
       version_min(kDefaultSSLVersionMin),
       version_max(kDefaultSSLVersionMax),
       deprecated_cipher_suites_enabled(false),
diff --git a/net/url_request/url_request_context.cc b/net/url_request/url_request_context.cc
index ab6ad531..cefa7b9 100644
--- a/net/url_request/url_request_context.cc
+++ b/net/url_request/url_request_context.cc
@@ -13,6 +13,7 @@
 #include "base/trace_event/memory_allocator_dump.h"
 #include "base/trace_event/memory_dump_manager.h"
 #include "base/trace_event/process_memory_dump.h"
+#include "net/base/sdch_manager.h"
 #include "net/cookies/cookie_store.h"
 #include "net/dns/host_resolver.h"
 #include "net/http/http_transaction_factory.h"
@@ -138,6 +139,8 @@
       network_session->DumpMemoryStats(pmd, dump->absolute_name());
   }
   SSLClientSocketImpl::DumpSSLClientSessionMemoryStats(pmd);
+  if (sdch_manager_)
+    sdch_manager_->DumpMemoryStats(pmd, dump->absolute_name());
   return true;
 }
 
diff --git a/ppapi/shared_impl/ppb_gamepad_shared.cc b/ppapi/shared_impl/ppb_gamepad_shared.cc
index a707926..4ee0dd9 100644
--- a/ppapi/shared_impl/ppb_gamepad_shared.cc
+++ b/ppapi/shared_impl/ppb_gamepad_shared.cc
@@ -14,10 +14,8 @@
 
 void ConvertWebKitGamepadData(const WebKitGamepads& webkit_data,
                               PP_GamepadsSampleData* output_data) {
-  size_t length = std::min(WebKitGamepads::kItemsLengthCap,
-                           static_cast<const size_t>(webkit_data.length));
-  output_data->length = static_cast<unsigned>(length);
-  for (unsigned i = 0; i < length; ++i) {
+  output_data->length = WebKitGamepads::kItemsLengthCap;
+  for (unsigned i = 0; i < WebKitGamepads::kItemsLengthCap; ++i) {
     PP_GamepadSampleData& output_pad = output_data->items[i];
     const WebKitGamepad& webkit_pad = webkit_data.items[i];
     output_pad.connected = webkit_pad.connected ? PP_TRUE : PP_FALSE;
diff --git a/ppapi/shared_impl/ppb_gamepad_shared.h b/ppapi/shared_impl/ppb_gamepad_shared.h
index 4a71d964..577a13d 100644
--- a/ppapi/shared_impl/ppb_gamepad_shared.h
+++ b/ppapi/shared_impl/ppb_gamepad_shared.h
@@ -101,9 +101,6 @@
 struct WebKitGamepads {
   static const size_t kItemsLengthCap = 4;
 
-  // Number of valid entries in the items array.
-  unsigned length;
-
   // Gamepad data for N separate gamepad devices.
   WebKitGamepad items[kItemsLengthCap];
 };
diff --git a/services/service_manager/public/cpp/BUILD.gn b/services/service_manager/public/cpp/BUILD.gn
index a73de27..2d68cd9 100644
--- a/services/service_manager/public/cpp/BUILD.gn
+++ b/services/service_manager/public/cpp/BUILD.gn
@@ -87,7 +87,7 @@
       "//mojo/public/cpp/bindings",
       "//mojo/public/cpp/system",
       "//services/service_manager/background:lib",
-      "//services/service_manager/public/interfaces:interfaces_cpp_sources",
+      "//services/service_manager/public/interfaces",
     ]
 
     data_deps = []
diff --git a/services/ui/public/cpp/window_tree_client.cc b/services/ui/public/cpp/window_tree_client.cc
index ecbc628..eb96c58 100644
--- a/services/ui/public/cpp/window_tree_client.cc
+++ b/services/ui/public/cpp/window_tree_client.cc
@@ -13,6 +13,7 @@
 #include "base/bind.h"
 #include "base/memory/ptr_util.h"
 #include "base/strings/stringprintf.h"
+#include "mojo/public/cpp/bindings/map.h"
 #include "services/service_manager/public/cpp/connector.h"
 #include "services/ui/common/util.h"
 #include "services/ui/public/cpp/in_flight_change.h"
diff --git a/services/ui/ws/drag_controller.cc b/services/ui/ws/drag_controller.cc
index ba891a3..2e05d26e 100644
--- a/services/ui/ws/drag_controller.cc
+++ b/services/ui/ws/drag_controller.cc
@@ -46,7 +46,7 @@
     ServerWindow* source_window,
     DragTargetConnection* source_connection,
     int32_t drag_pointer,
-    mojo::Map<mojo::String, mojo::Array<uint8_t>> mime_data,
+    const std::unordered_map<std::string, std::vector<uint8_t>>& mime_data,
     DropEffectBitmask drag_operations)
     : source_(source),
       cursor_updater_(cursor_updater),
@@ -55,7 +55,7 @@
       current_cursor_(ui::mojom::Cursor::NO_DROP),
       source_window_(source_window),
       source_connection_(source_connection),
-      mime_data_(std::move(mime_data)),
+      mime_data_(mime_data),
       weak_factory_(this) {
   SetCurrentTargetWindow(nullptr);
   EnsureWindowObserved(source_window_);
@@ -216,7 +216,7 @@
   DragTargetConnection* connection = source_->GetDragTargetForWindow(window);
   if (connection != source_connection_ &&
       !base::ContainsKey(called_on_drag_mime_types_, connection)) {
-    connection->PerformOnDragDropStart(mime_data_.Clone());
+    connection->PerformOnDragDropStart(mime_data_);
     called_on_drag_mime_types_.insert(connection);
   }
 
diff --git a/services/ui/ws/drag_controller.h b/services/ui/ws/drag_controller.h
index dd2b292d..0057480 100644
--- a/services/ui/ws/drag_controller.h
+++ b/services/ui/ws/drag_controller.h
@@ -37,13 +37,14 @@
 // WindowManagerState's EventDispatcher creates and owns this instance.
 class DragController : public ServerWindowObserver {
  public:
-  DragController(DragCursorUpdater* cursor_updater,
-                 DragSource* source,
-                 ServerWindow* source_window,
-                 DragTargetConnection* source_connection,
-                 int32_t drag_pointer,
-                 mojo::Map<mojo::String, mojo::Array<uint8_t>> mime_data,
-                 DropEffectBitmask drag_operations);
+  DragController(
+      DragCursorUpdater* cursor_updater,
+      DragSource* source,
+      ServerWindow* source_window,
+      DragTargetConnection* source_connection,
+      int32_t drag_pointer,
+      const std::unordered_map<std::string, std::vector<uint8_t>>& mime_data,
+      DropEffectBitmask drag_operations);
   ~DragController() override;
 
   ui::mojom::Cursor current_cursor() const { return current_cursor_; }
@@ -130,7 +131,7 @@
   DragTargetConnection* source_connection_;
 
   // A list of the offered mime types.
-  mojo::Map<mojo::String, mojo::Array<uint8_t>> mime_data_;
+  std::unordered_map<std::string, std::vector<uint8_t>> mime_data_;
 
   // We need to keep track of state on a per window basis. A window being in
   // this map means that we're observing it. WindowState also keeps track of
diff --git a/services/ui/ws/drag_controller_unittest.cc b/services/ui/ws/drag_controller_unittest.cc
index 0841976..d26fdcd 100644
--- a/services/ui/ws/drag_controller_unittest.cc
+++ b/services/ui/ws/drag_controller_unittest.cc
@@ -79,9 +79,10 @@
 
   // Overridden from DragTestConnection:
   void PerformOnDragDropStart(
-      mojo::Map<mojo::String, mojo::Array<uint8_t>> mime_data) override {
+      const std::unordered_map<std::string, std::vector<uint8_t>>& mime_data)
+      override {
     times_received_drag_drop_start_++;
-    mime_data_ = std::move(mime_data);
+    mime_data_ = mime_data;
   }
 
   void PerformOnDragEnter(
@@ -123,13 +124,13 @@
         {QueuedType::DROP, key_state, cursor_offset, effect_bitmask, callback});
   }
 
-  void PerformOnDragDropDone() override { mime_data_.SetToEmpty(); }
+  void PerformOnDragDropDone() override { mime_data_.clear(); }
 
  private:
   DragControllerTest* parent_;
   TestServerWindowDelegate window_delegate_;
   ServerWindow window_;
-  mojo::Map<mojo::String, mojo::Array<uint8_t>> mime_data_;
+  std::unordered_map<std::string, std::vector<uint8_t>> mime_data_;
   uint32_t times_received_drag_drop_start_ = 0;
 
   std::queue<DragEvent> queued_callbacks_;
@@ -149,13 +150,14 @@
   }
 
   void StartDragOperation(
-      mojo::Map<mojo::String, mojo::Array<uint8_t>> mime_data,
       DragTestWindow* window,
       uint32_t drag_operations) {
-    window->PerformOnDragDropStart(mime_data.Clone());
+    window->PerformOnDragDropStart(
+        std::unordered_map<std::string, std::vector<uint8_t>>());
     drag_operation_ = base::MakeUnique<DragController>(
         this, this, window->window(), window, PointerEvent::kMousePointerId,
-        std::move(mime_data), drag_operations);
+        std::unordered_map<std::string, std::vector<uint8_t>>(),
+        drag_operations);
 
     // It would be nice if we could just let the observer method fire, but it
     // fires during the constructor when we haven't assigned the unique_ptr
@@ -275,9 +277,7 @@
 
 TEST_F(DragControllerTest, SimpleDragDrop) {
   std::unique_ptr<DragTestWindow> window = BuildWindow();
-  mojo::Map<mojo::String, mojo::Array<uint8_t>> mime_data;
-  StartDragOperation(std::move(mime_data), window.get(),
-                     ui::mojom::kDropEffectMove);
+  StartDragOperation(window.get(), ui::mojom::kDropEffectMove);
 
   EXPECT_EQ(ui::mojom::Cursor::NO_DROP, cursor());
 
@@ -303,9 +303,7 @@
 
 TEST_F(DragControllerTest, FailsOnWindowSayingNo) {
   std::unique_ptr<DragTestWindow> window = BuildWindow();
-  mojo::Map<mojo::String, mojo::Array<uint8_t>> mime_data;
-  StartDragOperation(std::move(mime_data), window.get(),
-                     ui::mojom::kDropEffectMove);
+  StartDragOperation(window.get(), ui::mojom::kDropEffectMove);
 
   DispatchDrag(window.get(), false, ui::EF_LEFT_MOUSE_BUTTON, gfx::Point(1, 1));
   EXPECT_EQ(QueuedType::ENTER, window->queue_response_type());
@@ -330,13 +328,11 @@
 TEST_F(DragControllerTest, OnlyDeliverMimeDataOnce) {
   std::unique_ptr<DragTestWindow> window1 = BuildWindow();
   std::unique_ptr<DragTestWindow> window2 = BuildWindow();
-  mojo::Map<mojo::String, mojo::Array<uint8_t>> mime_data;
 
   // The client lib is responsible for sending the data to the window that's
   // the drag source to minimize IPC.
   EXPECT_EQ(0u, window1->times_received_drag_drop_start());
-  StartDragOperation(std::move(mime_data), window1.get(),
-                     ui::mojom::kDropEffectMove);
+  StartDragOperation(window1.get(), ui::mojom::kDropEffectMove);
   EXPECT_EQ(1u, window1->times_received_drag_drop_start());
   DispatchDrag(window1.get(), false, ui::EF_LEFT_MOUSE_BUTTON,
                gfx::Point(1, 1));
@@ -364,13 +360,11 @@
   std::unique_ptr<DragTestWindow> window1 = BuildWindow();
   std::unique_ptr<DragTestWindow> window2 = BuildWindow();
   std::unique_ptr<DragTestWindow> window3 = BuildWindow();
-  mojo::Map<mojo::String, mojo::Array<uint8_t>> mime_data;
 
   window3->SetParent(window2.get());
   window3->OptOutOfDrag();
 
-  StartDragOperation(std::move(mime_data), window1.get(),
-                     ui::mojom::kDropEffectMove);
+  StartDragOperation(window1.get(), ui::mojom::kDropEffectMove);
 
   // Dispatching a drag to window3 (which has can accept drags off) redirects
   // to window2, which is its parent.
@@ -381,9 +375,7 @@
 
 TEST_F(DragControllerTest, FailWhenDropOverNoWindow) {
   std::unique_ptr<DragTestWindow> window = BuildWindow();
-  mojo::Map<mojo::String, mojo::Array<uint8_t>> mime_data;
-  StartDragOperation(std::move(mime_data), window.get(),
-                     ui::mojom::kDropEffectMove);
+  StartDragOperation(window.get(), ui::mojom::kDropEffectMove);
 
   DispatchDrag(window.get(), false, ui::EF_LEFT_MOUSE_BUTTON, gfx::Point(1, 1));
   EXPECT_EQ(QueuedType::ENTER, window->queue_response_type());
@@ -404,9 +396,7 @@
 TEST_F(DragControllerTest, EnterLeaveWhenMovingBetweenTwoWindows) {
   std::unique_ptr<DragTestWindow> window1 = BuildWindow();
   std::unique_ptr<DragTestWindow> window2 = BuildWindow();
-  mojo::Map<mojo::String, mojo::Array<uint8_t>> mime_data;
-  StartDragOperation(std::move(mime_data), window1.get(),
-                     ui::mojom::kDropEffectMove);
+  StartDragOperation(window1.get(), ui::mojom::kDropEffectMove);
 
   DispatchDrag(window1.get(), false, ui::EF_LEFT_MOUSE_BUTTON,
                gfx::Point(1, 1));
@@ -424,9 +414,7 @@
 TEST_F(DragControllerTest, DeadWindowDoesntBlock) {
   std::unique_ptr<DragTestWindow> window1 = BuildWindow();
   std::unique_ptr<DragTestWindow> window2 = BuildWindow();
-  mojo::Map<mojo::String, mojo::Array<uint8_t>> mime_data;
-  StartDragOperation(std::move(mime_data), window1.get(),
-                     ui::mojom::kDropEffectMove);
+  StartDragOperation(window1.get(), ui::mojom::kDropEffectMove);
 
   test::DragControllerTestApi api(drag_operation());
 
@@ -448,9 +436,7 @@
 
 TEST_F(DragControllerTest, EnterToOverQueued) {
   std::unique_ptr<DragTestWindow> window = BuildWindow();
-  mojo::Map<mojo::String, mojo::Array<uint8_t>> mime_data;
-  StartDragOperation(std::move(mime_data), window.get(),
-                     ui::mojom::kDropEffectMove);
+  StartDragOperation(window.get(), ui::mojom::kDropEffectMove);
 
   DispatchDrag(window.get(), false, ui::EF_LEFT_MOUSE_BUTTON, gfx::Point(1, 1));
   ASSERT_EQ(1u, window->queue_size());
@@ -469,9 +455,7 @@
 
 TEST_F(DragControllerTest, CoalesceMouseOverEvents) {
   std::unique_ptr<DragTestWindow> window = BuildWindow();
-  mojo::Map<mojo::String, mojo::Array<uint8_t>> mime_data;
-  StartDragOperation(std::move(mime_data), window.get(),
-                     ui::mojom::kDropEffectMove);
+  StartDragOperation(window.get(), ui::mojom::kDropEffectMove);
 
   DispatchDrag(window.get(), false, ui::EF_LEFT_MOUSE_BUTTON, gfx::Point(1, 1));
   EXPECT_EQ(QueuedType::ENTER, window->queue_response_type());
@@ -494,9 +478,7 @@
 TEST_F(DragControllerTest, RemovePendingMouseOversOnLeave) {
   std::unique_ptr<DragTestWindow> window1 = BuildWindow();
   std::unique_ptr<DragTestWindow> window2 = BuildWindow();
-  mojo::Map<mojo::String, mojo::Array<uint8_t>> mime_data;
-  StartDragOperation(std::move(mime_data), window1.get(),
-                     ui::mojom::kDropEffectMove);
+  StartDragOperation(window1.get(), ui::mojom::kDropEffectMove);
 
   // Enter
   DispatchDrag(window1.get(), false, ui::EF_LEFT_MOUSE_BUTTON,
@@ -521,9 +503,7 @@
 TEST_F(DragControllerTest, TargetWindowClosedWhileDrag) {
   std::unique_ptr<DragTestWindow> window1 = BuildWindow();
   std::unique_ptr<DragTestWindow> window2 = BuildWindow();
-  mojo::Map<mojo::String, mojo::Array<uint8_t>> mime_data;
-  StartDragOperation(std::move(mime_data), window1.get(),
-                     ui::mojom::kDropEffectMove);
+  StartDragOperation(window1.get(), ui::mojom::kDropEffectMove);
 
   test::DragControllerTestApi api(drag_operation());
 
@@ -554,9 +534,7 @@
 TEST_F(DragControllerTest, TargetWindowClosedResetsCursor) {
   std::unique_ptr<DragTestWindow> window1 = BuildWindow();
   std::unique_ptr<DragTestWindow> window2 = BuildWindow();
-  mojo::Map<mojo::String, mojo::Array<uint8_t>> mime_data;
-  StartDragOperation(std::move(mime_data), window1.get(),
-                     ui::mojom::kDropEffectMove);
+  StartDragOperation(window1.get(), ui::mojom::kDropEffectMove);
   EXPECT_EQ(ui::mojom::Cursor::NO_DROP, cursor());
 
   // Send some events to |window|.
@@ -579,9 +557,7 @@
 TEST_F(DragControllerTest, SourceWindowClosedWhileDrag) {
   std::unique_ptr<DragTestWindow> window1 = BuildWindow();
   std::unique_ptr<DragTestWindow> window2 = BuildWindow();
-  mojo::Map<mojo::String, mojo::Array<uint8_t>> mime_data;
-  StartDragOperation(std::move(mime_data), window1.get(),
-                     ui::mojom::kDropEffectMove);
+  StartDragOperation(window1.get(), ui::mojom::kDropEffectMove);
 
   test::DragControllerTestApi api(drag_operation());
 
@@ -609,9 +585,7 @@
   // The DragController needs to stick around to coordinate the drop, but
   // it should ignore further mouse events during this time.
   std::unique_ptr<DragTestWindow> window = BuildWindow();
-  mojo::Map<mojo::String, mojo::Array<uint8_t>> mime_data;
-  StartDragOperation(std::move(mime_data), window.get(),
-                     ui::mojom::kDropEffectMove);
+  StartDragOperation(window.get(), ui::mojom::kDropEffectMove);
 
   test::DragControllerTestApi api(drag_operation());
 
@@ -637,9 +611,7 @@
   // The DragController needs to stick around to coordinate the drop, but
   // it should ignore further mouse events during this time.
   std::unique_ptr<DragTestWindow> window = BuildWindow();
-  mojo::Map<mojo::String, mojo::Array<uint8_t>> mime_data;
-  StartDragOperation(std::move(mime_data), window.get(),
-                     ui::mojom::kDropEffectMove);
+  StartDragOperation(window.get(), ui::mojom::kDropEffectMove);
 
   DispatchDrag(window.get(), false, ui::EF_LEFT_MOUSE_BUTTON, gfx::Point(1, 1));
   EXPECT_EQ(QueuedType::ENTER, window->queue_response_type());
@@ -652,10 +624,8 @@
 
 TEST_F(DragControllerTest, IgnoreEventsFromOtherPointers) {
   std::unique_ptr<DragTestWindow> window = BuildWindow();
-  mojo::Map<mojo::String, mojo::Array<uint8_t>> mime_data;
   // This starts the operation with PointerEvent::kMousePointerId.
-  StartDragOperation(std::move(mime_data), window.get(),
-                     ui::mojom::kDropEffectMove);
+  StartDragOperation(window.get(), ui::mojom::kDropEffectMove);
 
   // Ignore events from pointer 5.
   DispatchDragWithPointer(window.get(), 5, false, ui::EF_LEFT_MOUSE_BUTTON,
@@ -665,9 +635,7 @@
 
 TEST_F(DragControllerTest, RejectingWindowHasProperCursor) {
   std::unique_ptr<DragTestWindow> window = BuildWindow();
-  mojo::Map<mojo::String, mojo::Array<uint8_t>> mime_data;
-  StartDragOperation(std::move(mime_data), window.get(),
-                     ui::mojom::kDropEffectMove);
+  StartDragOperation(window.get(), ui::mojom::kDropEffectMove);
 
   EXPECT_EQ(ui::mojom::Cursor::NO_DROP, cursor());
 
@@ -688,9 +656,7 @@
 TEST_F(DragControllerTest, ResopnseFromOtherWindowDoesntChangeCursor) {
   std::unique_ptr<DragTestWindow> window1 = BuildWindow();
   std::unique_ptr<DragTestWindow> window2 = BuildWindow();
-  mojo::Map<mojo::String, mojo::Array<uint8_t>> mime_data;
-  StartDragOperation(std::move(mime_data), window1.get(),
-                     ui::mojom::kDropEffectMove);
+  StartDragOperation(window1.get(), ui::mojom::kDropEffectMove);
 
   // Send some events to |window2|.
   DispatchDrag(window2.get(), false, ui::EF_LEFT_MOUSE_BUTTON,
diff --git a/services/ui/ws/drag_target_connection.h b/services/ui/ws/drag_target_connection.h
index d3c22ce..a1fd04d 100644
--- a/services/ui/ws/drag_target_connection.h
+++ b/services/ui/ws/drag_target_connection.h
@@ -5,10 +5,11 @@
 #ifndef SERVICES_UI_WS_DRAG_TARGET_CONNECTION_H_
 #define SERVICES_UI_WS_DRAG_TARGET_CONNECTION_H_
 
+#include <string>
+#include <unordered_map>
+
 #include "base/bind.h"
 #include "mojo/public/cpp/bindings/array.h"
-#include "mojo/public/cpp/bindings/map.h"
-#include "mojo/public/cpp/bindings/string.h"
 #include "ui/gfx/geometry/point.h"
 
 namespace ui {
@@ -37,7 +38,8 @@
   // equivalent in ui::WindowDropTarget to minimize the load of inter-process
   // communication.)
   virtual void PerformOnDragDropStart(
-      mojo::Map<mojo::String, mojo::Array<uint8_t>> mime_data) = 0;
+      const std::unordered_map<std::string, std::vector<uint8_t>>&
+          mime_data) = 0;
 
   // Next, on each time that the mouse cursor moves from one |window| to
   // another, we send a DragEnter message. The value returned by |callback| is
diff --git a/services/ui/ws/event_dispatcher.cc b/services/ui/ws/event_dispatcher.cc
index 98d601c66..485bb5e1 100644
--- a/services/ui/ws/event_dispatcher.cc
+++ b/services/ui/ws/event_dispatcher.cc
@@ -174,12 +174,12 @@
     ServerWindow* window,
     DragTargetConnection* source_connection,
     int32_t drag_pointer,
-    mojo::Map<mojo::String, mojo::Array<uint8_t>> mime_data,
+    const std::unordered_map<std::string, std::vector<uint8_t>>& mime_data,
     uint32_t drag_operations) {
   CancelImplicitCaptureExcept(nullptr);
   drag_controller_ = base::MakeUnique<DragController>(
-      this, drag_source, window, source_connection, drag_pointer,
-      std::move(mime_data), drag_operations);
+      this, drag_source, window, source_connection, drag_pointer, mime_data,
+      drag_operations);
 }
 
 void EventDispatcher::CancelDragDrop() {
diff --git a/services/ui/ws/event_dispatcher.h b/services/ui/ws/event_dispatcher.h
index 67572d0d..18b0cb5 100644
--- a/services/ui/ws/event_dispatcher.h
+++ b/services/ui/ws/event_dispatcher.h
@@ -85,7 +85,7 @@
       ServerWindow* window,
       DragTargetConnection* source_connection,
       int32_t drag_pointer,
-      mojo::Map<mojo::String, mojo::Array<uint8_t>> mime_data,
+      const std::unordered_map<std::string, std::vector<uint8_t>>& mime_data,
       uint32_t drag_operations);
   void CancelDragDrop();
   void EndDragDrop();
diff --git a/services/ui/ws/test_change_tracker.cc b/services/ui/ws/test_change_tracker.cc
index 1e7e0f3..1ee3842 100644
--- a/services/ui/ws/test_change_tracker.cc
+++ b/services/ui/ws/test_change_tracker.cc
@@ -9,6 +9,7 @@
 #include "base/strings/string_util.h"
 #include "base/strings/stringprintf.h"
 #include "mojo/common/common_type_converters.h"
+#include "mojo/public/cpp/bindings/map.h"
 #include "services/ui/common/util.h"
 
 using mojo::Array;
diff --git a/services/ui/ws/window_manager_state.cc b/services/ui/ws/window_manager_state.cc
index fe6065db..c6851d67 100644
--- a/services/ui/ws/window_manager_state.cc
+++ b/services/ui/ws/window_manager_state.cc
@@ -165,7 +165,7 @@
     DragSource* drag_source,
     ServerWindow* window,
     DragTargetConnection* source_connection,
-    mojo::Map<mojo::String, mojo::Array<uint8_t>> drag_data,
+    const std::unordered_map<std::string, std::vector<uint8_t>>& drag_data,
     uint32_t drag_operation) {
   int32_t drag_pointer = PointerEvent::kMousePointerId;
   if (event_awaiting_input_ack_ &&
@@ -178,9 +178,9 @@
     return;
   }
 
-  event_dispatcher_.SetDragDropSourceWindow(
-      drag_source, window, source_connection, drag_pointer,
-      std::move(drag_data), drag_operation);
+  event_dispatcher_.SetDragDropSourceWindow(drag_source, window,
+                                            source_connection, drag_pointer,
+                                            drag_data, drag_operation);
 }
 
 void WindowManagerState::CancelDragDrop() {
diff --git a/services/ui/ws/window_manager_state.h b/services/ui/ws/window_manager_state.h
index c063628a..ef49648 100644
--- a/services/ui/ws/window_manager_state.h
+++ b/services/ui/ws/window_manager_state.h
@@ -68,7 +68,7 @@
       DragSource* drag_source,
       ServerWindow* window,
       DragTargetConnection* source_connection,
-      mojo::Map<mojo::String, mojo::Array<uint8_t>> drag_data,
+      const std::unordered_map<std::string, std::vector<uint8_t>>& drag_data,
       uint32_t drag_operation);
   void CancelDragDrop();
   void EndDragDrop();
diff --git a/services/ui/ws/window_tree.cc b/services/ui/ws/window_tree.cc
index 4fdd82c..09f6222 100644
--- a/services/ui/ws/window_tree.cc
+++ b/services/ui/ws/window_tree.cc
@@ -11,7 +11,7 @@
 #include "base/bind.h"
 #include "base/macros.h"
 #include "base/memory/ptr_util.h"
-#include "mojo/public/cpp/bindings/stl_converters.h"
+#include "mojo/public/cpp/bindings/map.h"
 #include "services/ui/ws/default_access_policy.h"
 #include "services/ui/ws/display.h"
 #include "services/ui/ws/display_manager.h"
@@ -1643,9 +1643,7 @@
   // normal.
   WindowManagerState* wms = display_root->window_manager_state();
   window_server_->StartDragLoop(change_id, window, this);
-  wms->SetDragDropSourceWindow(
-      this, window, this, mojo::WrapSTLType(mojo::UnorderedMapToMap(drag_data)),
-      drag_operation);
+  wms->SetDragDropSourceWindow(this, window, this, drag_data, drag_operation);
 }
 
 void WindowTree::CancelDragDrop(Id window_id) {
@@ -1953,9 +1951,8 @@
 }
 
 void WindowTree::PerformOnDragDropStart(
-    mojo::Map<mojo::String, mojo::Array<uint8_t>> mime_data) {
-  client()->OnDragDropStart(
-      mojo::MapToUnorderedMap(mojo::UnwrapToSTLType(std::move(mime_data))));
+    const std::unordered_map<std::string, std::vector<uint8_t>>& mime_data) {
+  client()->OnDragDropStart(mime_data);
 }
 
 void WindowTree::PerformOnDragEnter(
diff --git a/services/ui/ws/window_tree.h b/services/ui/ws/window_tree.h
index acf63c55..226fdba 100644
--- a/services/ui/ws/window_tree.h
+++ b/services/ui/ws/window_tree.h
@@ -498,7 +498,8 @@
 
   // DragTargetConnection:
   void PerformOnDragDropStart(
-      mojo::Map<mojo::String, mojo::Array<uint8_t>> mime_data) override;
+      const std::unordered_map<std::string, std::vector<uint8_t>>& mime_data)
+      override;
   void PerformOnDragEnter(
       const ServerWindow* window,
       uint32_t event_flags,
diff --git a/third_party/WebKit/LayoutTests/SlowTests b/third_party/WebKit/LayoutTests/SlowTests
index 6a35997..ee00259 100644
--- a/third_party/WebKit/LayoutTests/SlowTests
+++ b/third_party/WebKit/LayoutTests/SlowTests
@@ -8,7 +8,7 @@
 # can see a 3x variance or more in test times on the bots. In practice, this means that any test that takes longer
 # than 2 seconds in Release mode or 6 seconds in Debug mode should be listed here.
 
-crbug.com/24182 [ Debug ] storage/indexeddb/objectstore-cursor.html [ Slow ]
+crbug.com/24182 storage/indexeddb/objectstore-cursor.html [ Slow ]
 crbug.com/24182 storage/indexeddb/mozilla/test_objectStore_openKeyCursor.html [ Slow ]
 crbug.com/24182 editing/selection/modify_move/move-by-word-visually-mac.html [ Slow ]
 crbug.com/24182 editing/selection/modify_move/move-by-word-visually-multi-line.html [ Slow ]
diff --git a/third_party/WebKit/LayoutTests/TestExpectations b/third_party/WebKit/LayoutTests/TestExpectations
index fd7ea2e9..7ca1d0b 100644
--- a/third_party/WebKit/LayoutTests/TestExpectations
+++ b/third_party/WebKit/LayoutTests/TestExpectations
@@ -157,6 +157,9 @@
 
 crbug.com/664849 svg/dom/transform-parser.html [ Pass Failure ]
 
+crbug.com/657839 images/png-suite/test.html  [ NeedsRebaseline ]
+crbug.com/657839 virtual/gpu-rasterization/images/png-suite/test.html [ NeedsRebaseline ]
+
 crbug.com/664850 virtual/display_list_2d_canvas/fast/canvas/canvas-createImageBitmap-webgl.html [ Pass Failure ]
 crbug.com/664850 virtual/display_list_2d_canvas/fast/canvas/canvas-drawImage-live-video.html [ Pass Failure ]
 crbug.com/664850 virtual/display_list_2d_canvas/fast/canvas/OffscreenCanvas-2d-drawImage.html [ Pass Failure ]
@@ -1263,6 +1266,19 @@
 crbug.com/528062 [ Win ] virtual/mojo-loading/http/tests/security/xssAuditor/cached-frame.html [ Failure ]
 crbug.com/528062 [ Win ] virtual/mojo-loading/http/tests/security/xssAuditor/chunked-big-script.html [ Failure ]
 
+crbug.com/642454 [ Win Mac ] editing/selection/dont-select-text-overflow-ellipsis-when-wrapping-ltr-mixed.html [ NeedsRebaseline ]
+crbug.com/642454 [ Win Mac ] editing/selection/dont-select-text-overflow-ellipsis-when-wrapping-rtl-mixed.html [ NeedsRebaseline ]
+crbug.com/642454 [ Win Mac ] editing/selection/dont-select-text-overflow-ellipsis-when-wrapping-rtl.html [ NeedsRebaseline ]
+crbug.com/642454 [ Win Mac ] editing/selection/dont-select-text-overflow-ellipsis-when-wrapping.html [ NeedsRebaseline ]
+crbug.com/642454 [ Win Mac ] editing/selection/select-text-overflow-ellipsis-mixed-in-ltr-2.html [ NeedsRebaseline ]
+crbug.com/642454 [ Win Mac ] editing/selection/select-text-overflow-ellipsis-mixed-in-ltr.html [ NeedsRebaseline ]
+crbug.com/642454 [ Win Mac ] editing/selection/select-text-overflow-ellipsis-mixed-in-rtl-2.html [ NeedsRebaseline ]
+crbug.com/642454 [ Win Mac ] fast/css/text-overflow-ellipsis-strict.html [ NeedsRebaseline ]
+crbug.com/642454 [ Win Mac ] fast/css/text-overflow-ellipsis.html [ NeedsRebaseline ]
+crbug.com/642454 [ Win Mac ] fast/text/ellipsis-mixed-text-in-ltr-flow-underline-2.html [ NeedsRebaseline ]
+crbug.com/642454 [ Win Mac ] fast/text/ellipsis-mixed-text-in-ltr-flow-underline.html [ NeedsRebaseline ]
+crbug.com/642454 [ Win Mac ] fast/text/ellipsis-mixed-text-in-rtl-flow-underline-2.html [ NeedsRebaseline ]
+
 crbug.com/392706 virtual/threaded/animations/play-state-initially-paused.html [ Failure ]
 
 # Part of a larger issue referenced in the bug. This specific issue will be fixed shortly.
@@ -2412,3 +2428,4 @@
 crbug.com/678346 [ Win7 Debug ] storage/indexeddb/mozilla/test_objectStore_openKeyCursor.html [ Pass Timeout ]
 crbug.com/678346 [ Win7 Debug ] storage/indexeddb/structured-clone.html [ Pass Timeout ]
 
+crbug.com/678388 [ Trusty Mac10.10 Mac10.11 Debug ] fast/text/ellipsis-stroked.html [ Pass Failure ]
diff --git a/third_party/WebKit/LayoutTests/editing/selection/dont-select-text-overflow-ellipsis-when-wrapping-expected.png b/third_party/WebKit/LayoutTests/editing/selection/dont-select-text-overflow-ellipsis-when-wrapping-expected.png
new file mode 100644
index 0000000..52972d222
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/editing/selection/dont-select-text-overflow-ellipsis-when-wrapping-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/editing/selection/dont-select-text-overflow-ellipsis-when-wrapping-expected.txt b/third_party/WebKit/LayoutTests/editing/selection/dont-select-text-overflow-ellipsis-when-wrapping-expected.txt
new file mode 100644
index 0000000..4a43d5e0
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/editing/selection/dont-select-text-overflow-ellipsis-when-wrapping-expected.txt
@@ -0,0 +1,18 @@
+layer at (0,0) size 800x600
+  LayoutView at (0,0) size 800x600
+layer at (0,0) size 800x104
+  LayoutBlockFlow {HTML} at (0,0) size 800x104
+    LayoutBlockFlow {BODY} at (8,16) size 784x80
+      LayoutBlockFlow {P} at (0,0) size 784x20
+        LayoutText {#text} at (0,0) size 459x19
+          text run at (0,0) width 459: "crbug.com/642454: Don't select part of the ellipsis when wrapping selection."
+      LayoutBlockFlow {DL} at (200,36) size 384x44
+        LayoutBlockFlow {DT} at (0,22) size 384x22
+          LayoutText {#text} at (0,0) size 206x21
+            text run at (0,0) width 206: "Lorem ipsum dolor sit amet"
+layer at (248,52) size 344x22 scrollWidth 1177
+  LayoutBlockFlow {DD} at (40,0) size 344x22
+    LayoutText {#text} at (0,0) size 1178x21
+      text run at (0,0) width 1178: "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua."
+selection start: position 0 of child 0 {#text} of child 1 {DD} of child 2 {DL} of body
+selection end:   position 10 of child 0 {#text} of child 3 {DT} of child 2 {DL} of body
diff --git a/third_party/WebKit/LayoutTests/editing/selection/dont-select-text-overflow-ellipsis-when-wrapping-ltr-mixed-expected.png b/third_party/WebKit/LayoutTests/editing/selection/dont-select-text-overflow-ellipsis-when-wrapping-ltr-mixed-expected.png
new file mode 100644
index 0000000..96ca490
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/editing/selection/dont-select-text-overflow-ellipsis-when-wrapping-ltr-mixed-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/editing/selection/dont-select-text-overflow-ellipsis-when-wrapping-ltr-mixed-expected.txt b/third_party/WebKit/LayoutTests/editing/selection/dont-select-text-overflow-ellipsis-when-wrapping-ltr-mixed-expected.txt
new file mode 100644
index 0000000..f4759534
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/editing/selection/dont-select-text-overflow-ellipsis-when-wrapping-ltr-mixed-expected.txt
@@ -0,0 +1,19 @@
+layer at (0,0) size 800x600
+  LayoutView at (0,0) size 800x600
+layer at (0,0) size 800x104
+  LayoutBlockFlow {HTML} at (0,0) size 800x104
+    LayoutBlockFlow {BODY} at (8,16) size 784x80
+      LayoutBlockFlow {P} at (0,0) size 784x20
+        LayoutText {#text} at (0,0) size 459x19
+          text run at (0,0) width 459: "crbug.com/642454: Don't select part of the ellipsis when wrapping selection."
+      LayoutBlockFlow {DL} at (200,36) size 384x44
+        LayoutBlockFlow {DT} at (0,22) size 384x22
+          LayoutText {#text} at (0,0) size 206x21
+            text run at (0,0) width 206: "Lorem ipsum dolor sit amet"
+layer at (248,52) size 344x22 scrollWidth 393
+  LayoutBlockFlow {DD} at (40,0) size 344x22
+    LayoutText {#text} at (0,0) size 393x21
+      text run at (0,0) width 54: "Lorem "
+      text run at (54,0) width 339 RTL: "\x{5DC}\x{5D5}\x{5E8}\x{5DD} \x{5D0}\x{5D9}\x{5E4}\x{5E1}\x{5D5}\x{5DD} \x{5D4}\x{5D5}\x{5D0} \x{5DB}\x{5D9}\x{5E0}\x{5D5}\x{5D9} \x{5DC}\x{5D8}\x{5E7}\x{5E1}\x{5D8} \x{5D7}\x{5E1}\x{5E8} \x{5DE}\x{5E9}\x{5DE}\x{5E2}\x{5D5}\x{5EA} \x{5DC}\x{5D7}\x{5DC}\x{5D5}\x{5D8}\x{5D9}\x{5DF}"
+selection start: position 0 of child 0 {#text} of child 1 {DD} of child 2 {DL} of body
+selection end:   position 10 of child 0 {#text} of child 3 {DT} of child 2 {DL} of body
diff --git a/third_party/WebKit/LayoutTests/editing/selection/dont-select-text-overflow-ellipsis-when-wrapping-ltr-mixed.html b/third_party/WebKit/LayoutTests/editing/selection/dont-select-text-overflow-ellipsis-when-wrapping-ltr-mixed.html
new file mode 100644
index 0000000..16d5af9f
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/editing/selection/dont-select-text-overflow-ellipsis-when-wrapping-ltr-mixed.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<style>
+dl {
+    max-width: 20em;
+    margin: auto;
+    font-size: 120%;
+    white-space: nowrap;
+}
+dd {
+    overflow: hidden;
+    text-overflow: ellipsis;
+    text-decoration: inherit;
+}
+</style>
+<p>crbug.com/642454: Don't select part of the ellipsis when wrapping selection.</p>
+<dl>
+    <dd id="selection_with_ellipsis">Lorem לורם איפסום הוא כינוי לטקסט חסר משמעות לחלוטין</dd>
+    <dt id="wrapped">Lorem ipsum dolor sit amet</dt>
+</dl>
+<script src="../../resources/run-after-layout-and-paint.js"></script>
+<script>
+runAfterLayoutAndPaint(function() {
+        getSelection().setBaseAndExtent(selection_with_ellipsis.firstChild, 0, wrapped.firstChild, 10);
+        }, true);
+</script>
diff --git a/third_party/WebKit/LayoutTests/editing/selection/dont-select-text-overflow-ellipsis-when-wrapping-rtl-expected.png b/third_party/WebKit/LayoutTests/editing/selection/dont-select-text-overflow-ellipsis-when-wrapping-rtl-expected.png
new file mode 100644
index 0000000..30693e9
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/editing/selection/dont-select-text-overflow-ellipsis-when-wrapping-rtl-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/editing/selection/dont-select-text-overflow-ellipsis-when-wrapping-rtl-expected.txt b/third_party/WebKit/LayoutTests/editing/selection/dont-select-text-overflow-ellipsis-when-wrapping-rtl-expected.txt
new file mode 100644
index 0000000..f2d388b
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/editing/selection/dont-select-text-overflow-ellipsis-when-wrapping-rtl-expected.txt
@@ -0,0 +1,19 @@
+layer at (0,0) size 800x600
+  LayoutView at (0,0) size 800x600
+layer at (0,0) size 800x104
+  LayoutBlockFlow {HTML} at (0,0) size 800x104
+    LayoutBlockFlow {BODY} at (8,16) size 784x80
+      LayoutBlockFlow {P} at (0,0) size 784x20
+        LayoutText {#text} at (0,0) size 459x19
+          text run at (0,0) width 459: "crbug.com/642454: Don't select part of the ellipsis when wrapping selection."
+      LayoutBlockFlow {DL} at (200,36) size 384x44
+        LayoutBlockFlow {DT} at (0,22) size 384x22
+          LayoutText {#text} at (0,0) size 206x21
+            text run at (0,0) width 206: "Lorem ipsum dolor sit amet"
+layer at (208,52) size 344x22 scrollX 833.00 scrollWidth 1177
+  LayoutBlockFlow {DD} at (0,0) size 344x22
+    LayoutText {#text} at (-833,0) size 1178x21
+      text run at (-833,0) width 5 RTL: "."
+      text run at (-828,0) width 1172: "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua"
+selection start: position 0 of child 0 {#text} of child 1 {DD} of child 2 {DL} of body
+selection end:   position 10 of child 0 {#text} of child 3 {DT} of child 2 {DL} of body
diff --git a/third_party/WebKit/LayoutTests/editing/selection/dont-select-text-overflow-ellipsis-when-wrapping-rtl-mixed-expected.png b/third_party/WebKit/LayoutTests/editing/selection/dont-select-text-overflow-ellipsis-when-wrapping-rtl-mixed-expected.png
new file mode 100644
index 0000000..88dcee1
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/editing/selection/dont-select-text-overflow-ellipsis-when-wrapping-rtl-mixed-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/editing/selection/dont-select-text-overflow-ellipsis-when-wrapping-rtl-mixed-expected.txt b/third_party/WebKit/LayoutTests/editing/selection/dont-select-text-overflow-ellipsis-when-wrapping-rtl-mixed-expected.txt
new file mode 100644
index 0000000..e455766
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/editing/selection/dont-select-text-overflow-ellipsis-when-wrapping-rtl-mixed-expected.txt
@@ -0,0 +1,19 @@
+layer at (0,0) size 800x600
+  LayoutView at (0,0) size 800x600
+layer at (0,0) size 800x104
+  LayoutBlockFlow {HTML} at (0,0) size 800x104
+    LayoutBlockFlow {BODY} at (8,16) size 784x80
+      LayoutBlockFlow {P} at (0,0) size 784x20
+        LayoutText {#text} at (0,0) size 459x19
+          text run at (0,0) width 459: "crbug.com/642454: Don't select part of the ellipsis when wrapping selection."
+      LayoutBlockFlow {DL} at (200,36) size 384x44
+        LayoutBlockFlow {DT} at (0,22) size 384x22
+          LayoutText {#text} at (0,0) size 206x21
+            text run at (0,0) width 206: "Lorem ipsum dolor sit amet"
+layer at (208,52) size 344x22 scrollX 49.00 scrollWidth 393
+  LayoutBlockFlow {DD} at (0,0) size 344x22
+    LayoutText {#text} at (-49,0) size 393x21
+      text run at (-49,0) width 344 RTL: " \x{5DC}\x{5D5}\x{5E8}\x{5DD} \x{5D0}\x{5D9}\x{5E4}\x{5E1}\x{5D5}\x{5DD} \x{5D4}\x{5D5}\x{5D0} \x{5DB}\x{5D9}\x{5E0}\x{5D5}\x{5D9} \x{5DC}\x{5D8}\x{5E7}\x{5E1}\x{5D8} \x{5D7}\x{5E1}\x{5E8} \x{5DE}\x{5E9}\x{5DE}\x{5E2}\x{5D5}\x{5EA} \x{5DC}\x{5D7}\x{5DC}\x{5D5}\x{5D8}\x{5D9}\x{5DF}"
+      text run at (295,0) width 49: "Lorem"
+selection start: position 0 of child 0 {#text} of child 1 {DD} of child 2 {DL} of body
+selection end:   position 10 of child 0 {#text} of child 3 {DT} of child 2 {DL} of body
diff --git a/third_party/WebKit/LayoutTests/editing/selection/dont-select-text-overflow-ellipsis-when-wrapping-rtl-mixed.html b/third_party/WebKit/LayoutTests/editing/selection/dont-select-text-overflow-ellipsis-when-wrapping-rtl-mixed.html
new file mode 100644
index 0000000..fb0bb88
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/editing/selection/dont-select-text-overflow-ellipsis-when-wrapping-rtl-mixed.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<style>
+dl {
+    max-width: 20em;
+    margin: auto;
+    font-size: 120%;
+    white-space: nowrap;
+}
+dd {
+    overflow: hidden;
+    text-overflow: ellipsis;
+    text-decoration: inherit;
+    direction:rtl;
+}
+</style>
+<p>crbug.com/642454: Don't select part of the ellipsis when wrapping selection.</p>
+<dl>
+    <dd id="selection_with_ellipsis">Lorem לורם איפסום הוא כינוי לטקסט חסר משמעות לחלוטין</dd>
+    <dt id="wrapped">Lorem ipsum dolor sit amet</dt>
+</dl>
+<script src="../../resources/run-after-layout-and-paint.js"></script>
+<script>
+runAfterLayoutAndPaint(function() {
+        getSelection().setBaseAndExtent(selection_with_ellipsis.firstChild, 0, wrapped.firstChild, 10);
+        }, true);
+</script>
diff --git a/third_party/WebKit/LayoutTests/editing/selection/dont-select-text-overflow-ellipsis-when-wrapping-rtl.html b/third_party/WebKit/LayoutTests/editing/selection/dont-select-text-overflow-ellipsis-when-wrapping-rtl.html
new file mode 100644
index 0000000..ebd85c0f
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/editing/selection/dont-select-text-overflow-ellipsis-when-wrapping-rtl.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<style>
+dl {
+    max-width: 20em;
+    margin: auto;
+    font-size: 120%;
+    white-space: nowrap;
+}
+dd {
+    overflow: hidden;
+    text-overflow: ellipsis;
+    text-decoration: inherit;
+    direction:rtl;
+}
+</style>
+<p>crbug.com/642454: Don't select part of the ellipsis when wrapping selection.</p>
+<dl>
+    <dd id="selection_with_ellipsis">Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.</dd>
+    <dt id="wrapped">Lorem ipsum dolor sit amet</dt>
+</dl>
+<script src="../../resources/run-after-layout-and-paint.js"></script>
+<script>
+runAfterLayoutAndPaint(function() {
+        getSelection().setBaseAndExtent(selection_with_ellipsis.firstChild, 0, wrapped.firstChild, 10);
+        }, true);
+</script>
diff --git a/third_party/WebKit/LayoutTests/editing/selection/dont-select-text-overflow-ellipsis-when-wrapping.html b/third_party/WebKit/LayoutTests/editing/selection/dont-select-text-overflow-ellipsis-when-wrapping.html
new file mode 100644
index 0000000..c8c5dd4fd
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/editing/selection/dont-select-text-overflow-ellipsis-when-wrapping.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<style>
+dl {
+    max-width: 20em;
+    margin: auto;
+    font-size: 120%;
+    white-space: nowrap;
+}
+dd {
+    overflow: hidden;
+    text-overflow: ellipsis;
+    text-decoration: inherit;
+}
+</style>
+<p>crbug.com/642454: Don't select part of the ellipsis when wrapping selection.</p>
+<dl>
+    <dd id="selection_with_ellipsis">Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.</dd>
+    <dt id="wrapped">Lorem ipsum dolor sit amet</dt>
+</dl>
+<script src="../../resources/run-after-layout-and-paint.js"></script>
+<script>
+runAfterLayoutAndPaint(function() {
+        getSelection().setBaseAndExtent(selection_with_ellipsis.firstChild, 0, wrapped.firstChild, 10);
+        }, true);
+</script>
diff --git a/third_party/WebKit/LayoutTests/editing/selection/select-text-overflow-ellipsis-mixed-in-ltr-2-expected.png b/third_party/WebKit/LayoutTests/editing/selection/select-text-overflow-ellipsis-mixed-in-ltr-2-expected.png
new file mode 100644
index 0000000..d869e95
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/editing/selection/select-text-overflow-ellipsis-mixed-in-ltr-2-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/editing/selection/select-text-overflow-ellipsis-mixed-in-ltr-2-expected.txt b/third_party/WebKit/LayoutTests/editing/selection/select-text-overflow-ellipsis-mixed-in-ltr-2-expected.txt
new file mode 100644
index 0000000..a6c0926
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/editing/selection/select-text-overflow-ellipsis-mixed-in-ltr-2-expected.txt
@@ -0,0 +1,16 @@
+layer at (0,0) size 800x600
+  LayoutView at (0,0) size 800x600
+layer at (0,0) size 800x82
+  LayoutBlockFlow {HTML} at (0,0) size 800x82
+    LayoutBlockFlow {BODY} at (8,16) size 784x58
+      LayoutBlockFlow {P} at (0,0) size 784x20
+        LayoutText {#text} at (0,0) size 658x19
+          text run at (0,0) width 658: "crbug.com/642454: Select all text in mixed ltr-rtl in rtl flow. None of the dots in the ellipsis should be selected."
+      LayoutBlockFlow {DL} at (200,36) size 384x22
+layer at (248,52) size 344x22 scrollWidth 506
+  LayoutBlockFlow {DD} at (40,0) size 344x22
+    LayoutText {#text} at (0,0) size 506x21
+      text run at (0,0) width 104: "Lorem ipsum "
+      text run at (104,0) width 402 RTL: "\x{5D9}\x{5E0}\x{5D8}\x{5E8}\x{5E0}\x{5D8} \x{5D5}\x{5DB}\x{5D5}' \x{5D1}\x{5DE}\x{5E7}\x{5D5}\x{5DD} \x{5D4}\x{5D8}\x{5E7}\x{5E1}\x{5D8} \x{5D4}\x{5D0}\x{5DE}\x{5D9}\x{5EA}\x{5D9} \x{5D4}\x{5E1}\x{5D5}\x{5E4}\x{5D9}\x{5E2}\x{5D3} \x{5E9}\x{5D9}\x{5D4}\x{5D9}\x{5D4} \x{5D8}\x{5E7}\x{5E1}\x{5D8} \x{5D0}\x{5DE}\x{5D9}\x{5EA}"
+selection start: position 0 of child 0 {#text} of child 1 {DD} of child 2 {DL} of body
+selection end:   position 66 of child 0 {#text} of child 1 {DD} of child 2 {DL} of body
diff --git a/third_party/WebKit/LayoutTests/editing/selection/select-text-overflow-ellipsis-mixed-in-ltr-2.html b/third_party/WebKit/LayoutTests/editing/selection/select-text-overflow-ellipsis-mixed-in-ltr-2.html
new file mode 100644
index 0000000..6623fe5
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/editing/selection/select-text-overflow-ellipsis-mixed-in-ltr-2.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<style>
+dl {
+    max-width: 20em;
+    margin: auto;
+    font-size: 120%;
+    white-space: nowrap;
+}
+dd {
+    overflow: hidden;
+    text-overflow: ellipsis;
+    text-decoration: inherit;
+}
+</style>
+<p>crbug.com/642454: Select all text in mixed ltr-rtl in rtl flow. None of the dots in the ellipsis should be selected.</p>
+<dl>
+    <dd id="selection_with_ellipsis">Lorem ipsum ינטרנט וכו' במקום הטקסט האמיתי הסופיעד שיהיה טקסט אמית</dd>
+</dl>
+<script src="../../resources/run-after-layout-and-paint.js"></script>
+<script>
+runAfterLayoutAndPaint(function() {
+        getSelection().setBaseAndExtent(selection_with_ellipsis.firstChild, 0, selection_with_ellipsis.firstChild, 155);
+        }, true);
+</script>
diff --git a/third_party/WebKit/LayoutTests/editing/selection/select-text-overflow-ellipsis-mixed-in-rtl-2-expected.png b/third_party/WebKit/LayoutTests/editing/selection/select-text-overflow-ellipsis-mixed-in-rtl-2-expected.png
new file mode 100644
index 0000000..9d57936
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/editing/selection/select-text-overflow-ellipsis-mixed-in-rtl-2-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/editing/selection/select-text-overflow-ellipsis-mixed-in-rtl-2-expected.txt b/third_party/WebKit/LayoutTests/editing/selection/select-text-overflow-ellipsis-mixed-in-rtl-2-expected.txt
new file mode 100644
index 0000000..bef2cac
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/editing/selection/select-text-overflow-ellipsis-mixed-in-rtl-2-expected.txt
@@ -0,0 +1,16 @@
+layer at (0,0) size 800x600
+  LayoutView at (0,0) size 800x600
+layer at (0,0) size 800x82
+  LayoutBlockFlow {HTML} at (0,0) size 800x82
+    LayoutBlockFlow {BODY} at (8,16) size 784x58
+      LayoutBlockFlow {P} at (0,0) size 784x20
+        LayoutText {#text} at (0,0) size 658x19
+          text run at (0,0) width 658: "crbug.com/642454: Select all text in mixed ltr-rtl in rtl flow. None of the dots in the ellipsis should be selected."
+      LayoutBlockFlow {DL} at (200,36) size 384x22
+layer at (208,52) size 344x22 scrollX 162.00 scrollWidth 506
+  LayoutBlockFlow {DD} at (0,0) size 344x22
+    LayoutText {#text} at (-162,0) size 506x21
+      text run at (-162,0) width 407 RTL: " \x{5D9}\x{5E0}\x{5D8}\x{5E8}\x{5E0}\x{5D8} \x{5D5}\x{5DB}\x{5D5}' \x{5D1}\x{5DE}\x{5E7}\x{5D5}\x{5DD} \x{5D4}\x{5D8}\x{5E7}\x{5E1}\x{5D8} \x{5D4}\x{5D0}\x{5DE}\x{5D9}\x{5EA}\x{5D9} \x{5D4}\x{5E1}\x{5D5}\x{5E4}\x{5D9}\x{5E2}\x{5D3} \x{5E9}\x{5D9}\x{5D4}\x{5D9}\x{5D4} \x{5D8}\x{5E7}\x{5E1}\x{5D8} \x{5D0}\x{5DE}\x{5D9}\x{5EA}"
+      text run at (245,0) width 99: "Lorem ipsum"
+selection start: position 0 of child 0 {#text} of child 1 {DD} of child 2 {DL} of body
+selection end:   position 66 of child 0 {#text} of child 1 {DD} of child 2 {DL} of body
diff --git a/third_party/WebKit/LayoutTests/editing/selection/select-text-overflow-ellipsis-mixed-in-rtl-2.html b/third_party/WebKit/LayoutTests/editing/selection/select-text-overflow-ellipsis-mixed-in-rtl-2.html
new file mode 100644
index 0000000..7e0d15c
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/editing/selection/select-text-overflow-ellipsis-mixed-in-rtl-2.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<style>
+dl {
+    max-width: 20em;
+    margin: auto;
+    font-size: 120%;
+    white-space: nowrap;
+}
+dd {
+    overflow: hidden;
+    text-overflow: ellipsis;
+    text-decoration: inherit;
+    direction: rtl;
+}
+</style>
+<p>crbug.com/642454: Select all text in mixed ltr-rtl in rtl flow. None of the dots in the ellipsis should be selected.</p>
+<dl>
+    <dd id="selection_with_ellipsis">Lorem ipsum ינטרנט וכו' במקום הטקסט האמיתי הסופיעד שיהיה טקסט אמית</dd>
+</dl>
+<script src="../../resources/run-after-layout-and-paint.js"></script>
+<script>
+runAfterLayoutAndPaint(function() {
+        getSelection().setBaseAndExtent(selection_with_ellipsis.firstChild, 0, selection_with_ellipsis.firstChild, 155);
+        }, true);
+</script>
diff --git a/third_party/WebKit/LayoutTests/fast/text/ellipsis-mixed-text-in-ltr-flow-underline-2-expected.png b/third_party/WebKit/LayoutTests/fast/text/ellipsis-mixed-text-in-ltr-flow-underline-2-expected.png
new file mode 100644
index 0000000..ab9cd256
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/fast/text/ellipsis-mixed-text-in-ltr-flow-underline-2-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/fast/text/ellipsis-mixed-text-in-ltr-flow-underline-2-expected.txt b/third_party/WebKit/LayoutTests/fast/text/ellipsis-mixed-text-in-ltr-flow-underline-2-expected.txt
new file mode 100644
index 0000000..a9e76de
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/fast/text/ellipsis-mixed-text-in-ltr-flow-underline-2-expected.txt
@@ -0,0 +1,16 @@
+layer at (0,0) size 800x600
+  LayoutView at (0,0) size 800x600
+layer at (0,0) size 800x116
+  LayoutBlockFlow {HTML} at (0,0) size 800x116
+    LayoutBlockFlow {BODY} at (8,16) size 784x92
+      LayoutBlockFlow {P} at (0,0) size 784x20
+        LayoutText {#text} at (0,0) size 456x19
+          text run at (0,0) width 456: "crbug.com/634445: Rtl text in a ltr flow should truncate the text left-to-right."
+      LayoutBlockFlow {P} at (0,36) size 784x20
+        LayoutText {#text} at (0,0) size 382x19
+          text run at (0,0) width 382: "You should see an underline beneath the text but not the ellipsis."
+layer at (8,88) size 350x20 scrollWidth 425
+  LayoutBlockFlow {DIV} at (0,72) size 350x20
+    LayoutText {#text} at (0,0) size 425x19
+      text run at (0,0) width 83: "Lorem ipsum "
+      text run at (83,0) width 342 RTL: "\x{5D9}\x{5E0}\x{5D8}\x{5E8}\x{5E0}\x{5D8} \x{5D5}\x{5DB}\x{5D5}' \x{5D1}\x{5DE}\x{5E7}\x{5D5}\x{5DD} \x{5D4}\x{5D8}\x{5E7}\x{5E1}\x{5D8} \x{5D4}\x{5D0}\x{5DE}\x{5D9}\x{5EA}\x{5D9} \x{5D4}\x{5E1}\x{5D5}\x{5E4}\x{5D9}\x{5E2}\x{5D3} \x{5E9}\x{5D9}\x{5D4}\x{5D9}\x{5D4} \x{5D8}\x{5E7}\x{5E1}\x{5D8} \x{5D0}\x{5DE}\x{5D9}\x{5EA}"
diff --git a/third_party/WebKit/LayoutTests/fast/text/ellipsis-mixed-text-in-ltr-flow-underline-2.html b/third_party/WebKit/LayoutTests/fast/text/ellipsis-mixed-text-in-ltr-flow-underline-2.html
new file mode 100644
index 0000000..fefc561
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/fast/text/ellipsis-mixed-text-in-ltr-flow-underline-2.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<meta charset='utf-8'>
+<style>
+div {
+  width: 350px;
+  white-space: nowrap;
+  overflow: hidden;
+  text-overflow: ellipsis;
+  text-decoration: underline;
+}
+</style>
+<p>crbug.com/634445: Rtl text in a ltr flow should truncate the text left-to-right.</p>
+<p>You should see an underline beneath the text but not the ellipsis.</p>
+<div>Lorem ipsum ינטרנט וכו' במקום הטקסט האמיתי הסופיעד שיהיה טקסט אמית</div>
diff --git a/third_party/WebKit/LayoutTests/fast/text/ellipsis-mixed-text-in-rtl-flow-underline-2-expected.png b/third_party/WebKit/LayoutTests/fast/text/ellipsis-mixed-text-in-rtl-flow-underline-2-expected.png
new file mode 100644
index 0000000..d37b5c90
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/fast/text/ellipsis-mixed-text-in-rtl-flow-underline-2-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/fast/text/ellipsis-mixed-text-in-rtl-flow-underline-2-expected.txt b/third_party/WebKit/LayoutTests/fast/text/ellipsis-mixed-text-in-rtl-flow-underline-2-expected.txt
new file mode 100644
index 0000000..8485289
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/fast/text/ellipsis-mixed-text-in-rtl-flow-underline-2-expected.txt
@@ -0,0 +1,16 @@
+layer at (0,0) size 800x600
+  LayoutView at (0,0) size 800x600
+layer at (0,0) size 800x116
+  LayoutBlockFlow {HTML} at (0,0) size 800x116
+    LayoutBlockFlow {BODY} at (8,16) size 784x92
+      LayoutBlockFlow {P} at (0,0) size 784x20
+        LayoutText {#text} at (0,0) size 456x19
+          text run at (0,0) width 456: "crbug.com/634445: Rtl text in a ltr flow should truncate the text left-to-right."
+      LayoutBlockFlow {P} at (0,36) size 784x20
+        LayoutText {#text} at (0,0) size 382x19
+          text run at (0,0) width 382: "You should see an underline beneath the text but not the ellipsis."
+layer at (8,88) size 350x20 scrollX 75.00 scrollWidth 425
+  LayoutBlockFlow {DIV} at (0,72) size 350x20
+    LayoutText {#text} at (-75,0) size 425x19
+      text run at (-75,0) width 346 RTL: " \x{5D9}\x{5E0}\x{5D8}\x{5E8}\x{5E0}\x{5D8} \x{5D5}\x{5DB}\x{5D5}' \x{5D1}\x{5DE}\x{5E7}\x{5D5}\x{5DD} \x{5D4}\x{5D8}\x{5E7}\x{5E1}\x{5D8} \x{5D4}\x{5D0}\x{5DE}\x{5D9}\x{5EA}\x{5D9} \x{5D4}\x{5E1}\x{5D5}\x{5E4}\x{5D9}\x{5E2}\x{5D3} \x{5E9}\x{5D9}\x{5D4}\x{5D9}\x{5D4} \x{5D8}\x{5E7}\x{5E1}\x{5D8} \x{5D0}\x{5DE}\x{5D9}\x{5EA}"
+      text run at (271,0) width 79: "Lorem ipsum"
diff --git a/third_party/WebKit/LayoutTests/fast/text/ellipsis-mixed-text-in-rtl-flow-underline-2.html b/third_party/WebKit/LayoutTests/fast/text/ellipsis-mixed-text-in-rtl-flow-underline-2.html
new file mode 100644
index 0000000..d3c8105
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/fast/text/ellipsis-mixed-text-in-rtl-flow-underline-2.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<meta charset='utf-8'>
+<style>
+div {
+  width: 350px;
+  white-space: nowrap;
+  overflow: hidden;
+  text-overflow: ellipsis;
+  text-decoration: underline;
+  direction: rtl;
+}
+</style>
+<p>crbug.com/634445: Rtl text in a ltr flow should truncate the text left-to-right.</p>
+<p>You should see an underline beneath the text but not the ellipsis.</p>
+<div>Lorem ipsum ינטרנט וכו' במקום הטקסט האמיתי הסופיעד שיהיה טקסט אמית</div>
diff --git a/third_party/WebKit/LayoutTests/inspector/sources/debugger/script-collected-expected.txt b/third_party/WebKit/LayoutTests/inspector/sources/debugger/script-collected-expected.txt
new file mode 100644
index 0000000..96b5bf0
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/inspector/sources/debugger/script-collected-expected.txt
@@ -0,0 +1,5 @@
+Tests that DiscardedAnonymousScriptSource event is fired and workspace is cleared.
+
+Discarded: 1100
+Remaining UISourceCodes: 900
+
diff --git a/third_party/WebKit/LayoutTests/inspector/sources/debugger/script-collected.html b/third_party/WebKit/LayoutTests/inspector/sources/debugger/script-collected.html
new file mode 100644
index 0000000..82a66dd4
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/inspector/sources/debugger/script-collected.html
@@ -0,0 +1,42 @@
+<html>
+<head>
+<script src="../../../http/tests/inspector/inspector-test.js"></script>
+<script src="../../../http/tests/inspector/debugger-test.js"></script>
+<script>
+function generateErrorScripts()
+{
+    for (var i = 0; i < 2000; ++i) {
+        try {
+          eval("#BAD SCRIPT# " + i);
+        } catch(e) {
+        }
+    }
+}
+
+function test()
+{
+    InspectorTest.startDebuggerTest(step1);
+    var discardedScripts = 0;
+
+    function step1()
+    {
+        InspectorTest.debuggerModel.addEventListener(SDK.DebuggerModel.Events.DiscardedAnonymousScriptSource, () => ++discardedScripts);
+        InspectorTest.evaluateInPage("generateErrorScripts()\n//# sourceURL=foo", step2);
+    }
+
+    function step2()
+    {
+        InspectorTest.addResult("Discarded: " + discardedScripts);
+        var codes = Workspace.workspace.uiSourceCodesForProjectType(Workspace.projectTypes.Debugger).filter(code => !code.url().match(/VM\d+\s/));
+        InspectorTest.addResult("Remaining UISourceCodes: " + codes.length);
+        InspectorTest.completeDebuggerTest();
+    }
+}
+</script>
+</head>
+<body onload="runTest()">
+<p>
+Tests that DiscardedAnonymousScriptSource event is fired and workspace is cleared.
+</p>
+</body>
+</html>
diff --git a/third_party/WebKit/LayoutTests/platform/linux/editing/selection/select-text-overflow-ellipsis-mixed-in-ltr-expected.png b/third_party/WebKit/LayoutTests/platform/linux/editing/selection/select-text-overflow-ellipsis-mixed-in-ltr-expected.png
index fc20b80..00b5c62 100644
--- a/third_party/WebKit/LayoutTests/platform/linux/editing/selection/select-text-overflow-ellipsis-mixed-in-ltr-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/linux/editing/selection/select-text-overflow-ellipsis-mixed-in-ltr-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/text/ellipsis-mixed-text-in-ltr-flow-underline-expected.png b/third_party/WebKit/LayoutTests/platform/linux/fast/text/ellipsis-mixed-text-in-ltr-flow-underline-expected.png
index 1b7b090..db4e60d 100644
--- a/third_party/WebKit/LayoutTests/platform/linux/fast/text/ellipsis-mixed-text-in-ltr-flow-underline-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/linux/fast/text/ellipsis-mixed-text-in-ltr-flow-underline-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/virtual/threaded/fast/idleToBlob/OffscreenCanvas-convertToBlob-webgl-main-expected.html b/third_party/WebKit/LayoutTests/virtual/threaded/fast/idleToBlob/OffscreenCanvas-convertToBlob-webgl-main-expected.html
index 61c4f5c..6de349fe 100644
--- a/third_party/WebKit/LayoutTests/virtual/threaded/fast/idleToBlob/OffscreenCanvas-convertToBlob-webgl-main-expected.html
+++ b/third_party/WebKit/LayoutTests/virtual/threaded/fast/idleToBlob/OffscreenCanvas-convertToBlob-webgl-main-expected.html
@@ -1,16 +1,30 @@
 <img id="png"/>
-<script>
+<img id="jpeg-high"/>
+<img id="jpeg-low"/>
+<img id="webp-high"/>
+<img id="webp-low"/>
+<script type="text/javascript">
 if (window.testRunner) {
   testRunner.waitUntilDone();
 }
 
 var pngImage = document.getElementById('png');
+var jpegImageHigh = document.getElementById('jpeg-high');
+var jpegImageLow = document.getElementById('jpeg-low');
+var webpImageHigh = document.getElementById('webp-high');
+var webpImageLow = document.getElementById('webp-low');
+var numTestCount = 5;
 function imageLoaded() {
-  if (window.testRunner) {
+  numTestCount--;
+  if (numTestCount == 0 && window.testRunner) {
     window.testRunner.notifyDone();
   }
 }
 pngImage.addEventListener('load', imageLoaded);
+jpegImageHigh.addEventListener('load', imageLoaded);
+jpegImageLow.addEventListener('load', imageLoaded);
+webpImageHigh.addEventListener('load', imageLoaded);
+webpImageLow.addEventListener('load', imageLoaded);
 
 var canvas = document.createElement("canvas");
 canvas.width = 50;
@@ -22,5 +36,22 @@
 canvas.toBlob(function(blob) {
   pngImage.src = URL.createObjectURL(blob);
 });
+
+canvas.toBlob(function(blob) {
+  jpegImageHigh.src = URL.createObjectURL(blob);
+}, "image/jpeg", 1.0);
+
+canvas.toBlob(function(blob) {
+    jpegImageLow.src = URL.createObjectURL(blob);
+}, "image/jpeg", 0.2)
+
+canvas.toBlob(function(blob) {
+    webpImageHigh.src = URL.createObjectURL(blob);
+}, "image/webp", 1.0);
+
+canvas.toBlob(function(blob) {
+    webpImageLow.src = URL.createObjectURL(blob);
+}, "image/webp", 0.2);
+
 </script>
 
diff --git a/third_party/WebKit/LayoutTests/virtual/threaded/fast/idleToBlob/OffscreenCanvas-convertToBlob-webgl-main.html b/third_party/WebKit/LayoutTests/virtual/threaded/fast/idleToBlob/OffscreenCanvas-convertToBlob-webgl-main.html
index aeddaf4..69ec02a1 100644
--- a/third_party/WebKit/LayoutTests/virtual/threaded/fast/idleToBlob/OffscreenCanvas-convertToBlob-webgl-main.html
+++ b/third_party/WebKit/LayoutTests/virtual/threaded/fast/idleToBlob/OffscreenCanvas-convertToBlob-webgl-main.html
@@ -1,18 +1,30 @@
 <img id="png"/>
+<img id="jpeg-high"/>
+<img id="jpeg-low"/>
+<img id="webp-high"/>
+<img id="webp-low"/>
 <script type="text/javascript">
 if (window.testRunner) {
   testRunner.waitUntilDone();
 }
 
-// TODO: Add more image types to this test once the toImageData() for webgl
-// is completed. See crbug.com/657531.
 var pngImage = document.getElementById('png');
+var jpegImageHigh = document.getElementById('jpeg-high');
+var jpegImageLow = document.getElementById('jpeg-low');
+var webpImageHigh = document.getElementById('webp-high');
+var webpImageLow = document.getElementById('webp-low');
+var numTestCount = 5;
 function imageLoaded() {
-  if (window.testRunner) {
+  numTestCount--;
+  if (numTestCount == 0 && window.testRunner) {
     window.testRunner.notifyDone();
   }
 }
 pngImage.addEventListener('load', imageLoaded);
+jpegImageHigh.addEventListener('load', imageLoaded);
+jpegImageLow.addEventListener('load', imageLoaded);
+webpImageHigh.addEventListener('load', imageLoaded);
+webpImageLow.addEventListener('load', imageLoaded);
 
 var offCanvas = new OffscreenCanvas(50, 50);
 var gl = offCanvas.getContext('webgl');
@@ -24,5 +36,25 @@
     pngImage.src = URL.createObjectURL(blob);
   });
 
+offCanvas.convertToBlob({type: "image/jpeg"})
+  .then(function(blob) {
+    jpegImageHigh.src = URL.createObjectURL(blob);
+  });
+
+offCanvas.convertToBlob({type: "image/jpeg", quality: 0.2})
+  .then(function(blob) {
+    jpegImageLow.src = URL.createObjectURL(blob);
+  });
+
+offCanvas.convertToBlob({type: "image/webp"})
+  .then(function(blob) { 
+    webpImageHigh.src = URL.createObjectURL(blob);
+  });
+
+offCanvas.convertToBlob({type: "image/webp", quality: 0.2})
+  .then(function(blob) {
+    webpImageLow.src = URL.createObjectURL(blob);
+  });
+
 </script>
 
diff --git a/third_party/WebKit/LayoutTests/virtual/threaded/fast/idleToBlob/OffscreenCanvas-convertToBlob-webgl-worker-expected.html b/third_party/WebKit/LayoutTests/virtual/threaded/fast/idleToBlob/OffscreenCanvas-convertToBlob-webgl-worker-expected.html
index 61c4f5c..e402eef1 100644
--- a/third_party/WebKit/LayoutTests/virtual/threaded/fast/idleToBlob/OffscreenCanvas-convertToBlob-webgl-worker-expected.html
+++ b/third_party/WebKit/LayoutTests/virtual/threaded/fast/idleToBlob/OffscreenCanvas-convertToBlob-webgl-worker-expected.html
@@ -1,16 +1,30 @@
 <img id="png"/>
+<img id="jpeg-high"/>
+<img id="jpeg-low"/>
+<img id="webp-high"/>
+<img id="webp-low"/>
 <script>
 if (window.testRunner) {
   testRunner.waitUntilDone();
 }
 
 var pngImage = document.getElementById('png');
+var jpegImageHigh = document.getElementById('jpeg-high');
+var jpegImageLow = document.getElementById('jpeg-low');
+var webpImageHigh = document.getElementById('webp-high');
+var webpImageLow = document.getElementById('webp-low');
+var numTestCount = 5;
 function imageLoaded() {
-  if (window.testRunner) {
+  numTestCount--;
+  if (numTestCount == 0 && window.testRunner) {
     window.testRunner.notifyDone();
   }
 }
 pngImage.addEventListener('load', imageLoaded);
+jpegImageHigh.addEventListener('load', imageLoaded);
+jpegImageLow.addEventListener('load', imageLoaded);
+webpImageHigh.addEventListener('load', imageLoaded);
+webpImageLow.addEventListener('load', imageLoaded);
 
 var canvas = document.createElement("canvas");
 canvas.width = 50;
@@ -22,5 +36,22 @@
 canvas.toBlob(function(blob) {
   pngImage.src = URL.createObjectURL(blob);
 });
+
+canvas.toBlob(function(blob) {
+  jpegImageHigh.src = URL.createObjectURL(blob);
+}, "image/jpeg", 1.0);
+
+canvas.toBlob(function(blob) {
+    jpegImageLow.src = URL.createObjectURL(blob);
+}, "image/jpeg", 0.2)
+
+canvas.toBlob(function(blob) {
+    webpImageHigh.src = URL.createObjectURL(blob);
+}, "image/webp", 1.0);
+
+canvas.toBlob(function(blob) {
+    webpImageLow.src = URL.createObjectURL(blob);
+}, "image/webp", 0.2);
+
 </script>
 
diff --git a/third_party/WebKit/LayoutTests/virtual/threaded/fast/idleToBlob/OffscreenCanvas-convertToBlob-webgl-worker.html b/third_party/WebKit/LayoutTests/virtual/threaded/fast/idleToBlob/OffscreenCanvas-convertToBlob-webgl-worker.html
index a9be1b2..e0760ee 100644
--- a/third_party/WebKit/LayoutTests/virtual/threaded/fast/idleToBlob/OffscreenCanvas-convertToBlob-webgl-worker.html
+++ b/third_party/WebKit/LayoutTests/virtual/threaded/fast/idleToBlob/OffscreenCanvas-convertToBlob-webgl-worker.html
@@ -1,4 +1,8 @@
 <img id="png"/>
+<img id="jpeg-high"/>
+<img id="jpeg-low"/>
+<img id="webp-high"/>
+<img id="webp-low"/>
 <script id="myWorker" type="text/worker">
 self.onmessage = function (e) {
   var offCanvas = new OffscreenCanvas(50, 50);
@@ -6,12 +10,31 @@
   gl.clearColor(0, 1, 0, 1);
   gl.clear(gl.COLOR_BUFFER_BIT);
 
-  // TODO: Add more image types to this test once the toImageData() for webgl
-  // is completed. See crbug.com/657531.
   offCanvas.convertToBlob()
     .then(function(blob) { 
       self.postMessage({version: "png", data:blob});
     });
+
+  offCanvas.convertToBlob({type: "image/jpeg"})
+    .then(function(blob) {
+      self.postMessage({version: "jpeg-high", data:blob});
+    });
+
+  offCanvas.convertToBlob({type: "image/jpeg", quality: 0.2})
+    .then(function(blob) {
+      self.postMessage({version: "jpeg-low", data:blob});
+    });
+
+  offCanvas.convertToBlob({type: "image/webp"})
+    .then(function(blob) { 
+      self.postMessage({version: "webp-high", data:blob});
+    });
+
+  offCanvas.convertToBlob({type: "image/webp", quality: 0.2})
+    .then(function(blob) {
+
+      self.postMessage({version: "webp-low", data:blob});
+    });
 }
 </script>
 <script>
@@ -20,12 +43,22 @@
 }
 
 var pngImage = document.getElementById('png');
+var jpegImageHigh = document.getElementById('jpeg-high');
+var jpegImageLow = document.getElementById('jpeg-low');
+var webpImageHigh = document.getElementById('webp-high');
+var webpImageLow = document.getElementById('webp-low');
+var numTestCount = 5;
 function imageLoaded() {
-  if (window.testRunner) {
+  numTestCount--;
+  if (numTestCount == 0 && window.testRunner) {
     window.testRunner.notifyDone();
   }
 }
 pngImage.addEventListener('load', imageLoaded);
+jpegImageHigh.addEventListener('load', imageLoaded);
+jpegImageLow.addEventListener('load', imageLoaded);
+webpImageHigh.addEventListener('load', imageLoaded);
+webpImageLow.addEventListener('load', imageLoaded);
 
 var workerBlob = new Blob([document.getElementById('myWorker').textContent]);
 var worker = new Worker(URL.createObjectURL(workerBlob));
@@ -35,6 +68,18 @@
     case 'png':
       pngImage.src = URL.createObjectURL(blob);
       break;
+    case 'jpeg-high':
+      jpegImageHigh.src = URL.createObjectURL(blob);
+      break;
+    case 'jpeg-low':
+      jpegImageLow.src = URL.createObjectURL(blob);
+      break;
+    case 'webp-high':
+      webpImageHigh.src = URL.createObjectURL(blob);
+      break;
+    case 'webp-low':
+      webpImageLow.src = URL.createObjectURL(blob);
+      break;
   }
 });
 worker.postMessage("");
diff --git a/third_party/WebKit/LayoutTests/webaudio/Analyser/automatic-pull-node.html b/third_party/WebKit/LayoutTests/webaudio/Analyser/automatic-pull-node.html
index 840bad8..1772d72 100644
--- a/third_party/WebKit/LayoutTests/webaudio/Analyser/automatic-pull-node.html
+++ b/third_party/WebKit/LayoutTests/webaudio/Analyser/automatic-pull-node.html
@@ -63,7 +63,9 @@
   bufferSource.start(0);
 
   context.startRendering()
-    .then(test1Finished(should))
+    .then(function () {
+      test1Finished(should);
+    })
     .then(task.done.bind(task));
 });
 
@@ -88,7 +90,9 @@
   bufferSource.start(0);
 
   context.startRendering()
-    .then(test2Finished(should))
+    .then(function () {
+      test2Finished(should);
+    })
     .then(task.done.bind(task));
 });
 
@@ -113,7 +117,9 @@
   bufferSource.start(0);
 
   context.startRendering()
-    .then(test3Finished(should))
+    .then(function () {
+      test3Finished(should);
+    })
     .then(task.done.bind(task));
 });
 
diff --git a/third_party/WebKit/LayoutTests/webaudio/AudioBuffer/audiobuffer-expected.txt b/third_party/WebKit/LayoutTests/webaudio/AudioBuffer/audiobuffer-expected.txt
deleted file mode 100644
index 27eeccd6..0000000
--- a/third_party/WebKit/LayoutTests/webaudio/AudioBuffer/audiobuffer-expected.txt
+++ /dev/null
@@ -1,19 +0,0 @@
-Basic tests for AudioBuffer.
-
-On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
-
-
-PASS sampleRate has been set correctly.
-PASS length has been set correctly.
-PASS duration has been set correctly.
-PASS numberOfChannels has been set correctly.
-PASS getChannelData(0) returns a Float32Array object.
-PASS getChannelData(1) returns a Float32Array object.
-PASS getChannelData(2) returns a Float32Array object.
-PASS getChannelData(3) returns a Float32Array object.
-PASS Exception has been thrown correctly when index is not less than numberOfChannels.
-PASS duration has expected accuracy.
-PASS successfullyParsed is true
-
-TEST COMPLETE
-
diff --git a/third_party/WebKit/LayoutTests/webaudio/AudioBuffer/audiobuffer.html b/third_party/WebKit/LayoutTests/webaudio/AudioBuffer/audiobuffer.html
index 65abaa1..0f23cff2 100644
--- a/third_party/WebKit/LayoutTests/webaudio/AudioBuffer/audiobuffer.html
+++ b/third_party/WebKit/LayoutTests/webaudio/AudioBuffer/audiobuffer.html
@@ -1,62 +1,66 @@
 <!DOCTYPE html>
 <html>
 <head>
-<script src="../../resources/js-test.js"></script>
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
 <script src="../resources/audit-util.js"></script>
-<script src="../resources/audio-testing.js"></script>
+<script src="../resources/audit.js"></script>
 </head>
 <body>
 <script>
-description("Basic tests for AudioBuffer.");
+let sampleRate = 44100.0
+let lengthInSeconds = 2;
+let numberOfChannels = 4;
 
-var sampleRate = 44100.0
-var lengthInSeconds = 2;
-var numberOfChannels = 4;
+let audit = Audit.createTaskRunner();
 
-var context = new AudioContext();
-var buffer = context.createBuffer(numberOfChannels, sampleRate * lengthInSeconds, sampleRate);
+audit.define("Basic tests for AudioBuffer", function (task, should) {
+  let context = new AudioContext();
+  let buffer = context.createBuffer(numberOfChannels,
+    sampleRate * lengthInSeconds, sampleRate);
 
-if (buffer.sampleRate === sampleRate)
-    testPassed("sampleRate has been set correctly.");
-else
-    testFailed("sampleRate should be set correctly.");
+  // Just for printing out a message describing what "buffer" is in the
+  // following tests.
+  should(true,
+      "buffer = context.createBuffer(" + numberOfChannels + ", " + (
+        sampleRate * lengthInSeconds) + ", " + sampleRate + ")")
+    .beTrue();
 
-if (buffer.length === sampleRate * lengthInSeconds)
-    testPassed("length has been set correctly.");
-else
-    testFailed("length should be set correctly");
+  should(buffer.sampleRate, "buffer.sampleRate")
+    .beEqualTo(sampleRate);
 
-if (buffer.duration === lengthInSeconds)
-    testPassed("duration has been set correctly.");
-else
-    testFailed("duration should be set correctly.");
+  should(buffer.length, "buffer.length")
+    .beEqualTo(sampleRate * lengthInSeconds);
 
-if (buffer.numberOfChannels === numberOfChannels)
-    testPassed("numberOfChannels has been set correctly.");
-else
-    testFailed("numberOfChannels should be set correctly.");
+  should(buffer.duration, "buffer.duration")
+    .beEqualTo(lengthInSeconds);
 
-for (var index = 0; index < buffer.numberOfChannels; ++index) {
-    if (buffer.getChannelData(index) instanceof window.Float32Array)
-        testPassed("getChannelData(" + index + ") returns a Float32Array object.");
-    else
-        testFailed("getChannelData(" + index + ") should return a Float32Array object.");
-}
+  should(buffer.numberOfChannels, "buffer.numberOfChannels")
+    .beEqualTo(numberOfChannels);
 
-try {
-    buffer.getChannelData(buffer.numberOfChannels);
-    testFailed("Exception should be thrown when index is not less than numberOfChannels.");
-} catch(e) {
-    testPassed("Exception has been thrown correctly when index is not less than numberOfChannels.");
-}
+  for (let index = 0; index < buffer.numberOfChannels; ++index) {
+    should(
+        buffer.getChannelData(index) instanceof window.Float32Array,
+        "buffer.getChannelData(" + index +
+        ") instanceof window.Float32Array")
+      .beTrue();
+  }
 
-var buffer2 = context.createBuffer(1, 1000, 24576);
-var expectedDuration = 1000/24576;
+  should(function () {
+      buffer.getChannelData(buffer.numberOfChannels);
+    }, "buffer.getChannelData(" + buffer.numberOfChannels + ")")
+    .throw("IndexSizeError");
 
-if (buffer2.duration == expectedDuration)
-    testPassed("duration has expected accuracy.");
-else
-    testFailed("duration is " + buffer2.duration + " sec instead of " + expectedDuration + " sec.");
+  let buffer2 = context.createBuffer(1, 1000, 24576);
+  let expectedDuration = 1000 / 24576;
+
+  should(buffer2.duration, "context.createBuffer(1, 1000, 24576).duration")
+    .beEqualTo(expectedDuration);
+
+  task.done();
+});
+
+audit.run();
 </script>
 
 </body>
diff --git a/third_party/WebKit/LayoutTests/webaudio/AudioBufferSource/audiobuffersource-channels-expected.txt b/third_party/WebKit/LayoutTests/webaudio/AudioBufferSource/audiobuffersource-channels-expected.txt
deleted file mode 100644
index 2263542..0000000
--- a/third_party/WebKit/LayoutTests/webaudio/AudioBufferSource/audiobuffersource-channels-expected.txt
+++ /dev/null
@@ -1,19 +0,0 @@
-Tests that AudioBufferSourceNode validates AudioBuffer in .buffer attribute setter.
-
-On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
-
-PASS source.buffer = 57 threw exception TypeError: Failed to set the 'buffer' property on 'AudioBufferSourceNode': The provided value is not of type 'AudioBuffer'..
-PASS source.buffer = null threw exception TypeError: Failed to set the 'buffer' property on 'AudioBufferSourceNode': The provided value is not of type 'AudioBuffer'..
-PASS Mono buffer can be set.
-PASS Stereo buffer can be set.
-PASS 3 channels buffer can be set.
-PASS 4 channels buffer can be set.
-PASS 5 channels buffer can be set.
-PASS 6 channels buffer can be set.
-PASS 7 channels buffer can be set.
-PASS 8 channels buffer can be set.
-PASS 9 channels buffer can be set.
-PASS successfullyParsed is true
-
-TEST COMPLETE
-
diff --git a/third_party/WebKit/LayoutTests/webaudio/AudioBufferSource/audiobuffersource-channels.html b/third_party/WebKit/LayoutTests/webaudio/AudioBufferSource/audiobuffersource-channels.html
index f123103..2fecbc4 100644
--- a/third_party/WebKit/LayoutTests/webaudio/AudioBufferSource/audiobuffersource-channels.html
+++ b/third_party/WebKit/LayoutTests/webaudio/AudioBufferSource/audiobuffersource-channels.html
@@ -2,74 +2,66 @@
 
 <html>
 <head>
-<script src="../../resources/js-test.js"></script>
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
 <script src="../resources/audit-util.js"></script>
-<script src="../resources/audio-testing.js"></script>
+<script src="../resources/audit.js"></script>
 </head>
 
 <body>
-<div id="description"></div>
-<div id="console"></div>
-
 <script>
-description("Tests that AudioBufferSourceNode validates AudioBuffer in .buffer attribute setter.");
+let audit = Audit.createTaskRunner();
+let context;
+let source;
 
-var context;
-var source;
-
-function runTest() {
-    if (window.testRunner) {
-        testRunner.dumpAsText();
-        testRunner.waitUntilDone();
-    }
-    
-    window.jsTestIsAsync = true;
-    
+audit.define("validate .buffer",
+  function(task, should) {
+    task.describe(
+      "AudioBufferSourceNode validates AudioBuffer in .buffer attribute setter"
+    );
     context = new AudioContext();
     source = context.createBufferSource();
 
     // Make sure we can't set to something which isn't an AudioBuffer.
-    shouldThrow("source.buffer = 57", '"TypeError: Failed to set the \'buffer\' property on \'AudioBufferSourceNode\': The provided value is not of type \'AudioBuffer\'."');
-    shouldThrow("source.buffer = null", '"TypeError: Failed to set the \'buffer\' property on \'AudioBufferSourceNode\': The provided value is not of type \'AudioBuffer\'."');
+    should(function() {
+        source.buffer = 57;
+      }, "source.buffer = 57")
+      .throw("TypeError");
+
+    should(function() {
+        source.buffer = null;
+      }, "source.buffer = null")
+      .throw("TypeError");
 
     // Check that mono buffer can be set.
-    try {
-        var monoBuffer = context.createBuffer(1, 1024, context.sampleRate);
-        var testSource = context.createBufferSource();
+    should(function() {
+        let monoBuffer = context.createBuffer(1, 1024, context.sampleRate);
+        let testSource = context.createBufferSource();
         testSource.buffer = monoBuffer;
-        testPassed("Mono buffer can be set.");
-    } catch(e) {
-        testFailed("Mono buffer can not be set.");
-    }
+      }, "Setting source with mono buffer")
+      .notThrow();
 
     // Check that stereo buffer can be set.
-    try {
-        var stereoBuffer = context.createBuffer(2, 1024, context.sampleRate);
-        var testSource = context.createBufferSource();
+    should(function() {
+        let stereoBuffer = context.createBuffer(2, 1024, context.sampleRate);
+        let testSource = context.createBufferSource();
         testSource.buffer = stereoBuffer;
-        testPassed("Stereo buffer can be set.");
-    } catch(e) {
-        testFailed("Stereo buffer can not be set.");
-    }
-    
-    // Check buffers with more than two channels.
-    for (var i = 3; i < 10; ++i) {
-        try {
-            var buffer = context.createBuffer(i, 1024, context.sampleRate);
-            var testSource = context.createBufferSource();
-            testSource.buffer = buffer;
-            var message = i + " channels buffer can be set.";
-            testPassed(message);
-        } catch(e) {
-            var message = i + " channels buffer can not be set.";
-            testFailed(message);
-        }
-    }
-        
-    finishJSTest();
-}
+      }, "Setting source with stereo buffer")
+      .notThrow();
 
-runTest();
+    // Check buffers with more than two channels.
+    for (let i = 3; i < 10; ++i) {
+      should(function() {
+          let buffer = context.createBuffer(i, 1024, context.sampleRate);
+          let testSource = context.createBufferSource();
+          testSource.buffer = buffer;
+        }, "Setting source with " + i + " channels buffer")
+        .notThrow();
+    }
+    task.done();
+  });
+
+audit.run();
 
 </script>
 
diff --git a/third_party/WebKit/LayoutTests/webaudio/AudioBufferSource/audiobuffersource-grain-expected.txt b/third_party/WebKit/LayoutTests/webaudio/AudioBufferSource/audiobuffersource-grain-expected.txt
deleted file mode 100644
index 619558d1..0000000
--- a/third_party/WebKit/LayoutTests/webaudio/AudioBufferSource/audiobuffersource-grain-expected.txt
+++ /dev/null
@@ -1,10 +0,0 @@
-Test setting the source buffer after starting the grain
-
-On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
-
-
-PASS Buffer was played.
-PASS successfullyParsed is true
-
-TEST COMPLETE
-
diff --git a/third_party/WebKit/LayoutTests/webaudio/AudioBufferSource/audiobuffersource-grain.html b/third_party/WebKit/LayoutTests/webaudio/AudioBufferSource/audiobuffersource-grain.html
index a50b108..0ac2da6b 100644
--- a/third_party/WebKit/LayoutTests/webaudio/AudioBufferSource/audiobuffersource-grain.html
+++ b/third_party/WebKit/LayoutTests/webaudio/AudioBufferSource/audiobuffersource-grain.html
@@ -2,31 +2,30 @@
 <html>
   <head>
     <title>Test Start Grain with Delayed Buffer Setting </title>
-    <script src="../../resources/js-test.js"></script>
+    <script src="../../resources/testharness.js"></script>
+    <script src="../../resources/testharnessreport.js"></script>
     <script src="../resources/audit-util.js"></script>
-    <script src="../resources/audio-testing.js"></script>
+    <script src="../resources/audit.js"></script>
   </head>
 
   <body>
     <script>
-      description("Test setting the source buffer after starting the grain");
+      let audit = Audit.createTaskRunner();
+      let context;
+      let source;
+      let buffer;
+      let renderedData;
 
-      var context;
-      var source;
-      var buffer;
-      var renderedData;
+      let sampleRate = 44100;
 
-      var sampleRate = 44100;
+      let testDurationSec = 1;
+      let testDurationSamples = testDurationSec * sampleRate;
+      let startTime = 0.9 * testDurationSec;
 
-      var testDurationSec = 1;
-      var testDurationSamples = testDurationSec * sampleRate;
-      var startTime = 0.9 * testDurationSec;
-
-      function runTest() {
-          window.jsTestIsAsync = true;
-
-          context = new OfflineAudioContext(1, testDurationSamples, sampleRate);
-          context.oncomplete = checkResult;
+      audit.define("Test setting the source buffer after starting the grain",
+        function (task, should) {
+          context = new OfflineAudioContext(1, testDurationSamples,
+            sampleRate);
 
           buffer = createConstantBuffer(context, testDurationSamples, 1);
           source = context.createBufferSource();
@@ -38,34 +37,32 @@
           source.buffer = buffer;
 
           // Render it!
-          context.startRendering();
-      }
+          context.startRendering()
+            .then(function (buffer) {
+              checkResult(buffer, should);
+            })
+            .then(task.done.bind(task));;
+        });
 
-      function checkResult(event) {
-          var success = false;
+      function checkResult(buffer, should) {
+        let success = false;
 
-          renderedData = event.renderedBuffer.getChannelData(0);
+        renderedData = buffer.getChannelData(0);
 
-          // Check that the rendered data is not all zeroes.  Any non-zero data means the test
-          // passed.
-          var startFrame = Math.round(startTime * sampleRate);
-          for (k = 0; k < renderedData.length; ++k) {
-              if (renderedData[k]) {
-                  success = true;
-                  break;
-              }
+        // Check that the rendered data is not all zeroes.  Any non-zero data means the test
+        // passed.
+        let startFrame = Math.round(startTime * sampleRate);
+        for (k = 0; k < renderedData.length; ++k) {
+          if (renderedData[k]) {
+            success = true;
+            break;
           }
+        }
 
-          if (success)
-              testPassed("Buffer was played.");
-          else
-              testFailed("Buffer was not played.");
-
-          finishJSTest();
+        should(success, "Buffer was played").beTrue();
       }
 
-      runTest();
-      successfullyParsed = true;
+      audit.run();
     </script>
   </body>
 </html>
diff --git a/third_party/WebKit/LayoutTests/webaudio/AudioBufferSource/audiobuffersource-one-sample-loop-expected.txt b/third_party/WebKit/LayoutTests/webaudio/AudioBufferSource/audiobuffersource-one-sample-loop-expected.txt
deleted file mode 100644
index a3634ee..0000000
--- a/third_party/WebKit/LayoutTests/webaudio/AudioBufferSource/audiobuffersource-one-sample-loop-expected.txt
+++ /dev/null
@@ -1,10 +0,0 @@
-Test AudioBufferSourceNode With Looping a Single-Sample Buffer
-
-On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
-
-
-PASS All samples equal to 1
-PASS successfullyParsed is true
-
-TEST COMPLETE
-
diff --git a/third_party/WebKit/LayoutTests/webaudio/AudioBufferSource/audiobuffersource-one-sample-loop.html b/third_party/WebKit/LayoutTests/webaudio/AudioBufferSource/audiobuffersource-one-sample-loop.html
index 0e824a3..af2366d4 100644
--- a/third_party/WebKit/LayoutTests/webaudio/AudioBufferSource/audiobuffersource-one-sample-loop.html
+++ b/third_party/WebKit/LayoutTests/webaudio/AudioBufferSource/audiobuffersource-one-sample-loop.html
@@ -2,63 +2,44 @@
 <html>
   <head>
     <title>Test AudioBufferSourceNode With Looping a Single-Sample Buffer</title>
-    <script src="../../resources/js-test.js"></script>
+    <script src="../../resources/testharness.js"></script>
+    <script src="../../resources/testharnessreport.js"></script>
     <script src="../resources/audit-util.js"></script>
-    <script src="../resources/audio-testing.js"></script>
+    <script src="../resources/audit.js"></script>
   </head>
 
   <body>
     <script>
-      description("Test AudioBufferSourceNode With Looping a Single-Sample Buffer");
+      let audit = Audit.createTaskRunner();
 
-      var context;
-      var source;
-      var buffer;
-      var renderedData;
+      let sampleRate = 44100;
+      let testDurationSamples = 1000;
 
-      var sampleRate = 44100;
-      var testDurationSamples = 1000;
+      audit.define("one-sample-loop", function (task, should) {
+        // Create the offline context for the test.
+        let context = new OfflineAudioContext(1, testDurationSamples,
+          sampleRate);
 
-      function checkResult (event) {
-          var success = true;
+        // Create the single sample buffer
+        let buffer = createConstantBuffer(context, 1, 1);
 
-          renderedData = event.renderedBuffer.getChannelData(0);
-          // Check that the rendered data is all ones, like the buffer source.
-          for (k = 0; k < renderedData.length; ++k) {
-              if (renderedData[k] != 1) {
-                  success = false;
-                  testFailed("Expected all ones, but sample " + k + " is " + renderedData[k]);
-                  break;
-              }
-          }
-          if (success)
-              testPassed("All samples equal to 1");
-          finishJSTest();
-      }
+        // Create the source and connect it to the destination
+        let source = context.createBufferSource();
+        source.buffer = buffer;
+        source.loop = true;
+        source.connect(context.destination);
+        source.start();
 
-      function runTest() {
-          window.jsTestIsAsync = true;
+        // Render it!
+        context.startRendering()
+          .then(function(audioBuffer) {
+            should(audioBuffer.getChannelData(0), "Rendered data")
+              .beConstantValueOf(1);
+          })
+          .then(task.done.bind(task));;
+      });
 
-          // Create the offline context for the test.
-          context = new OfflineAudioContext(1, testDurationSamples, sampleRate);
-          context.oncomplete = checkResult;
-
-          // Create the single sample buffer
-          buffer = createConstantBuffer(context, 1, 1);
-
-          // Create the source and connect it to the destination
-          source = context.createBufferSource();
-          source.buffer = buffer;
-          source.loop = true;
-          source.connect(context.destination);
-          source.start();
-
-          // Render it!
-          context.startRendering();
-      }
-
-      runTest();
-      succesfullyParsed = true;
+      audit.run();
     </script>
   </body>
 </html>
diff --git a/third_party/WebKit/Source/core/frame/FrameView.cpp b/third_party/WebKit/Source/core/frame/FrameView.cpp
index 7befa59..e28f3e7 100644
--- a/third_party/WebKit/Source/core/frame/FrameView.cpp
+++ b/third_party/WebKit/Source/core/frame/FrameView.cpp
@@ -4552,6 +4552,14 @@
 
 void FrameView::updateViewportIntersectionsForSubtree(
     DocumentLifecycle::LifecycleState targetState) {
+  // TODO(dcheng): Since widget tree updates are deferred, FrameViews might
+  // still be in the widget hierarchy even though the associated Document is
+  // already detached. Investigate if this check and a similar check in
+  // lifecycle updates are still needed when there are no more deferred widget
+  // updates: https://crbug.com/561683
+  if (!frame().document()->isActive())
+    return;
+
   // Notify javascript IntersectionObservers
   if (targetState == DocumentLifecycle::PaintClean &&
       frame().document()->intersectionObserverController())
diff --git a/third_party/WebKit/Source/core/html/canvas/CanvasRenderingContext.h b/third_party/WebKit/Source/core/html/canvas/CanvasRenderingContext.h
index e6a3d891..7aa0815 100644
--- a/third_party/WebKit/Source/core/html/canvas/CanvasRenderingContext.h
+++ b/third_party/WebKit/Source/core/html/canvas/CanvasRenderingContext.h
@@ -92,9 +92,7 @@
 
   virtual PassRefPtr<Image> getImage(AccelerationHint,
                                      SnapshotReason) const = 0;
-  virtual ImageData* toImageData(SnapshotReason reason) const {
-    return nullptr;
-  }
+  virtual ImageData* toImageData(SnapshotReason reason) { return nullptr; }
   virtual ContextType getContextType() const = 0;
   virtual bool isAccelerated() const { return false; }
   virtual bool shouldAntialias() const { return false; }
diff --git a/third_party/WebKit/Source/core/layout/LayoutTextTrackContainer.cpp b/third_party/WebKit/Source/core/layout/LayoutTextTrackContainer.cpp
index fd075d22..67532156 100644
--- a/third_party/WebKit/Source/core/layout/LayoutTextTrackContainer.cpp
+++ b/third_party/WebKit/Source/core/layout/LayoutTextTrackContainer.cpp
@@ -69,10 +69,10 @@
   // the behavior is currently not portable. fontSize may have precision higher
   // than m_fontSize thus straight comparison can fail despite they cast to the
   // same float value.
-  volatile float& m_fontSize = this->m_fontSize;
-  float oldFontSize = m_fontSize;
-  m_fontSize = fontSize;
-  return m_fontSize != oldFontSize;
+  volatile float& currentFontSize = m_fontSize;
+  float oldFontSize = currentFontSize;
+  currentFontSize = fontSize;
+  return currentFontSize != oldFontSize;
 }
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/core/paint/InlineTextBoxPainter.cpp b/third_party/WebKit/Source/core/paint/InlineTextBoxPainter.cpp
index cb57b318..2b15321b 100644
--- a/third_party/WebKit/Source/core/paint/InlineTextBoxPainter.cpp
+++ b/third_party/WebKit/Source/core/paint/InlineTextBoxPainter.cpp
@@ -559,11 +559,21 @@
   if (respectHyphen)
     selectionEnd = textRun.length();
 
+  bool ltr = m_inlineTextBox.isLeftToRightDirection();
+  bool flowIsLTR =
+      m_inlineTextBox.getLineLayoutItem().style()->isLeftToRightDirection();
   if (m_inlineTextBox.truncation() != cNoTruncation) {
+    // In a mixed-direction flow the ellipsis is at the start of the text
+    // rather than at the end of it.
     selectionStart =
-        std::min<int>(selectionStart, m_inlineTextBox.truncation());
-    selectionEnd = std::min<int>(selectionEnd, m_inlineTextBox.truncation());
-    length = m_inlineTextBox.truncation();
+        ltr == flowIsLTR
+            ? std::min<int>(selectionStart, m_inlineTextBox.truncation())
+            : std::max<int>(selectionStart, m_inlineTextBox.truncation());
+    selectionEnd =
+        ltr == flowIsLTR
+            ? std::min<int>(selectionEnd, m_inlineTextBox.truncation())
+            : std::max<int>(selectionEnd, m_inlineTextBox.truncation());
+    length = ltr == flowIsLTR ? m_inlineTextBox.truncation() : textRun.length();
   }
 
   TextPainter textPainter(context, font, textRun, textOrigin, boxRect,
@@ -576,27 +586,25 @@
                                 emphasisMarkPosition);
   if (combinedText)
     textPainter.setCombinedText(combinedText);
+  if (m_inlineTextBox.truncation() != cNoTruncation && ltr != flowIsLTR)
+    textPainter.setEllipsisOffset(m_inlineTextBox.truncation());
 
   if (!paintSelectedTextOnly) {
     int startOffset = 0;
     int endOffset = length;
-    if (paintSelectedTextSeparately && selectionStart < selectionEnd) {
-      startOffset = selectionEnd;
-      endOffset = selectionStart;
-    }
     // Where the text and its flow have opposite directions then our offset into
     // the text given by |truncation| is at the start of the part that will be
     // visible.
-    if (m_inlineTextBox.truncation() != cNoTruncation &&
-        m_inlineTextBox.getLineLayoutItem()
-                .containingBlock()
-                .style()
-                ->isLeftToRightDirection() !=
-            m_inlineTextBox.isLeftToRightDirection()) {
+    if (m_inlineTextBox.truncation() != cNoTruncation && ltr != flowIsLTR) {
       startOffset = m_inlineTextBox.truncation();
       endOffset = textRun.length();
     }
 
+    if (paintSelectedTextSeparately && selectionStart < selectionEnd) {
+      startOffset = selectionEnd;
+      endOffset = selectionStart;
+    }
+
     // FIXME: This cache should probably ultimately be held somewhere else.
     // A hashmap is convenient to avoid a memory hit when the
     // RuntimeEnabledFeature is off.
@@ -945,11 +953,13 @@
   bool flowIsLTR =
       m_inlineTextBox.getLineLayoutItem().style()->isLeftToRightDirection();
   if (m_inlineTextBox.truncation() != cNoTruncation) {
-    start = ltr == flowIsLTR ? m_inlineTextBox.start()
-                             : m_inlineTextBox.truncation();
-    length = ltr == flowIsLTR
-                 ? m_inlineTextBox.truncation()
-                 : m_inlineTextBox.len() - m_inlineTextBox.truncation();
+    // In a mixed-direction flow the ellipsis is at the start of the text
+    // so we need to start after it. Otherwise we just need to make sure
+    // the end of the text is where the ellipsis starts.
+    if (ltr != flowIsLTR)
+      sPos = std::max<int>(sPos, m_inlineTextBox.truncation());
+    else
+      length = m_inlineTextBox.truncation();
   }
   StringView string(m_inlineTextBox.getLineLayoutItem().text(), start,
                     static_cast<unsigned>(length));
@@ -988,10 +998,12 @@
                          (boxRect.y() - deltaY).toFloat());
   LayoutRect selectionRect = LayoutRect(
       font.selectionRectForText(textRun, localOrigin, selHeight, sPos, ePos));
-  if (m_inlineTextBox.hasWrappedSelectionNewline()
-      // For line breaks, just painting a selection where the line break itself
-      // is rendered is sufficient.
-      && !m_inlineTextBox.isLineBreak())
+  // For line breaks, just painting a selection where the line break itself
+  // is rendered is sufficient. Don't select it if there's an ellipsis
+  // there.
+  if (m_inlineTextBox.hasWrappedSelectionNewline() &&
+      m_inlineTextBox.truncation() == cNoTruncation &&
+      !m_inlineTextBox.isLineBreak())
     expandToIncludeNewlineForSelection(selectionRect);
 
   // Line breaks report themselves as having zero width for layout purposes,
@@ -1001,7 +1013,7 @@
   if (!m_inlineTextBox.isLeftToRightDirection() &&
       m_inlineTextBox.isLineBreak())
     selectionRect.move(-selectionRect.width(), LayoutUnit());
-  if (!flowIsLTR && m_inlineTextBox.truncation() != cNoTruncation)
+  if (!flowIsLTR && !ltr && m_inlineTextBox.truncation() != cNoTruncation)
     selectionRect.move(m_inlineTextBox.logicalWidth() - selectionRect.width(),
                        LayoutUnit());
 
@@ -1038,8 +1050,9 @@
     bool flowIsLTR =
         m_inlineTextBox.getLineLayoutItem().style()->isLeftToRightDirection();
     width = LayoutUnit(m_inlineTextBox.getLineLayoutItem().width(
-        ltr == flowIsLTR ? m_inlineTextBox.start()
-                         : m_inlineTextBox.truncation(),
+        ltr == flowIsLTR
+            ? m_inlineTextBox.start()
+            : m_inlineTextBox.start() + m_inlineTextBox.truncation(),
         ltr == flowIsLTR ? m_inlineTextBox.truncation()
                          : m_inlineTextBox.len() - m_inlineTextBox.truncation(),
         m_inlineTextBox.textPos(),
diff --git a/third_party/WebKit/Source/core/paint/TextPainter.cpp b/third_party/WebKit/Source/core/paint/TextPainter.cpp
index 95c02bd..36a1f3b 100644
--- a/third_party/WebKit/Source/core/paint/TextPainter.cpp
+++ b/third_party/WebKit/Source/core/paint/TextPainter.cpp
@@ -36,7 +36,8 @@
       m_textBounds(textBounds),
       m_horizontal(horizontal),
       m_emphasisMarkOffset(0),
-      m_combinedText(0) {}
+      m_combinedText(0),
+      m_ellipsisOffset(0) {}
 
 TextPainter::~TextPainter() {}
 
@@ -247,7 +248,7 @@
     paintInternalRun<Step>(textRunPaintInfo, startOffset, endOffset);
   } else {
     if (endOffset > 0)
-      paintInternalRun<Step>(textRunPaintInfo, 0, endOffset);
+      paintInternalRun<Step>(textRunPaintInfo, m_ellipsisOffset, endOffset);
     if (startOffset < truncationPoint)
       paintInternalRun<Step>(textRunPaintInfo, startOffset, truncationPoint);
   }
diff --git a/third_party/WebKit/Source/core/paint/TextPainter.h b/third_party/WebKit/Source/core/paint/TextPainter.h
index 48a87e9..a3d1de2 100644
--- a/third_party/WebKit/Source/core/paint/TextPainter.h
+++ b/third_party/WebKit/Source/core/paint/TextPainter.h
@@ -47,6 +47,7 @@
   void setCombinedText(LayoutTextCombine* combinedText) {
     m_combinedText = combinedText;
   }
+  void setEllipsisOffset(int offset) { m_ellipsisOffset = offset; }
 
   static void updateGraphicsContext(GraphicsContext&,
                                     const Style&,
@@ -117,6 +118,7 @@
   AtomicString m_emphasisMark;
   int m_emphasisMarkOffset;
   LayoutTextCombine* m_combinedText;
+  int m_ellipsisOffset;
 };
 
 inline AffineTransform TextPainter::rotation(
diff --git a/third_party/WebKit/Source/devtools/front_end/bindings/DebuggerWorkspaceBinding.js b/third_party/WebKit/Source/devtools/front_end/bindings/DebuggerWorkspaceBinding.js
index ebc3a428..84ebe85a 100644
--- a/third_party/WebKit/Source/devtools/front_end/bindings/DebuggerWorkspaceBinding.js
+++ b/third_party/WebKit/Source/devtools/front_end/bindings/DebuggerWorkspaceBinding.js
@@ -356,6 +356,8 @@
       debuggerModel.addEventListener(SDK.DebuggerModel.Events.ParsedScriptSource, this._parsedScriptSource, this),
       debuggerModel.addEventListener(
           SDK.DebuggerModel.Events.FailedToParseScriptSource, this._parsedScriptSource, this),
+      debuggerModel.addEventListener(
+          SDK.DebuggerModel.Events.DiscardedAnonymousScriptSource, this._discardedScriptSource, this)
     ];
   }
 
@@ -380,6 +382,14 @@
   }
 
   /**
+   * @param {!Common.Event} event
+   */
+  _discardedScriptSource(event) {
+    var script = /** @type {!SDK.Script} */ (event.data);
+    this._defaultMapping.removeScript(script);
+  }
+
+  /**
    * @param {!Workspace.UISourceCode} uiSourceCode
    * @param {?Bindings.DebuggerSourceMapping} sourceMapping
    */
diff --git a/third_party/WebKit/Source/devtools/front_end/bindings/DefaultScriptMapping.js b/third_party/WebKit/Source/devtools/front_end/bindings/DefaultScriptMapping.js
index 10407e2..37b06eb 100644
--- a/third_party/WebKit/Source/devtools/front_end/bindings/DefaultScriptMapping.js
+++ b/third_party/WebKit/Source/devtools/front_end/bindings/DefaultScriptMapping.js
@@ -112,6 +112,18 @@
   }
 
   /**
+   * @param {!SDK.Script} script
+   */
+  removeScript(script) {
+    var uiSourceCode = script[Bindings.DefaultScriptMapping._uiSourceCodeSymbol];
+    if (!uiSourceCode)
+      return;
+    delete script[Bindings.DefaultScriptMapping._uiSourceCodeSymbol];
+    delete uiSourceCode[Bindings.DefaultScriptMapping._scriptSymbol];
+    this._project.removeUISourceCode(uiSourceCode.url());
+  }
+
+  /**
    * @override
    * @return {boolean}
    */
diff --git a/third_party/WebKit/Source/devtools/front_end/sdk/DebuggerModel.js b/third_party/WebKit/Source/devtools/front_end/sdk/DebuggerModel.js
index 6593e49..4de10f21 100644
--- a/third_party/WebKit/Source/devtools/front_end/sdk/DebuggerModel.js
+++ b/third_party/WebKit/Source/devtools/front_end/sdk/DebuggerModel.js
@@ -47,6 +47,8 @@
     this._scripts = {};
     /** @type {!Map.<string, !Array.<!SDK.Script>>} */
     this._scriptsBySourceURL = new Map();
+    /** @type {!Array.<!SDK.Script>} */
+    this._discardableScripts = [];
 
     /** @type {!Common.Object} */
     this._breakpointResolvedEventTarget = new Common.Object();
@@ -323,6 +325,7 @@
     this._scripts = {};
     this._scriptsBySourceURL.clear();
     this._stringMap.clear();
+    this._discardableScripts = [];
   }
 
   /**
@@ -494,13 +497,18 @@
       sourceURL = this._internString(sourceURL);
     }
     var script = new SDK.Script(
-        this, scriptId, sourceURL, startLine, startColumn, endLine, endColumn, executionContextId, this._internString(hash),
-        isContentScript, isLiveEdit, sourceMapURL, hasSourceURL);
+        this, scriptId, sourceURL, startLine, startColumn, endLine, endColumn, executionContextId,
+        this._internString(hash), isContentScript, isLiveEdit, sourceMapURL, hasSourceURL);
     this._registerScript(script);
     if (!hasSyntaxError)
       this.dispatchEventToListeners(SDK.DebuggerModel.Events.ParsedScriptSource, script);
     else
       this.dispatchEventToListeners(SDK.DebuggerModel.Events.FailedToParseScriptSource, script);
+    var isDiscardable = hasSyntaxError && script.isAnonymousScript();
+    if (isDiscardable) {
+      this._discardableScripts.push(script);
+      this._collectDiscardedScripts();
+    }
     return script;
   }
 
@@ -522,6 +530,24 @@
 
   /**
    * @param {!SDK.Script} script
+   */
+  _unregisterScript(script) {
+    console.assert(script.isAnonymousScript());
+    delete this._scripts[script.scriptId];
+  }
+
+  _collectDiscardedScripts() {
+    if (this._discardableScripts.length < 1000)
+      return;
+    var scriptsToDiscard = this._discardableScripts.splice(0, 100);
+    for (var script of scriptsToDiscard) {
+      this._unregisterScript(script);
+      this.dispatchEventToListeners(SDK.DebuggerModel.Events.DiscardedAnonymousScriptSource, script);
+    }
+  }
+
+  /**
+   * @param {!SDK.Script} script
    * @param {number} lineNumber
    * @param {number} columnNumber
    * @return {?SDK.DebuggerModel.Location}
@@ -804,9 +830,9 @@
    * @return {string} string
    */
   _internString(string) {
-     if (!this._stringMap.has(string))
-       this._stringMap.set(string, string);
-     return this._stringMap.get(string);
+    if (!this._stringMap.has(string))
+      this._stringMap.set(string, string);
+    return this._stringMap.get(string);
   }
 };
 
@@ -832,6 +858,7 @@
   DebuggerResumed: Symbol('DebuggerResumed'),
   ParsedScriptSource: Symbol('ParsedScriptSource'),
   FailedToParseScriptSource: Symbol('FailedToParseScriptSource'),
+  DiscardedAnonymousScriptSource: Symbol('DiscardedAnonymousScriptSource'),
   GlobalObjectCleared: Symbol('GlobalObjectCleared'),
   CallFrameSelected: Symbol('CallFrameSelected'),
   ConsoleCommandEvaluatedInSelectedCallFrame: Symbol('ConsoleCommandEvaluatedInSelectedCallFrame'),
diff --git a/third_party/WebKit/Source/devtools/front_end/timeline/TimelineFlameChartView.js b/third_party/WebKit/Source/devtools/front_end/timeline/TimelineFlameChartView.js
index 0b4d4d7..c638759 100644
--- a/third_party/WebKit/Source/devtools/front_end/timeline/TimelineFlameChartView.js
+++ b/third_party/WebKit/Source/devtools/front_end/timeline/TimelineFlameChartView.js
@@ -129,8 +129,15 @@
     this._networkView.alwaysShowVerticalScroll();
     networkViewGroupExpansionSetting.addChangeListener(this.resizeToPreferredHeights.bind(this));
 
+    const networkPane = new UI.VBox();
+    networkPane.setMinimumSize(23, 23);
+    this._networkView.show(networkPane.element);
+    this._splitResizer = networkPane.element.createChild('div', 'timeline-flamechart-resizer');
+    this._splitWidget.hideDefaultResizer();
+    this._splitWidget.installResizer(this._splitResizer);
+
     this._splitWidget.setMainWidget(this._mainView);
-    this._splitWidget.setSidebarWidget(this._networkView);
+    this._splitWidget.setSidebarWidget(networkPane);
     this._splitWidget.show(this.element);
 
     this._onMainEntrySelected = this._onEntrySelected.bind(this, this._dataProvider);
@@ -288,7 +295,7 @@
   }
 
   resizeToPreferredHeights() {
-    this._splitWidget.setSidebarSize(this._networkDataProvider.preferredHeight());
+    this._splitWidget.setSidebarSize(this._networkDataProvider.preferredHeight() + this._splitResizer.clientHeight);
   }
 };
 
diff --git a/third_party/WebKit/Source/devtools/front_end/timeline/TimelineNetworkFlameChart.js b/third_party/WebKit/Source/devtools/front_end/timeline/TimelineNetworkFlameChart.js
index 99b668d..1243f468 100644
--- a/third_party/WebKit/Source/devtools/front_end/timeline/TimelineNetworkFlameChart.js
+++ b/third_party/WebKit/Source/devtools/front_end/timeline/TimelineNetworkFlameChart.js
@@ -349,7 +349,7 @@
    * @return {number}
    */
   preferredHeight() {
-    return this._style.height * (this._group.expanded ? Number.constrain(this._maxLevel + 1, 4, 8) : 2) + 2;
+    return this._style.height * (this._group.expanded ? Number.constrain(this._maxLevel + 1, 4, 8) : 2);
   }
 
   /**
diff --git a/third_party/WebKit/Source/devtools/front_end/timeline/TimelinePanel.js b/third_party/WebKit/Source/devtools/front_end/timeline/TimelinePanel.js
index 0bbceac..81484a6 100644
--- a/third_party/WebKit/Source/devtools/front_end/timeline/TimelinePanel.js
+++ b/third_party/WebKit/Source/devtools/front_end/timeline/TimelinePanel.js
@@ -1283,6 +1283,7 @@
     this._defaultDetailsWidget.element.classList.add('timeline-details-view');
     this._defaultDetailsContentElement =
         this._defaultDetailsWidget.element.createChild('div', 'timeline-details-view-body vbox');
+    this._defaultDetailsContentElement.tabIndex = 0;
     this.appendTab(tabIds.Details, Common.UIString('Summary'), this._defaultDetailsWidget);
     this.setPreferredTab(tabIds.Details);
 
diff --git a/third_party/WebKit/Source/devtools/front_end/timeline/timelinePanel.css b/third_party/WebKit/Source/devtools/front_end/timeline/timelinePanel.css
index 79fa08f3..021b1b1 100644
--- a/third_party/WebKit/Source/devtools/front_end/timeline/timelinePanel.css
+++ b/third_party/WebKit/Source/devtools/front_end/timeline/timelinePanel.css
@@ -440,6 +440,22 @@
     overflow: hidden;
 }
 
+.timeline-flamechart-resizer {
+    height: 7px;
+    background-color: #f3f3f3;
+    border-top: #a3a3a3 1px solid;
+    display: flex;
+    flex-direction: row;
+    align-items: flex-end;
+    justify-content: center;
+}
+
+.timeline-flamechart-resizer:after {
+    content: "...";
+    font-size: 14px;
+    margin-bottom: -1px;
+}
+
 .timeline-status-pane.full-widget-dimmed-banner {
     text-align: left !important;
 }
diff --git a/third_party/WebKit/Source/devtools/scripts/npm_test.js b/third_party/WebKit/Source/devtools/scripts/npm_test.js
index 701c21ae..4522399 100644
--- a/third_party/WebKit/Source/devtools/scripts/npm_test.js
+++ b/third_party/WebKit/Source/devtools/scripts/npm_test.js
@@ -38,7 +38,7 @@
             console.log("Compiling devtools frontend");
             shell(`ninja -C ${RELEASE_PATH} devtools_frontend_resources`);
         }
-        runTests(outDir, false);
+        runTests(outDir, IS_DEBUG_ENABLED);
         return;
     }
     if (!utils.isDir(CACHE_PATH))
diff --git a/third_party/WebKit/Source/modules/gamepad/NavigatorGamepad.cpp b/third_party/WebKit/Source/modules/gamepad/NavigatorGamepad.cpp
index 8b4411a..1dd81ed 100644
--- a/third_party/WebKit/Source/modules/gamepad/NavigatorGamepad.cpp
+++ b/third_party/WebKit/Source/modules/gamepad/NavigatorGamepad.cpp
@@ -59,7 +59,7 @@
 
   for (unsigned i = 0; i < WebGamepads::itemsLengthCap; ++i) {
     WebGamepad& webGamepad = gamepads.items[i];
-    if (i < gamepads.length && webGamepad.connected) {
+    if (webGamepad.connected) {
       GamepadType* gamepad = into->item(i);
       if (!gamepad)
         gamepad = GamepadType::create();
diff --git a/third_party/WebKit/Source/modules/offscreencanvas2d/OffscreenCanvasRenderingContext2D.cpp b/third_party/WebKit/Source/modules/offscreencanvas2d/OffscreenCanvasRenderingContext2D.cpp
index 969e1d2..96a32d1 100644
--- a/third_party/WebKit/Source/modules/offscreencanvas2d/OffscreenCanvasRenderingContext2D.cpp
+++ b/third_party/WebKit/Source/modules/offscreencanvas2d/OffscreenCanvasRenderingContext2D.cpp
@@ -173,7 +173,7 @@
 }
 
 ImageData* OffscreenCanvasRenderingContext2D::toImageData(
-    SnapshotReason reason) const {
+    SnapshotReason reason) {
   if (!imageBuffer())
     return nullptr;
   sk_sp<SkImage> snapshot =
diff --git a/third_party/WebKit/Source/modules/offscreencanvas2d/OffscreenCanvasRenderingContext2D.h b/third_party/WebKit/Source/modules/offscreencanvas2d/OffscreenCanvasRenderingContext2D.h
index 3baaa55..16c3d09 100644
--- a/third_party/WebKit/Source/modules/offscreencanvas2d/OffscreenCanvasRenderingContext2D.h
+++ b/third_party/WebKit/Source/modules/offscreencanvas2d/OffscreenCanvasRenderingContext2D.h
@@ -51,7 +51,7 @@
     BaseRenderingContext2D::clearRect(x, y, width, height);
   }
   PassRefPtr<Image> getImage(AccelerationHint, SnapshotReason) const final;
-  ImageData* toImageData(SnapshotReason) const override;
+  ImageData* toImageData(SnapshotReason) override;
   void reset() override;
 
   // BaseRenderingContext2D implementation
diff --git a/third_party/WebKit/Source/modules/permissions/DEPS b/third_party/WebKit/Source/modules/permissions/DEPS
new file mode 100644
index 0000000..16764a92
--- /dev/null
+++ b/third_party/WebKit/Source/modules/permissions/DEPS
@@ -0,0 +1,3 @@
+include_rules = [
+  "+mojo/public/cpp/bindings",
+]
diff --git a/third_party/WebKit/Source/modules/permissions/PermissionStatus.cpp b/third_party/WebKit/Source/modules/permissions/PermissionStatus.cpp
index 41e71ae..a3b2b99 100644
--- a/third_party/WebKit/Source/modules/permissions/PermissionStatus.cpp
+++ b/third_party/WebKit/Source/modules/permissions/PermissionStatus.cpp
@@ -38,9 +38,12 @@
                                    MojoPermissionDescriptor descriptor)
     : SuspendableObject(executionContext),
       m_status(status),
-      m_descriptor(std::move(descriptor)) {}
+      m_descriptor(std::move(descriptor)),
+      m_binding(this) {}
 
-PermissionStatus::~PermissionStatus() {
+PermissionStatus::~PermissionStatus() = default;
+
+void PermissionStatus::dispose() {
   stopListening();
 }
 
@@ -52,22 +55,8 @@
   return SuspendableObject::getExecutionContext();
 }
 
-void PermissionStatus::permissionChanged(MojoPermissionStatus status) {
-  if (m_status == status)
-    return;
-
-  m_status = status;
-  dispatchEvent(Event::create(EventTypeNames::change));
-
-  m_service->GetNextPermissionChange(
-      m_descriptor->Clone(), getExecutionContext()->getSecurityOrigin(),
-      m_status,
-      convertToBaseCallback(WTF::bind(&PermissionStatus::permissionChanged,
-                                      wrapWeakPersistent(this))));
-}
-
 bool PermissionStatus::hasPendingActivity() const {
-  return m_service;
+  return m_binding.is_bound();
 }
 
 void PermissionStatus::resume() {
@@ -82,21 +71,6 @@
   stopListening();
 }
 
-void PermissionStatus::startListening() {
-  DCHECK(!m_service);
-  connectToPermissionService(getExecutionContext(),
-                             mojo::MakeRequest(&m_service));
-  m_service->GetNextPermissionChange(
-      m_descriptor->Clone(), getExecutionContext()->getSecurityOrigin(),
-      m_status,
-      convertToBaseCallback(WTF::bind(&PermissionStatus::permissionChanged,
-                                      wrapWeakPersistent(this))));
-}
-
-void PermissionStatus::stopListening() {
-  m_service.reset();
-}
-
 String PermissionStatus::state() const {
   switch (m_status) {
     case MojoPermissionStatus::GRANTED:
@@ -111,6 +85,31 @@
   return "denied";
 }
 
+void PermissionStatus::startListening() {
+  DCHECK(!m_binding.is_bound());
+  mojom::blink::PermissionObserverPtr observer;
+  m_binding.Bind(mojo::MakeRequest(&observer));
+
+  mojom::blink::PermissionServicePtr service;
+  connectToPermissionService(getExecutionContext(),
+                             mojo::MakeRequest(&service));
+  service->AddPermissionObserver(m_descriptor->Clone(),
+                                 getExecutionContext()->getSecurityOrigin(),
+                                 m_status, std::move(observer));
+}
+
+void PermissionStatus::stopListening() {
+  m_binding.Close();
+}
+
+void PermissionStatus::OnPermissionStatusChange(MojoPermissionStatus status) {
+  if (m_status == status)
+    return;
+
+  m_status = status;
+  dispatchEvent(Event::create(EventTypeNames::change));
+}
+
 DEFINE_TRACE(PermissionStatus) {
   EventTargetWithInlineData::trace(visitor);
   SuspendableObject::trace(visitor);
diff --git a/third_party/WebKit/Source/modules/permissions/PermissionStatus.h b/third_party/WebKit/Source/modules/permissions/PermissionStatus.h
index f27c485..efdc4e8 100644
--- a/third_party/WebKit/Source/modules/permissions/PermissionStatus.h
+++ b/third_party/WebKit/Source/modules/permissions/PermissionStatus.h
@@ -8,6 +8,7 @@
 #include "bindings/core/v8/ActiveScriptWrappable.h"
 #include "core/dom/SuspendableObject.h"
 #include "core/events/EventTarget.h"
+#include "mojo/public/cpp/bindings/binding.h"
 #include "platform/heap/Handle.h"
 #include "public/platform/modules/permissions/permission.mojom-blink.h"
 #include "wtf/text/AtomicString.h"
@@ -22,9 +23,11 @@
 // ExecutionContext.
 class PermissionStatus final : public EventTargetWithInlineData,
                                public ActiveScriptWrappable<PermissionStatus>,
-                               public SuspendableObject {
+                               public SuspendableObject,
+                               public mojom::blink::PermissionObserver {
   USING_GARBAGE_COLLECTED_MIXIN(PermissionStatus);
   DEFINE_WRAPPERTYPEINFO();
+  USING_PRE_FINALIZER(PermissionStatus, dispose);
 
   using MojoPermissionDescriptor = mojom::blink::PermissionDescriptorPtr;
   using MojoPermissionStatus = mojom::blink::PermissionStatus;
@@ -38,6 +41,7 @@
                                            MojoPermissionStatus,
                                            MojoPermissionDescriptor);
   ~PermissionStatus() override;
+  void dispose();
 
   // EventTarget implementation.
   const AtomicString& interfaceName() const override;
@@ -52,7 +56,6 @@
   void contextDestroyed() override;
 
   String state() const;
-  void permissionChanged(MojoPermissionStatus);
 
   DEFINE_ATTRIBUTE_EVENT_LISTENER(change);
 
@@ -66,9 +69,11 @@
   void startListening();
   void stopListening();
 
+  void OnPermissionStatusChange(MojoPermissionStatus);
+
   MojoPermissionStatus m_status;
   MojoPermissionDescriptor m_descriptor;
-  mojom::blink::PermissionServicePtr m_service;
+  mojo::Binding<mojom::blink::PermissionObserver> m_binding;
 };
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/modules/webgl/DEPS b/third_party/WebKit/Source/modules/webgl/DEPS
index 7e1735dc..3058c13 100644
--- a/third_party/WebKit/Source/modules/webgl/DEPS
+++ b/third_party/WebKit/Source/modules/webgl/DEPS
@@ -1,4 +1,5 @@
 include_rules = [
     "+gpu/GLES2/gl2extchromium.h",
     "+gpu/command_buffer/client/gles2_interface.h",
+    "+skia/ext",
 ]
diff --git a/third_party/WebKit/Source/modules/webgl/WebGLRenderingContextBase.cpp b/third_party/WebKit/Source/modules/webgl/WebGLRenderingContextBase.cpp
index e9d8b80c..b4a5099 100644
--- a/third_party/WebKit/Source/modules/webgl/WebGLRenderingContextBase.cpp
+++ b/third_party/WebKit/Source/modules/webgl/WebGLRenderingContextBase.cpp
@@ -92,7 +92,9 @@
 #include "platform/graphics/GraphicsContext.h"
 #include "platform/graphics/UnacceleratedImageBufferSurface.h"
 #include "platform/graphics/gpu/AcceleratedImageBufferSurface.h"
+#include "platform/graphics/gpu/SharedGpuContext.h"
 #include "public/platform/Platform.h"
+#include "skia/ext/texture_handle.h"
 #include "wtf/CheckedNumeric.h"
 #include "wtf/Functional.h"
 #include "wtf/PtrUtil.h"
@@ -768,22 +770,42 @@
   return buffer->newImageSnapshot(hint, reason);
 }
 
-ImageData* WebGLRenderingContextBase::toImageData(SnapshotReason reason) const {
-  // TODO: Furnish toImageData in webgl renderingcontext for jpeg and webp
-  // images. See crbug.com/657531.
+ImageData* WebGLRenderingContextBase::toImageData(SnapshotReason reason) {
   ImageData* imageData = nullptr;
   // TODO(ccameron): WebGL should produce sRGB images.
   // https://crbug.com/672299
-  if (this->drawingBuffer()) {
-    sk_sp<SkImage> snapshot =
-        this->drawingBuffer()
-            ->transferToStaticBitmapImage()
-            ->imageForCurrentFrame(ColorBehavior::transformToGlobalTarget());
+  if (drawingBuffer()) {
+    // For un-premultiplied data
+    imageData = paintRenderingResultsToImageData(BackBuffer);
+    if (imageData) {
+      return imageData;
+    }
+
+    int width = drawingBuffer()->size().width();
+    int height = drawingBuffer()->size().height();
+    OpacityMode opacityMode = creationAttributes().alpha() ? NonOpaque : Opaque;
+
+    drawingBuffer()->resolveAndBindForReadAndDraw();
+    gpu::gles2::GLES2Interface* gl = SharedGpuContext::gl();
+    SkImageInfo imageInfo = SkImageInfo::Make(
+        width, height, kRGBA_8888_SkColorType,
+        Opaque == opacityMode ? kOpaque_SkAlphaType : kPremul_SkAlphaType);
+    SkSurfaceProps disableLCDProps(0, kUnknown_SkPixelGeometry);
+    sk_sp<SkSurface> surface = SkSurface::MakeRenderTarget(
+        SharedGpuContext::gr(), SkBudgeted::kYes, imageInfo, 0,
+        Opaque == opacityMode ? nullptr : &disableLCDProps);
+    GLuint textureId = skia::GrBackendObjectToGrGLTextureInfo(
+                           surface->getTextureHandle(
+                               SkSurface::kDiscardWrite_TextureHandleAccess))
+                           ->fID;
+
+    drawingBuffer()->copyToPlatformTexture(
+        gl, textureId, GL_RGBA, GL_UNSIGNED_BYTE, 0, true, false,
+        IntPoint(0, 0), IntRect(IntPoint(0, 0), drawingBuffer()->size()),
+        BackBuffer);
+    sk_sp<SkImage> snapshot = surface->makeImageSnapshot();
     if (snapshot) {
-      imageData = ImageData::create(this->getOffscreenCanvas()->size());
-      SkImageInfo imageInfo = SkImageInfo::Make(
-          this->drawingBufferWidth(), this->drawingBufferHeight(),
-          kRGBA_8888_SkColorType, kUnpremul_SkAlphaType);
+      imageData = ImageData::create(drawingBuffer()->size());
       snapshot->readPixels(imageInfo, imageData->data()->data(),
                            imageInfo.minRowBytes(), 0, 0);
     }
diff --git a/third_party/WebKit/Source/modules/webgl/WebGLRenderingContextBase.h b/third_party/WebKit/Source/modules/webgl/WebGLRenderingContextBase.h
index 18f00f5..88dce3d 100644
--- a/third_party/WebKit/Source/modules/webgl/WebGLRenderingContextBase.h
+++ b/third_party/WebKit/Source/modules/webgl/WebGLRenderingContextBase.h
@@ -574,7 +574,7 @@
   };
 
   PassRefPtr<Image> getImage(AccelerationHint, SnapshotReason) const override;
-  ImageData* toImageData(SnapshotReason) const override;
+  ImageData* toImageData(SnapshotReason) override;
   void setFilterQuality(SkFilterQuality) override;
   bool isWebGL2OrHigher() { return version() >= 2; }
 
diff --git a/third_party/WebKit/Source/modules/webmidi/BUILD.gn b/third_party/WebKit/Source/modules/webmidi/BUILD.gn
index b762380..2774764d 100644
--- a/third_party/WebKit/Source/modules/webmidi/BUILD.gn
+++ b/third_party/WebKit/Source/modules/webmidi/BUILD.gn
@@ -33,6 +33,6 @@
   ]
 
   deps = [
-    "//media/midi:mojo_blink_cpp_sources",
+    "//media/midi:mojo_blink",
   ]
 }
diff --git a/third_party/WebKit/Source/platform/BUILD.gn b/third_party/WebKit/Source/platform/BUILD.gn
index 995b3f9..2e7efe7 100644
--- a/third_party/WebKit/Source/platform/BUILD.gn
+++ b/third_party/WebKit/Source/platform/BUILD.gn
@@ -238,8 +238,8 @@
   visibility = []  # Allow re-assignment of list.
   visibility = [
     "//third_party/WebKit/*",
-    "//url/mojo:url_mojom_origin_blink_cpp_sources",
-    "//url/mojo:url_mojom_gurl_blink_cpp_sources",
+    "//url/mojo:url_mojom_origin_blink",
+    "//url/mojo:url_mojom_gurl_blink",
   ]
   output_name = "blink_platform"
 
diff --git a/third_party/WebKit/Source/platform/graphics/compositing/PaintArtifactCompositor.cpp b/third_party/WebKit/Source/platform/graphics/compositing/PaintArtifactCompositor.cpp
index 0365863..2e230c6 100644
--- a/third_party/WebKit/Source/platform/graphics/compositing/PaintArtifactCompositor.cpp
+++ b/third_party/WebKit/Source/platform/graphics/compositing/PaintArtifactCompositor.cpp
@@ -603,6 +603,38 @@
   void setupRootEffectNode();
   void setupRootScrollNode();
 
+  // A brief discourse on cc property tree nodes, identifiers, and current and
+  // future design evolution envisioned:
+  //
+  // cc property trees identify nodes by their |id|, which implementation-wise
+  // is actually its index in the property tree's vector of its node type. More
+  // recent cc code now refers to these as 'node indices', or 'property tree
+  // indices'. |parent_id| is the same sort of 'node index' of that node's
+  // parent, whereas |owner_id| is the layer id of the layer owning that node.
+  //
+  // Note there are two other primary types of 'ids' referenced in cc property
+  // tree related logic: (1) ElementId, also known Blink-side as
+  // CompositorElementId, used by the animation system to allow tying an element
+  // to its respective layer, and (2) layer ids. There are other ancillary ids
+  // not relevant to any of the above, such as
+  // cc::TransformNode::sorting_context_id
+  // (a.k.a. blink::TransformPaintPropertyNode::renderingContextId()).
+  //
+  // There is a vision to move toward a world where cc property nodes have no
+  // association with layers and instead have a |stable_id|. The id could come
+  // from an ElementId in turn derived from the layout object responsible for
+  // creating the property node.
+  //
+  // We would also like to explore moving to use a single shared property tree
+  // representation across both cc and Blink. See
+  // platform/graphics/paint/README.md for more.
+  //
+  // With the above as background, we can now state more clearly a description
+  // of the below set of compositor id generation methods: they take Blink paint
+  // property tree nodes as input and produce a corresponding cc 'node id',
+  // a.k.a., 'node index', for use as we build out the corresponding cc property
+  // tree representation.
+
   int compositorIdForTransformNode(const TransformPaintPropertyNode*);
   int compositorIdForClipNode(const ClipPaintPropertyNode*);
   int switchToEffectNode(const EffectPaintPropertyNode& nextEffect);
@@ -640,6 +672,8 @@
 
   struct BlinkEffectAndCcIdPair {
     const EffectPaintPropertyNode* effect;
+    // The cc property tree effect node id, or 'node index', for the cc effect
+    // node corresponding to the above Blink effect paint property node.
     int id;
   };
   Vector<BlinkEffectAndCcIdPair> m_effectStack;
@@ -661,7 +695,7 @@
   transformTree.SetTargetId(transformNode.id, kRealRootNodeId);
   transformTree.SetContentTargetId(transformNode.id, kRealRootNodeId);
 
-  // TODO(jaydasika): We shouldn't set ToScreeen and FromScreen of root
+  // TODO(jaydasika): We shouldn't set ToScreen and FromScreen of root
   // transform node here. They should be set while updating transform tree in
   // cc.
   float deviceScaleFactor = m_rootLayer->GetLayerTree()->device_scale_factor();
diff --git a/third_party/WebKit/Source/platform/graphics/paint/README.md b/third_party/WebKit/Source/platform/graphics/paint/README.md
index 1d60a22..8c10efa 100644
--- a/third_party/WebKit/Source/platform/graphics/paint/README.md
+++ b/third_party/WebKit/Source/platform/graphics/paint/README.md
@@ -259,6 +259,10 @@
 The owner of the `PaintArtifactCompositor` (e.g. `WebView`) can then attach its
 root layer to the overall layer hierarchy to be displayed to the user.
 
+In the future we would like to explore moving to a single shared property tree
+representation across both cc and
+Blink. See [Web Page Geometries](https://goo.gl/MwVIto) for more.
+
 ## Geometry routines
 
 The [`GeometryMapper`](GeometryMapper.h) is responsible for efficiently computing
diff --git a/third_party/WebKit/Source/wtf/text/WTFString.cpp b/third_party/WebKit/Source/wtf/text/WTFString.cpp
index 5e4f19c..6fdc7d8f 100644
--- a/third_party/WebKit/Source/wtf/text/WTFString.cpp
+++ b/third_party/WebKit/Source/wtf/text/WTFString.cpp
@@ -496,7 +496,7 @@
   return !m_impl || m_impl->isSafeToSendToAnotherThread();
 }
 
-void String::split(const String& separator,
+void String::split(const StringView& separator,
                    bool allowEmptyEntries,
                    Vector<String>& result) const {
   result.clear();
diff --git a/third_party/WebKit/Source/wtf/text/WTFString.h b/third_party/WebKit/Source/wtf/text/WTFString.h
index d0382a01..b04d49f4 100644
--- a/third_party/WebKit/Source/wtf/text/WTFString.h
+++ b/third_party/WebKit/Source/wtf/text/WTFString.h
@@ -315,10 +315,10 @@
     return StringImpl::createUninitialized(length, data);
   }
 
-  void split(const String& separator,
+  void split(const StringView& separator,
              bool allowEmptyEntries,
              Vector<String>& result) const;
-  void split(const String& separator, Vector<String>& result) const {
+  void split(const StringView& separator, Vector<String>& result) const {
     split(separator, false, result);
   }
   void split(UChar separator,
diff --git a/third_party/WebKit/Tools/Scripts/webkitpy/w3c/deps_updater.py b/third_party/WebKit/Tools/Scripts/webkitpy/w3c/deps_updater.py
index 5d0a82d..56ab91e 100644
--- a/third_party/WebKit/Tools/Scripts/webkitpy/w3c/deps_updater.py
+++ b/third_party/WebKit/Tools/Scripts/webkitpy/w3c/deps_updater.py
@@ -149,17 +149,21 @@
             self.run(['git', 'add', destination])
 
     def _generate_manifest(self, original_repo_path, dest_path):
-        """Generate MANIFEST.json for imported tests.
+        """Generates MANIFEST.json for imported tests.
 
-        Run 'manifest' command if it exists in original_repo_path, and
-        add generated MANIFEST.json to dest_path.
+        Args:
+            original_repo_path: Path to the temporary source WPT repo directory.
+            dest_path: Path to the destination WPT directory.
+
+        Runs the (newly-updated) manifest command if it's found, and then
+        stages the generated MANIFEST.json in the git index, ready to commit.
         """
         manifest_command = self.fs.join(original_repo_path, 'manifest')
         if not self.fs.exists(manifest_command):
             # Do nothing for csswg-test.
             return
         _log.info('Generating MANIFEST.json')
-        self.run([manifest_command, '--tests-root', dest_path])
+        self.run([manifest_command, '--work', '--tests-root', dest_path])
         self.run(['git', 'add', self.fs.join(dest_path, 'MANIFEST.json')])
 
     def update(self, dest_dir_name, url, keep_w3c_repos_around, revision):
diff --git a/third_party/WebKit/Tools/Scripts/webkitpy/w3c/deps_updater_unittest.py b/third_party/WebKit/Tools/Scripts/webkitpy/w3c/deps_updater_unittest.py
index 806ca41..46427f5 100644
--- a/third_party/WebKit/Tools/Scripts/webkitpy/w3c/deps_updater_unittest.py
+++ b/third_party/WebKit/Tools/Scripts/webkitpy/w3c/deps_updater_unittest.py
@@ -89,3 +89,40 @@
              'TBR=qyearsley@chromium.org\n'
              'NOEXPORT=true'))
         self.assertEqual(host.executive.calls, [['git', 'log', '-1', '--format=%B']])
+
+    def test_generate_manifest_command_not_found(self):
+        # If we're updating csswg-test, then the manifest file won't be found.
+        host = MockHost()
+        host.filesystem.files = {}
+        updater = DepsUpdater(host)
+        updater._generate_manifest(
+            '/mock-checkout/third_party/WebKit/css',
+            '/mock-checkout/third_party/WebKit/LayoutTests/imported/csswg-test')
+        self.assertEqual(host.executive.calls, [])
+
+    def test_generate_manifest_successful_run(self):
+        # This test doesn't test any aspect of the real manifest script, it just
+        # asserts that DepsUpdater._generate_manifest would invoke the script.
+        host = MockHost()
+        host.filesystem.files = {
+            '/mock-checkout/third_party/WebKit/wpt/manifest': 'dummy content'
+        }
+        updater = DepsUpdater(host)
+        updater._generate_manifest(
+            '/mock-checkout/third_party/WebKit/wpt',
+            '/mock-checkout/third_party/WebKit/LayoutTests/imported/wpt')
+        self.assertEqual(
+            host.executive.calls,
+            [
+                [
+                    '/mock-checkout/third_party/WebKit/wpt/manifest',
+                    '--work',
+                    '--tests-root',
+                    '/mock-checkout/third_party/WebKit/LayoutTests/imported/wpt'
+                ],
+                [
+                    'git',
+                    'add',
+                    '/mock-checkout/third_party/WebKit/LayoutTests/imported/wpt/MANIFEST.json'
+                ]
+            ])
diff --git a/third_party/WebKit/public/platform/Platform.h b/third_party/WebKit/public/platform/Platform.h
index fce5664..a4b58743 100644
--- a/third_party/WebKit/public/platform/Platform.h
+++ b/third_party/WebKit/public/platform/Platform.h
@@ -272,7 +272,7 @@
 
   // Gamepad -------------------------------------------------------------
 
-  virtual void sampleGamepads(WebGamepads& into) { into.length = 0; }
+  virtual void sampleGamepads(WebGamepads& into) {}
 
   // History -------------------------------------------------------------
 
diff --git a/third_party/WebKit/public/platform/WebGamepad.h b/third_party/WebKit/public/platform/WebGamepad.h
index 34ca828..0e14669 100644
--- a/third_party/WebKit/public/platform/WebGamepad.h
+++ b/third_party/WebKit/public/platform/WebGamepad.h
@@ -90,7 +90,11 @@
   static const size_t buttonsLengthCap = 32;
 
   WebGamepad()
-      : connected(false), timestamp(0), axesLength(0), buttonsLength(0) {
+      : connected(false),
+        timestamp(0),
+        axesLength(0),
+        buttonsLength(0),
+        displayId(0) {
     id[0] = 0;
     mapping[0] = 0;
   }
diff --git a/third_party/WebKit/public/platform/WebGamepads.h b/third_party/WebKit/public/platform/WebGamepads.h
index 15ede96..4b76542 100644
--- a/third_party/WebKit/public/platform/WebGamepads.h
+++ b/third_party/WebKit/public/platform/WebGamepads.h
@@ -35,13 +35,8 @@
 // browser.
 class WebGamepads {
  public:
-  WebGamepads() : length(0) {}
-
   static const size_t itemsLengthCap = 4;
 
-  // Number of valid entries in the items array.
-  unsigned length;
-
   // Gamepad data for N separate gamepad devices.
   WebGamepad items[itemsLengthCap];
 };
diff --git a/third_party/WebKit/public/platform/modules/permissions/permission.mojom b/third_party/WebKit/public/platform/modules/permissions/permission.mojom
index ef42937..97387f98 100644
--- a/third_party/WebKit/public/platform/modules/permissions/permission.mojom
+++ b/third_party/WebKit/public/platform/modules/permissions/permission.mojom
@@ -35,6 +35,11 @@
   PermissionDescriptorExtension? extension;
 };
 
+// Interface a client can implement to observe permission changes.
+interface PermissionObserver {
+  OnPermissionStatusChange(PermissionStatus status);
+};
+
 // The Permission service provides permission handling capabilities by exposing
 // methods to check, request, and revoke permissions. It also allows a client to
 // start listening to permission changes.
@@ -49,13 +54,10 @@
     => (array<PermissionStatus> statuses);
   RevokePermission(PermissionDescriptor permission, url.mojom.Origin origin)
     => (PermissionStatus status);
-
-  // Runs the callback next time there is a permission status change for the
-  // given { permission, origin }. Callers of this method will have to call it
-  // again if they want to keep listening to the changes. To prevent race
-  // conditions, the caller must pass the last known value.
-  GetNextPermissionChange(PermissionDescriptor permission,
-                          url.mojom.Origin origin,
-                          PermissionStatus last_known_status)
-    => (PermissionStatus status);
+  // Subscribes |observer| to updates about changes to |origin|'s access to
+  // |permission|. Closing the pipe will cancel the subscription.
+  AddPermissionObserver(PermissionDescriptor permission,
+                        url.mojom.Origin origin,
+                        PermissionStatus last_known_status,
+                        PermissionObserver observer);
 };
diff --git a/tools/chrome_proxy/webdriver/incognito.py b/tools/chrome_proxy/webdriver/incognito.py
new file mode 100644
index 0000000..1f36d3a
--- /dev/null
+++ b/tools/chrome_proxy/webdriver/incognito.py
@@ -0,0 +1,22 @@
+# Copyright 2017 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import common
+from common import TestDriver
+from common import IntegrationTest
+
+
+class Incognito(IntegrationTest):
+
+  # Ensure Chrome does not use DataSaver in Incognito mode.
+  def testCheckPageWithIncognito(self):
+    with TestDriver() as t:
+      t.AddChromeArg('--enable-spdy-proxy-auth')
+      t.AddChromeArg('--incognito')
+      t.LoadURL('http://check.googlezip.net/test.html')
+      for response in t.GetHTTPResponses():
+        self.assertNotHasChromeProxyViaHeader(response)
+
+if __name__ == '__main__':
+  IntegrationTest.RunAllTests()
diff --git a/tools/clang/rewrite_to_chrome_style/RewriteToChromeStyle.cpp b/tools/clang/rewrite_to_chrome_style/RewriteToChromeStyle.cpp
index e38c136a..c3d3356 100644
--- a/tools/clang/rewrite_to_chrome_style/RewriteToChromeStyle.cpp
+++ b/tools/clang/rewrite_to_chrome_style/RewriteToChromeStyle.cpp
@@ -328,6 +328,11 @@
   if (type.isVolatileQualified())
     return false;
 
+  // Parameters should not be renamed to |kFooBar| style (even if they are
+  // const and have an initializer (aka default value)).
+  if (clang::isa<clang::ParmVarDecl>(&decl))
+    return false;
+
   // http://google.github.io/styleguide/cppguide.html#Constant_Names
   // Static variables that are const-qualified should use kConstantStyle naming.
   if (decl.getStorageDuration() == clang::SD_Static)
diff --git a/tools/clang/rewrite_to_chrome_style/tests/constants-expected.cc b/tools/clang/rewrite_to_chrome_style/tests/constants-expected.cc
index e27982a..3f55421 100644
--- a/tools/clang/rewrite_to_chrome_style/tests/constants-expected.cc
+++ b/tools/clang/rewrite_to_chrome_style/tests/constants-expected.cc
@@ -42,7 +42,8 @@
   }
 };
 
-void F() {
+// |constParam| should not be renamed to |kConstParam|.
+void F(const bool const_param = true) {
   // Constant in function body.
   static const char kStaticString[] = "abc";
   // Constant-style naming, since it's initialized with a literal.
diff --git a/tools/clang/rewrite_to_chrome_style/tests/constants-original.cc b/tools/clang/rewrite_to_chrome_style/tests/constants-original.cc
index bc51bffa..51ce017 100644
--- a/tools/clang/rewrite_to_chrome_style/tests/constants-original.cc
+++ b/tools/clang/rewrite_to_chrome_style/tests/constants-original.cc
@@ -42,7 +42,8 @@
   }
 };
 
-void F() {
+// |constParam| should not be renamed to |kConstParam|.
+void F(const bool constParam = true) {
   // Constant in function body.
   static const char staticString[] = "abc";
   // Constant-style naming, since it's initialized with a literal.
diff --git a/tools/gn/xcode_writer.cc b/tools/gn/xcode_writer.cc
index b35cb44..5030b5ab 100644
--- a/tools/gn/xcode_writer.cc
+++ b/tools/gn/xcode_writer.cc
@@ -31,6 +31,12 @@
 
 namespace {
 
+using TargetToFileList = std::unordered_map<const Target*, Target::FileList>;
+
+const char kEarlGreyFileNameIdentifier[] = "egtest.mm";
+const char kXCTestFileNameIdentifier[] = "xctest.mm";
+const char kXCTestModuleTargetNamePostfix[] = "_module";
+
 struct SafeEnvironmentVariableInfo {
   const char* name;
   bool capture_at_generation;
@@ -86,6 +92,107 @@
   return script.str();
 }
 
+bool IsApplicationTarget(const Target* target) {
+  return target->output_type() == Target::CREATE_BUNDLE &&
+         target->bundle_data().product_type() ==
+             "com.apple.product-type.application";
+}
+
+bool IsXCTestModuleTarget(const Target* target) {
+  return target->output_type() == Target::CREATE_BUNDLE &&
+         target->bundle_data().product_type() ==
+             "com.apple.product-type.bundle.unit-test" &&
+         base::EndsWith(target->label().name(), kXCTestModuleTargetNamePostfix,
+                        base::CompareCase::SENSITIVE);
+}
+
+const Target* FindXCTestApplicationTarget(
+    const Target* xctest_module_target,
+    const std::vector<const Target*>& targets) {
+  DCHECK(IsXCTestModuleTarget(xctest_module_target));
+  DCHECK(base::EndsWith(xctest_module_target->label().name(),
+                        kXCTestModuleTargetNamePostfix,
+                        base::CompareCase::SENSITIVE));
+  std::string application_target_name =
+      xctest_module_target->label().name().substr(
+          0, xctest_module_target->label().name().size() -
+                 strlen(kXCTestModuleTargetNamePostfix));
+  for (const Target* target : targets) {
+    if (target->label().name() == application_target_name) {
+      return target;
+    }
+  }
+  NOTREACHED();
+  return nullptr;
+}
+
+// Returns the corresponding application targets given XCTest module targets.
+void FindXCTestApplicationTargets(
+    const std::vector<const Target*>& xctest_module_targets,
+    const std::vector<const Target*>& targets,
+    std::vector<const Target*>* xctest_application_targets) {
+  for (const Target* xctest_module_target : xctest_module_targets) {
+    xctest_application_targets->push_back(
+        FindXCTestApplicationTarget(xctest_module_target, targets));
+  }
+}
+
+// Searches the list of xctest files recursively under |target|.
+void SearchXCTestFiles(const Target* target,
+                       TargetToFileList* xctest_files_per_target) {
+  // Early return if already visited and processed.
+  if (xctest_files_per_target->find(target) != xctest_files_per_target->end())
+    return;
+
+  Target::FileList xctest_files;
+  for (const SourceFile& file : target->sources()) {
+    if (base::EndsWith(file.GetName(), kEarlGreyFileNameIdentifier,
+                       base::CompareCase::SENSITIVE) ||
+        base::EndsWith(file.GetName(), kXCTestFileNameIdentifier,
+                       base::CompareCase::SENSITIVE)) {
+      xctest_files.push_back(file);
+    }
+  }
+
+  // Call recursively on public and private deps.
+  for (const auto& target : target->public_deps()) {
+    SearchXCTestFiles(target.ptr, xctest_files_per_target);
+    const Target::FileList& deps_xctest_files =
+        (*xctest_files_per_target)[target.ptr];
+    xctest_files.insert(xctest_files.end(), deps_xctest_files.begin(),
+                        deps_xctest_files.end());
+  }
+
+  for (const auto& target : target->private_deps()) {
+    SearchXCTestFiles(target.ptr, xctest_files_per_target);
+    const Target::FileList& deps_xctest_files =
+        (*xctest_files_per_target)[target.ptr];
+    xctest_files.insert(xctest_files.end(), deps_xctest_files.begin(),
+                        deps_xctest_files.end());
+  }
+
+  // Sort xctest_files to remove duplicates.
+  std::sort(xctest_files.begin(), xctest_files.end());
+  xctest_files.erase(std::unique(xctest_files.begin(), xctest_files.end()),
+                     xctest_files.end());
+
+  xctest_files_per_target->insert(std::make_pair(target, xctest_files));
+}
+
+// Finds the list of xctest files recursively under each of the application
+// targets.
+void FindXCTestFilesForTargets(
+    const std::vector<const Target*>& application_targets,
+    std::vector<Target::FileList>* file_lists) {
+  TargetToFileList xctest_files_per_target;
+
+  for (const Target* target : application_targets) {
+    DCHECK(IsApplicationTarget(target));
+    SearchXCTestFiles(target, &xctest_files_per_target);
+    file_lists->push_back(xctest_files_per_target[target]);
+  }
+}
+
 class CollectPBXObjectsPerClassHelper : public PBXObjectVisitor {
  public:
   CollectPBXObjectsPerClassHelper() {}
@@ -266,6 +373,18 @@
   return true;
 }
 
+// static
+void XcodeWriter::FilterXCTestModuleTargets(
+    const std::vector<const Target*>& targets,
+    std::vector<const Target*>* xctest_module_targets) {
+  for (const Target* target : targets) {
+    if (!IsXCTestModuleTarget(target))
+      continue;
+
+    xctest_module_targets->push_back(target);
+  }
+}
+
 void XcodeWriter::CreateProductsProject(
     const std::vector<const Target*>& targets,
     const PBXAttributes& attributes,
@@ -278,6 +397,20 @@
   std::unique_ptr<PBXProject> main_project(
       new PBXProject("products", config_name, source_path, attributes));
 
+  // Filter xctest module and application targets and find list of xctest files
+  // recursively under them.
+  std::vector<const Target*> xctest_module_targets;
+  FilterXCTestModuleTargets(targets, &xctest_module_targets);
+
+  std::vector<const Target*> xctest_application_targets;
+  FindXCTestApplicationTargets(xctest_module_targets, targets,
+                               &xctest_application_targets);
+  DCHECK_EQ(xctest_module_targets.size(), xctest_application_targets.size());
+
+  std::vector<Target::FileList> xctest_file_lists;
+  FindXCTestFilesForTargets(xctest_application_targets, &xctest_file_lists);
+  DCHECK_EQ(xctest_application_targets.size(), xctest_file_lists.size());
+
   std::string build_path;
   std::unique_ptr<base::Environment> env(base::Environment::Create());
 
@@ -299,10 +432,21 @@
                            env.get()));
         break;
 
-      case Target::CREATE_BUNDLE:
+      case Target::CREATE_BUNDLE: {
         if (target->bundle_data().product_type().empty())
           continue;
 
+        // Test files need to be known to Xcode for proper indexing and for
+        // discovery of tests function for XCTest, but the compilation is done
+        // via ninja and thus must prevent Xcode from linking object files via
+        // this hack.
+        PBXAttributes extra_attributes;
+        if (IsXCTestModuleTarget(target)) {
+          extra_attributes["OTHER_LDFLAGS"] = "-help";
+          extra_attributes["ONLY_ACTIVE_ARCH"] = "YES";
+          extra_attributes["DEBUG_INFORMATION_FORMAT"] = "dwarf";
+        }
+
         main_project->AddNativeTarget(
             target->label().name(), std::string(),
             RebasePath(target->bundle_data()
@@ -310,9 +454,10 @@
                            .value(),
                        build_settings->build_dir()),
             target->bundle_data().product_type(),
-            GetBuildScript(target->label().name(), ninja_extra_args,
-                           env.get()));
+            GetBuildScript(target->label().name(), ninja_extra_args, env.get()),
+            extra_attributes);
         break;
+      }
 
       default:
         break;
diff --git a/tools/gn/xcode_writer.h b/tools/gn/xcode_writer.h
index 7373cff0..37f49d6 100644
--- a/tools/gn/xcode_writer.h
+++ b/tools/gn/xcode_writer.h
@@ -60,6 +60,11 @@
                             std::vector<const Target*>* targets,
                             Err* err);
 
+  // Filters list of targets to only return ones that are xctest module bundles.
+  static void FilterXCTestModuleTargets(
+      const std::vector<const Target*>& targets,
+      std::vector<const Target*>* xctest_module_targets);
+
   // Generate the "products.xcodeproj" project that reference all products
   // (i.e. targets that have a build artefact usable from Xcode, mostly
   // application bundles).
diff --git a/tools/metrics/actions/actions.xml b/tools/metrics/actions/actions.xml
index 72d028f..43c254c 100644
--- a/tools/metrics/actions/actions.xml
+++ b/tools/metrics/actions/actions.xml
@@ -1147,6 +1147,132 @@
   </description>
 </action>
 
+<action name="Android.HistoryPage.ClearBrowsingData">
+  <owner>twellington@chromium.org</owner>
+  <description>
+    User clicked the clear browsing data button on the native Android history
+    page.
+  </description>
+</action>
+
+<action name="Android.HistoryPage.LoadMoreOnScroll">
+  <owner>twellington@chromium.org</owner>
+  <description>
+    User scrolled to the bottom of the screen causing more items to load on the
+    native Android history page.
+  </description>
+</action>
+
+<action name="Android.HistoryPage.OpenItem">
+  <owner>twellington@chromium.org</owner>
+  <description>
+    User opened an item on the native Android history page.
+  </description>
+</action>
+
+<action name="Android.HistoryPage.OpenSelected">
+  <owner>twellington@chromium.org</owner>
+  <description>
+    User opened one or more selected items in new tabs on the native Android
+    history page.
+  </description>
+</action>
+
+<action name="Android.HistoryPage.OpenSelectedIncognito">
+  <owner>twellington@chromium.org</owner>
+  <description>
+    User opened one or more selected items in new incognito tabs on the native
+    Android history page.
+  </description>
+</action>
+
+<action name="Android.HistoryPage.RemoveItem">
+  <owner>twellington@chromium.org</owner>
+  <description>
+    User removed an item on the native Android history page.
+  </description>
+</action>
+
+<action name="Android.HistoryPage.RemoveSelected">
+  <owner>twellington@chromium.org</owner>
+  <description>
+    User removed one or more selected items on the native Android history page.
+  </description>
+</action>
+
+<action name="Android.HistoryPage.Search">
+  <owner>twellington@chromium.org</owner>
+  <description>
+    User tapped the search icon on the native Android history page.
+  </description>
+</action>
+
+<action name="Android.HistoryPage.Search.LoadMoreOnScroll">
+  <owner>twellington@chromium.org</owner>
+  <description>
+    User scrolled to the bottom of the screen causing more items to load while
+    searching on the native Android history page.
+  </description>
+</action>
+
+<action name="Android.HistoryPage.Search.OpenItem">
+  <owner>twellington@chromium.org</owner>
+  <description>
+    User opened an item while searching on the native Android history page.
+  </description>
+</action>
+
+<action name="Android.HistoryPage.Search.OpenSelected">
+  <owner>twellington@chromium.org</owner>
+  <description>
+    User opened one or more selected items in new tabs while searching on the
+    native Android history page.
+  </description>
+</action>
+
+<action name="Android.HistoryPage.Search.OpenSelectedIncognito">
+  <owner>twellington@chromium.org</owner>
+  <description>
+    User opened one or more selected items in new incognito tabs while searching
+    on the native Android history page.
+  </description>
+</action>
+
+<action name="Android.HistoryPage.Search.RemoveItem">
+  <owner>twellington@chromium.org</owner>
+  <description>
+    User removed an item while searching on the native Android history page.
+  </description>
+</action>
+
+<action name="Android.HistoryPage.Search.RemoveSelected">
+  <owner>twellington@chromium.org</owner>
+  <description>
+    User removed one or more selected items while searching on the native
+    Android history page.
+  </description>
+</action>
+
+<action name="Android.HistoryPage.Search.SelectionEstablished">
+  <owner>twellington@chromium.org</owner>
+  <description>
+    User established a selection while searching on the native Android history
+    page.
+  </description>
+</action>
+
+<action name="Android.HistoryPage.SelectionEstablished">
+  <owner>twellington@chromium.org</owner>
+  <description>
+    User established a selection on the native Android history page.
+  </description>
+</action>
+
+<action name="Android.HistoryPage.Show">
+  <owner>twellington@chromium.org</owner>
+  <description>User navigated to the native Android history page.</description>
+</action>
+
 <action name="Android.InstantApps.BannerDismissed">
   <owner>mariakhomenko@chromium.org</owner>
   <description>Promo banner for an instant app has been dismissed.</description>
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index d384a53..48eff29 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -547,6 +547,22 @@
   <summary>The percentage of total storage downloads consume.</summary>
 </histogram>
 
+<histogram name="Android.HistoryPage.OpenSelected">
+  <owner>twellington@chromium.org</owner>
+  <summary>
+    The number of selected items the user opened in new tabs from the native
+    Android history page.
+  </summary>
+</histogram>
+
+<histogram name="Android.HistoryPage.RemoveSelected">
+  <owner>twellington@chromium.org</owner>
+  <summary>
+    The number of selected items the user removed from the native Android
+    history page.
+  </summary>
+</histogram>
+
 <histogram name="Android.InstantApps.ApiCallDuration2" units="ms">
   <owner>mariakhomenko@chromium.org</owner>
   <summary>
diff --git a/ui/app_list/presenter/BUILD.gn b/ui/app_list/presenter/BUILD.gn
index 2f7b0e7..50d0ea9 100644
--- a/ui/app_list/presenter/BUILD.gn
+++ b/ui/app_list/presenter/BUILD.gn
@@ -25,7 +25,6 @@
   sources = [
     "app_list.cc",
     "app_list.h",
-    "app_list_presenter.h",
     "app_list_presenter_delegate.cc",
     "app_list_presenter_delegate.h",
     "app_list_presenter_delegate_factory.h",
@@ -58,6 +57,10 @@
   sources = [
     "test/app_list_presenter_impl_test_api.cc",
     "test/app_list_presenter_impl_test_api.h",
+    "test/test_app_list_presenter.cc",
+    "test/test_app_list_presenter.h",
+    "test/test_app_list_view_delegate_factory.cc",
+    "test/test_app_list_view_delegate_factory.h",
 
     # Temporary dependency to fix compile flake in http://crbug.com/611898.
     # TODO(tapted): Remove once http://crbug.com/612382 is fixed.
@@ -65,10 +68,13 @@
   ]
 
   public_deps = [
+    ":mojom",
     ":presenter",
   ]
   deps = [
     "//base",
+    "//mojo/public/cpp/bindings",
+    "//ui/app_list:test_support",
   ]
 }
 
diff --git a/ui/app_list/presenter/app_list.cc b/ui/app_list/presenter/app_list.cc
index 9dda27a8..76b36d8c 100644
--- a/ui/app_list/presenter/app_list.cc
+++ b/ui/app_list/presenter/app_list.cc
@@ -18,8 +18,39 @@
   return presenter_.get();
 }
 
+void AppList::Show(int64_t display_id) {
+  if (presenter_)
+    presenter_->Show(display_id);
+}
+
+void AppList::Dismiss() {
+  if (presenter_)
+    presenter_->Dismiss();
+}
+
+void AppList::ToggleAppList(int64_t display_id) {
+  if (presenter_)
+    presenter_->ToggleAppList(display_id);
+}
+
+bool AppList::IsVisible() const {
+  return visible_;
+}
+
+bool AppList::GetTargetVisibility() const {
+  return target_visible_;
+}
+
 void AppList::SetAppListPresenter(mojom::AppListPresenterPtr presenter) {
   presenter_ = std::move(presenter);
 }
 
+void AppList::OnTargetVisibilityChanged(bool visible) {
+  target_visible_ = visible;
+}
+
+void AppList::OnVisibilityChanged(bool visible) {
+  visible_ = visible;
+}
+
 }  // namespace app_list
diff --git a/ui/app_list/presenter/app_list.h b/ui/app_list/presenter/app_list.h
index 2cf701d..fb4b8a2 100644
--- a/ui/app_list/presenter/app_list.h
+++ b/ui/app_list/presenter/app_list.h
@@ -23,8 +23,19 @@
   // Get a raw pointer to the mojom::AppListPresenter interface; may be null.
   mojom::AppListPresenter* GetAppListPresenter();
 
+  // Helper functions to call the underlying functionality on the presenter.
+  void Show(int64_t display_id);
+  void Dismiss();
+  void ToggleAppList(int64_t display_id);
+
+  // Helper functions to get the cached state as reported by the presenter.
+  bool IsVisible() const;
+  bool GetTargetVisibility() const;
+
   // mojom::AppList:
   void SetAppListPresenter(mojom::AppListPresenterPtr presenter) override;
+  void OnTargetVisibilityChanged(bool visible) override;
+  void OnVisibilityChanged(bool visible) override;
 
  private:
   // Bindings for the mojom::AppList interface.
@@ -33,6 +44,10 @@
   // App list presenter interface in chrome; used to show/hide the app list.
   mojom::AppListPresenterPtr presenter_;
 
+  // The cached [target] visibility, as reported by the presenter.
+  bool target_visible_ = false;
+  bool visible_ = false;
+
   DISALLOW_COPY_AND_ASSIGN(AppList);
 };
 
diff --git a/ui/app_list/presenter/app_list_presenter.h b/ui/app_list/presenter/app_list_presenter.h
deleted file mode 100644
index 320564b..0000000
--- a/ui/app_list/presenter/app_list_presenter.h
+++ /dev/null
@@ -1,41 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef UI_APP_LIST_PRESENTER_APP_LIST_PRESENTER_H_
-#define UI_APP_LIST_PRESENTER_APP_LIST_PRESENTER_H_
-
-#include <stdint.h>
-
-#include "ui/app_list/presenter/app_list_presenter_export.h"
-
-namespace app_list {
-
-// Interface for showing and hiding the app list.
-class APP_LIST_PRESENTER_EXPORT AppListPresenter {
- public:
-  virtual ~AppListPresenter() {}
-
-  // Show/hide app list window. The |window| is used to deterime in
-  // which display (in which the |window| exists) the app list should
-  // be shown.
-  virtual void Show(int64_t display_id) = 0;
-
-  // Invoked to dismiss app list. This may leave the view open but hidden from
-  // the user.
-  virtual void Dismiss() = 0;
-
-  // Show the app list if it is visible, hide it if it is hidden.
-  virtual void ToggleAppList(int64_t display_id) = 0;
-
-  // Returns current visibility of the app list.
-  virtual bool IsVisible() const = 0;
-
-  // Returns target visibility. This may differ from IsVisible() if a
-  // visibility transition is in progress.
-  virtual bool GetTargetVisibility() const = 0;
-};
-
-}  // namespace app_list
-
-#endif  // UI_APP_LIST_PRESENTER_APP_LIST_PRESENTER_H_
diff --git a/ui/app_list/presenter/app_list_presenter.mojom b/ui/app_list/presenter/app_list_presenter.mojom
index 8522ca5..14d376c 100644
--- a/ui/app_list/presenter/app_list_presenter.mojom
+++ b/ui/app_list/presenter/app_list_presenter.mojom
@@ -14,6 +14,10 @@
 interface AppList {
   // Set the app list presenter interface, to let ash trigger Chrome's app list.
   SetAppListPresenter(AppListPresenter presenter);
+
+  // Notify the app list that the presenter's [target] visibility changed.
+  OnTargetVisibilityChanged(bool visible);
+  OnVisibilityChanged(bool visible);
 };
 
 // Implemented by chrome. Used by ash to actually show and dismiss the app list.
@@ -23,7 +27,7 @@
 
   // Dismiss the app list.
   Dismiss();
-  
+
   // Show the app list (on the specified display) if it is hidden; hide the
   // app list if it is shown.
   ToggleAppList(int64 display_id);
diff --git a/ui/app_list/presenter/app_list_presenter_delegate.h b/ui/app_list/presenter/app_list_presenter_delegate.h
index d75b7567..5535921e 100644
--- a/ui/app_list/presenter/app_list_presenter_delegate.h
+++ b/ui/app_list/presenter/app_list_presenter_delegate.h
@@ -22,8 +22,7 @@
 class AppListView;
 class AppListViewDelegate;
 
-// Delegate of AppListPresenter which allows to customize AppListPresenter's
-// behavior.
+// Delegate of the app list presenter which allows customizing its behavior.
 // The design of this interface was heavily influenced by the needs of Ash's
 // app list implementation (see ash::AppListPresenterDelegate).
 class APP_LIST_PRESENTER_EXPORT AppListPresenterDelegate {
diff --git a/ui/app_list/presenter/app_list_presenter_delegate_factory.h b/ui/app_list/presenter/app_list_presenter_delegate_factory.h
index 6e2e8fc..1a7875e 100644
--- a/ui/app_list/presenter/app_list_presenter_delegate_factory.h
+++ b/ui/app_list/presenter/app_list_presenter_delegate_factory.h
@@ -11,7 +11,7 @@
 
 namespace app_list {
 
-class AppListPresenter;
+class AppListPresenterImpl;
 class AppListPresenterDelegate;
 
 class APP_LIST_PRESENTER_EXPORT AppListPresenterDelegateFactory {
@@ -19,7 +19,7 @@
   virtual ~AppListPresenterDelegateFactory() {}
 
   virtual std::unique_ptr<AppListPresenterDelegate> GetDelegate(
-      AppListPresenter* presenter) = 0;
+      AppListPresenterImpl* presenter) = 0;
 };
 
 }  // namespace app_list
diff --git a/ui/app_list/presenter/app_list_presenter_impl.cc b/ui/app_list/presenter/app_list_presenter_impl.cc
index e6db16a..852a16b 100644
--- a/ui/app_list/presenter/app_list_presenter_impl.cc
+++ b/ui/app_list/presenter/app_list_presenter_impl.cc
@@ -31,9 +31,9 @@
 }  // namespace
 
 AppListPresenterImpl::AppListPresenterImpl(
-    AppListPresenterDelegateFactory* factory)
-    : factory_(factory) {
-  DCHECK(factory);
+    std::unique_ptr<AppListPresenterDelegateFactory> factory)
+    : factory_(std::move(factory)) {
+  DCHECK(factory_);
 }
 
 AppListPresenterImpl::~AppListPresenterImpl() {
@@ -57,6 +57,9 @@
     return;
 
   is_visible_ = true;
+  if (app_list_)
+    app_list_->OnTargetVisibilityChanged(GetTargetVisibility());
+
   if (view_) {
     ScheduleAnimation();
   } else {
@@ -80,6 +83,8 @@
   DCHECK(view_);
 
   is_visible_ = false;
+  if (app_list_)
+    app_list_->OnTargetVisibilityChanged(GetTargetVisibility());
 
   // The dismissal may have occurred in response to the app list losing
   // activation. Otherwise, our widget is currently active. When the animation
@@ -110,6 +115,14 @@
   return is_visible_;
 }
 
+void AppListPresenterImpl::SetAppList(mojom::AppListPtr app_list) {
+  DCHECK(app_list);
+  app_list_ = std::move(app_list);
+  // Notify the app list interface of the current [target] visibility.
+  app_list_->OnTargetVisibilityChanged(GetTargetVisibility());
+  app_list_->OnVisibilityChanged(IsVisible());
+}
+
 ////////////////////////////////////////////////////////////////////////////////
 // AppListPresenterImpl, private:
 
@@ -211,12 +224,19 @@
 // AppListPresenterImpl, views::WidgetObserver implementation:
 
 void AppListPresenterImpl::OnWidgetDestroying(views::Widget* widget) {
-  DCHECK(view_->GetWidget() == widget);
+  DCHECK_EQ(view_->GetWidget(), widget);
   if (is_visible_)
     Dismiss();
   ResetView();
 }
 
+void AppListPresenterImpl::OnWidgetVisibilityChanged(views::Widget* widget,
+                                                     bool visible) {
+  DCHECK_EQ(view_->GetWidget(), widget);
+  if (app_list_)
+    app_list_->OnVisibilityChanged(visible);
+}
+
 ////////////////////////////////////////////////////////////////////////////////
 // AppListPresenterImpl, PaginationModelObserver implementation:
 
diff --git a/ui/app_list/presenter/app_list_presenter_impl.h b/ui/app_list/presenter/app_list_presenter_impl.h
index 537d1c0..616d647 100644
--- a/ui/app_list/presenter/app_list_presenter_impl.h
+++ b/ui/app_list/presenter/app_list_presenter_impl.h
@@ -10,8 +10,9 @@
 #include "base/compiler_specific.h"
 #include "base/macros.h"
 #include "ui/app_list/pagination_model_observer.h"
-#include "ui/app_list/presenter/app_list_presenter.h"
+#include "ui/app_list/presenter/app_list_presenter.mojom.h"
 #include "ui/app_list/presenter/app_list_presenter_delegate.h"
+#include "ui/app_list/presenter/app_list_presenter_export.h"
 #include "ui/aura/client/focus_change_observer.h"
 #include "ui/aura/window_observer.h"
 #include "ui/compositor/layer_animation_observer.h"
@@ -34,14 +35,14 @@
 // activation state to auto dismiss the UI. Delegates the responsibility
 // for laying out the app list UI to ash::AppListLayoutDelegate.
 class APP_LIST_PRESENTER_EXPORT AppListPresenterImpl
-    : public AppListPresenter,
-      public aura::client::FocusChangeObserver,
+    : public aura::client::FocusChangeObserver,
       public aura::WindowObserver,
       public ui::ImplicitAnimationObserver,
       public views::WidgetObserver,
       public PaginationModelObserver {
  public:
-  explicit AppListPresenterImpl(AppListPresenterDelegateFactory* factory);
+  explicit AppListPresenterImpl(
+      std::unique_ptr<AppListPresenterDelegateFactory> factory);
   ~AppListPresenterImpl() override;
 
   // Returns app list window or NULL if it is not visible.
@@ -50,12 +51,26 @@
   // Returns app list view if one exists, or NULL otherwise.
   AppListView* GetView() { return view_; }
 
-  // AppListPresenter:
-  void Show(int64_t display_id) override;
-  void Dismiss() final;
-  void ToggleAppList(int64_t display_id) override;
-  bool IsVisible() const override;
-  bool GetTargetVisibility() const override;
+  // Show/hide app list window. The |window| is used to deterime in which
+  // display (in which the |window| exists) the app list should be shown.
+  void Show(int64_t display_id);
+
+  // Invoked to dismiss app list. This may leave the view open but hidden from
+  // the user.
+  void Dismiss();
+
+  // Show the app list if it is visible, hide it if it is hidden.
+  void ToggleAppList(int64_t display_id);
+
+  // Returns current visibility of the app list.
+  bool IsVisible() const;
+
+  // Returns target visibility. This may differ from IsVisible() if a visibility
+  // transition is in progress.
+  bool GetTargetVisibility() const;
+
+  // Sets the app list interface pointer; used to report visibility changes.
+  void SetAppList(mojom::AppListPtr app_list);
 
  private:
   friend class test::AppListPresenterImplTestApi;
@@ -83,6 +98,7 @@
 
   // views::WidgetObserver overrides:
   void OnWidgetDestroying(views::Widget* widget) override;
+  void OnWidgetVisibilityChanged(views::Widget* widget, bool visible) override;
 
   // PaginationModelObserver overrides:
   void TotalPagesChanged() override;
@@ -90,8 +106,8 @@
   void TransitionStarted() override;
   void TransitionChanged() override;
 
-  // Not owned
-  AppListPresenterDelegateFactory* const factory_;
+  // The factory for the presenter's delegate.
+  std::unique_ptr<AppListPresenterDelegateFactory> factory_;
 
   // Responsible for laying out the app list UI.
   std::unique_ptr<AppListPresenterDelegate> presenter_delegate_;
@@ -112,6 +128,9 @@
   // Whether should schedule snap back animation.
   bool should_snap_back_ = false;
 
+  // The app list interface pointer; used for reporting visibility changes.
+  mojom::AppListPtr app_list_;
+
   DISALLOW_COPY_AND_ASSIGN(AppListPresenterImpl);
 };
 
diff --git a/ui/app_list/presenter/app_list_presenter_impl_unittest.cc b/ui/app_list/presenter/app_list_presenter_impl_unittest.cc
index cd9941f..0fd6639 100644
--- a/ui/app_list/presenter/app_list_presenter_impl_unittest.cc
+++ b/ui/app_list/presenter/app_list_presenter_impl_unittest.cc
@@ -74,19 +74,15 @@
       : container_(container) {}
   ~AppListPresenterDelegateFactoryTest() override {}
 
-  AppListPresenterDelegateTest* current_delegate() { return current_delegate_; }
-
   // AppListPresenterDelegateFactory:
   std::unique_ptr<AppListPresenterDelegate> GetDelegate(
-      AppListPresenter* presenter) override {
-    current_delegate_ =
-        new AppListPresenterDelegateTest(container_, &app_list_view_delegate_);
-    return base::WrapUnique(current_delegate_);
+      AppListPresenterImpl* presenter) override {
+    return base::MakeUnique<AppListPresenterDelegateTest>(
+        container_, &app_list_view_delegate_);
   }
 
  private:
   aura::Window* container_;
-  AppListPresenterDelegateTest* current_delegate_ = nullptr;
   test::AppListTestViewDelegate app_list_view_delegate_;
 
   DISALLOW_COPY_AND_ASSIGN(AppListPresenterDelegateFactoryTest);
@@ -106,7 +102,8 @@
   // Don't cache the return of this method - a new delegate is created every
   // time the app list is shown.
   AppListPresenterDelegateTest* delegate() {
-    return factory_->current_delegate();
+    return static_cast<AppListPresenterDelegateTest*>(
+        presenter_test_api_->presenter_delegate());
   }
 
   // aura::test::AuraTestBase:
@@ -114,9 +111,9 @@
   void TearDown() override;
 
  private:
-  std::unique_ptr<AppListPresenterDelegateFactoryTest> factory_;
   std::unique_ptr<AppListPresenterImpl> presenter_;
   std::unique_ptr<aura::Window> container_;
+  std::unique_ptr<test::AppListPresenterImplTestApi> presenter_test_api_;
 
   DISALLOW_COPY_AND_ASSIGN(AppListPresenterImplTest);
 };
@@ -129,8 +126,10 @@
   AuraTestBase::SetUp();
   new wm::DefaultActivationClient(root_window());
   container_.reset(CreateNormalWindow(0, root_window(), nullptr));
-  factory_.reset(new AppListPresenterDelegateFactoryTest(container_.get()));
-  presenter_.reset(new AppListPresenterImpl(factory_.get()));
+  presenter_ = base::MakeUnique<AppListPresenterImpl>(
+      base::MakeUnique<AppListPresenterDelegateFactoryTest>(container_.get()));
+  presenter_test_api_ =
+      base::MakeUnique<test::AppListPresenterImplTestApi>(presenter());
 }
 
 void AppListPresenterImplTest::TearDown() {
@@ -204,8 +203,7 @@
   EXPECT_TRUE(presenter()->GetTargetVisibility());
   presenter()->GetView()->GetWidget()->CloseNow();
   EXPECT_FALSE(presenter()->GetTargetVisibility());
-  test::AppListPresenterImplTestApi presenter_test_api(presenter());
-  EXPECT_FALSE(presenter_test_api.presenter_delegate());
+  EXPECT_FALSE(delegate());
 }
 
 }  // namespace app_list
diff --git a/ui/app_list/presenter/app_list_view_delegate_factory.h b/ui/app_list/presenter/app_list_view_delegate_factory.h
index 612d6e3..2319692 100644
--- a/ui/app_list/presenter/app_list_view_delegate_factory.h
+++ b/ui/app_list/presenter/app_list_view_delegate_factory.h
@@ -14,6 +14,7 @@
  public:
   virtual ~AppListViewDelegateFactory();
 
+  // This does not pass ownership of the delegate.
   virtual app_list::AppListViewDelegate* GetDelegate() = 0;
 };
 
diff --git a/ui/app_list/presenter/test/test_app_list_presenter.cc b/ui/app_list/presenter/test/test_app_list_presenter.cc
new file mode 100644
index 0000000..f83b116
--- /dev/null
+++ b/ui/app_list/presenter/test/test_app_list_presenter.cc
@@ -0,0 +1,32 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/app_list/presenter/test/test_app_list_presenter.h"
+
+namespace app_list {
+namespace test {
+
+TestAppListPresenter::TestAppListPresenter() : binding_(this) {}
+
+TestAppListPresenter::~TestAppListPresenter() {}
+
+app_list::mojom::AppListPresenterPtr
+TestAppListPresenter::CreateInterfacePtrAndBind() {
+  return binding_.CreateInterfacePtrAndBind();
+}
+
+void TestAppListPresenter::Show(int64_t display_id) {
+  show_count_++;
+}
+
+void TestAppListPresenter::Dismiss() {
+  dismiss_count_++;
+}
+
+void TestAppListPresenter::ToggleAppList(int64_t display_id) {
+  toggle_count_++;
+}
+
+}  // namespace test
+}  // namespace app_list
diff --git a/ui/app_list/presenter/test/test_app_list_presenter.h b/ui/app_list/presenter/test/test_app_list_presenter.h
new file mode 100644
index 0000000..947e1965
--- /dev/null
+++ b/ui/app_list/presenter/test/test_app_list_presenter.h
@@ -0,0 +1,46 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_APP_LIST_PRESENTER_TEST_TEST_APP_LIST_PRESENTER_H_
+#define UI_APP_LIST_PRESENTER_TEST_TEST_APP_LIST_PRESENTER_H_
+
+#include "base/macros.h"
+#include "mojo/public/cpp/bindings/binding.h"
+#include "ui/app_list/presenter/app_list_presenter.mojom.h"
+
+namespace app_list {
+namespace test {
+
+// A test implementation of AppListPresenter that records function call counts.
+// Registers itself as the presenter for the app list on construction.
+class TestAppListPresenter : public app_list::mojom::AppListPresenter {
+ public:
+  TestAppListPresenter();
+  ~TestAppListPresenter() override;
+
+  app_list::mojom::AppListPresenterPtr CreateInterfacePtrAndBind();
+
+  // app_list::mojom::AppListPresenter:
+  void Show(int64_t display_id) override;
+  void Dismiss() override;
+  void ToggleAppList(int64_t display_id) override;
+
+  size_t show_count() const { return show_count_; }
+  size_t dismiss_count() const { return dismiss_count_; }
+  size_t toggle_count() const { return toggle_count_; }
+
+ private:
+  size_t show_count_ = 0u;
+  size_t dismiss_count_ = 0u;
+  size_t toggle_count_ = 0u;
+
+  mojo::Binding<app_list::mojom::AppListPresenter> binding_;
+
+  DISALLOW_COPY_AND_ASSIGN(TestAppListPresenter);
+};
+
+}  // namespace test
+}  // namespace app_list
+
+#endif  // UI_APP_LIST_PRESENTER_TEST_TEST_APP_LIST_PRESENTER_H_
diff --git a/ui/app_list/presenter/test/test_app_list_view_delegate_factory.cc b/ui/app_list/presenter/test/test_app_list_view_delegate_factory.cc
new file mode 100644
index 0000000..b982518
--- /dev/null
+++ b/ui/app_list/presenter/test/test_app_list_view_delegate_factory.cc
@@ -0,0 +1,19 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/app_list/presenter/test/test_app_list_view_delegate_factory.h"
+
+namespace app_list {
+namespace test {
+
+TestAppListViewDelegateFactory::TestAppListViewDelegateFactory() {}
+
+TestAppListViewDelegateFactory::~TestAppListViewDelegateFactory() {}
+
+AppListViewDelegate* TestAppListViewDelegateFactory::GetDelegate() {
+  return &delegate_;
+}
+
+}  // namespace test
+}  // namespace app_list
diff --git a/ui/app_list/presenter/test/test_app_list_view_delegate_factory.h b/ui/app_list/presenter/test/test_app_list_view_delegate_factory.h
new file mode 100644
index 0000000..6c1a069
--- /dev/null
+++ b/ui/app_list/presenter/test/test_app_list_view_delegate_factory.h
@@ -0,0 +1,32 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_APP_LIST_PRESENTER_TEST_TEST_APP_LIST_VIEW_DELEGATE_FACTORY_H_
+#define UI_APP_LIST_PRESENTER_TEST_TEST_APP_LIST_VIEW_DELEGATE_FACTORY_H_
+
+#include "base/macros.h"
+#include "ui/app_list/presenter/app_list_view_delegate_factory.h"
+#include "ui/app_list/test/app_list_test_view_delegate.h"
+
+namespace app_list {
+namespace test {
+
+// A concrete AppListViewDelegateFactory for unit tests.
+class TestAppListViewDelegateFactory : public AppListViewDelegateFactory {
+ public:
+  TestAppListViewDelegateFactory();
+  ~TestAppListViewDelegateFactory() override;
+
+  AppListViewDelegate* GetDelegate() override;
+
+ private:
+  AppListTestViewDelegate delegate_;
+
+  DISALLOW_COPY_AND_ASSIGN(TestAppListViewDelegateFactory);
+};
+
+}  // namespace test
+}  // namespace app_list
+
+#endif  // UI_APP_LIST_PRESENTER_TEST_TEST_APP_LIST_VIEW_DELEGATE_FACTORY_H_
diff --git a/ui/aura/mus/drag_drop_controller_mus.cc b/ui/aura/mus/drag_drop_controller_mus.cc
index 972eea8..a82841c8 100644
--- a/ui/aura/mus/drag_drop_controller_mus.cc
+++ b/ui/aura/mus/drag_drop_controller_mus.cc
@@ -12,6 +12,7 @@
 #include "base/memory/ptr_util.h"
 #include "base/message_loop/message_loop.h"
 #include "base/run_loop.h"
+#include "mojo/public/cpp/bindings/map.h"
 #include "services/ui/public/interfaces/window_tree.mojom.h"
 #include "services/ui/public/interfaces/window_tree_constants.mojom.h"
 #include "ui/aura/client/drag_drop_delegate.h"
diff --git a/ui/aura/mus/os_exchange_data_provider_mus.h b/ui/aura/mus/os_exchange_data_provider_mus.h
index 4293faf9..e8037bf 100644
--- a/ui/aura/mus/os_exchange_data_provider_mus.h
+++ b/ui/aura/mus/os_exchange_data_provider_mus.h
@@ -11,7 +11,6 @@
 #include <utility>
 #include <vector>
 
-#include "mojo/public/cpp/bindings/map.h"
 #include "ui/aura/aura_export.h"
 #include "ui/base/dragdrop/os_exchange_data.h"
 #include "ui/gfx/geometry/vector2d.h"
diff --git a/ui/aura/mus/window_tree_client.cc b/ui/aura/mus/window_tree_client.cc
index e9d1c07..9f28713 100644
--- a/ui/aura/mus/window_tree_client.cc
+++ b/ui/aura/mus/window_tree_client.cc
@@ -13,6 +13,7 @@
 #include "base/auto_reset.h"
 #include "base/bind.h"
 #include "base/memory/ptr_util.h"
+#include "mojo/public/cpp/bindings/map.h"
 #include "services/service_manager/public/cpp/connector.h"
 #include "services/ui/common/accelerator_util.h"
 #include "services/ui/public/cpp/property_type_converters.h"
diff --git a/ui/aura/mus/window_tree_client_unittest.cc b/ui/aura/mus/window_tree_client_unittest.cc
index f3aeeed..8792478 100644
--- a/ui/aura/mus/window_tree_client_unittest.cc
+++ b/ui/aura/mus/window_tree_client_unittest.cc
@@ -12,6 +12,7 @@
 #include "base/memory/ptr_util.h"
 #include "base/strings/utf_string_conversions.h"
 #include "mojo/common/common_type_converters.h"
+#include "mojo/public/cpp/bindings/map.h"
 #include "services/ui/public/cpp/property_type_converters.h"
 #include "services/ui/public/interfaces/window_manager.mojom.h"
 #include "testing/gtest/include/gtest/gtest.h"
diff --git a/ui/gl/generate_bindings.py b/ui/gl/generate_bindings.py
index 3640aac..d06481a 100755
--- a/ui/gl/generate_bindings.py
+++ b/ui/gl/generate_bindings.py
@@ -2976,14 +2976,17 @@
 
   # Write a function to lookup a mock GL function based on its name.
   file.write('\n')
-  file.write('void* GL_BINDING_CALL ' +
+  file.write('GLFunctionPointerType GL_BINDING_CALL ' +
       'MockGLInterface::GetGLProcAddress(const char* name) {\n')
   for key in sorted_function_names:
     name = uniquely_named_functions[key]['name']
     file.write('  if (strcmp(name, "%s") == 0)\n' % name)
-    file.write('    return reinterpret_cast<void*>(Mock_%s);\n' % name)
+    file.write(
+        '    return reinterpret_cast<GLFunctionPointerType>(Mock_%s);\n' %
+            name)
   # Always return a non-NULL pointer like some EGL implementations do.
-  file.write('  return reinterpret_cast<void*>(&MockInvalidFunction);\n')
+  file.write('  return reinterpret_cast<GLFunctionPointerType>('
+             '&MockInvalidFunction);\n')
   file.write('}\n')
 
   file.write('\n')
diff --git a/ui/gl/gl_api_unittest.cc b/ui/gl/gl_api_unittest.cc
index 23c3075..6f274b4 100644
--- a/ui/gl/gl_api_unittest.cc
+++ b/ui/gl/gl_api_unittest.cc
@@ -51,8 +51,9 @@
         static_cast<GLGetProcAddressProc>(&FakeGLGetProcAddress));
   }
 
-  static void* GL_BINDING_CALL FakeGLGetProcAddress(const char *proc) {
-    return reinterpret_cast<void*>(0x1);
+  static GLFunctionPointerType GL_BINDING_CALL
+  FakeGLGetProcAddress(const char* proc) {
+    return reinterpret_cast<GLFunctionPointerType>(0x1);
   }
 
   void TearDown() override {
diff --git a/ui/gl/gl_bindings_autogen_mock.cc b/ui/gl/gl_bindings_autogen_mock.cc
index 453b7a1..50c40dd2 100644
--- a/ui/gl/gl_bindings_autogen_mock.cc
+++ b/ui/gl/gl_bindings_autogen_mock.cc
@@ -3865,958 +3865,1106 @@
   NOTREACHED();
 }
 
-void* GL_BINDING_CALL MockGLInterface::GetGLProcAddress(const char* name) {
+GLFunctionPointerType GL_BINDING_CALL
+MockGLInterface::GetGLProcAddress(const char* name) {
   if (strcmp(name, "glActiveTexture") == 0)
-    return reinterpret_cast<void*>(Mock_glActiveTexture);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glActiveTexture);
   if (strcmp(name, "glApplyFramebufferAttachmentCMAAINTEL") == 0)
-    return reinterpret_cast<void*>(Mock_glApplyFramebufferAttachmentCMAAINTEL);
+    return reinterpret_cast<GLFunctionPointerType>(
+        Mock_glApplyFramebufferAttachmentCMAAINTEL);
   if (strcmp(name, "glAttachShader") == 0)
-    return reinterpret_cast<void*>(Mock_glAttachShader);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glAttachShader);
   if (strcmp(name, "glBeginQuery") == 0)
-    return reinterpret_cast<void*>(Mock_glBeginQuery);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glBeginQuery);
   if (strcmp(name, "glBeginQueryARB") == 0)
-    return reinterpret_cast<void*>(Mock_glBeginQueryARB);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glBeginQueryARB);
   if (strcmp(name, "glBeginQueryEXT") == 0)
-    return reinterpret_cast<void*>(Mock_glBeginQueryEXT);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glBeginQueryEXT);
   if (strcmp(name, "glBeginTransformFeedback") == 0)
-    return reinterpret_cast<void*>(Mock_glBeginTransformFeedback);
+    return reinterpret_cast<GLFunctionPointerType>(
+        Mock_glBeginTransformFeedback);
   if (strcmp(name, "glBeginTransformFeedbackEXT") == 0)
-    return reinterpret_cast<void*>(Mock_glBeginTransformFeedbackEXT);
+    return reinterpret_cast<GLFunctionPointerType>(
+        Mock_glBeginTransformFeedbackEXT);
   if (strcmp(name, "glBindAttribLocation") == 0)
-    return reinterpret_cast<void*>(Mock_glBindAttribLocation);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glBindAttribLocation);
   if (strcmp(name, "glBindBuffer") == 0)
-    return reinterpret_cast<void*>(Mock_glBindBuffer);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glBindBuffer);
   if (strcmp(name, "glBindBufferBase") == 0)
-    return reinterpret_cast<void*>(Mock_glBindBufferBase);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glBindBufferBase);
   if (strcmp(name, "glBindBufferBaseEXT") == 0)
-    return reinterpret_cast<void*>(Mock_glBindBufferBaseEXT);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glBindBufferBaseEXT);
   if (strcmp(name, "glBindBufferRange") == 0)
-    return reinterpret_cast<void*>(Mock_glBindBufferRange);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glBindBufferRange);
   if (strcmp(name, "glBindBufferRangeEXT") == 0)
-    return reinterpret_cast<void*>(Mock_glBindBufferRangeEXT);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glBindBufferRangeEXT);
   if (strcmp(name, "glBindFragDataLocation") == 0)
-    return reinterpret_cast<void*>(Mock_glBindFragDataLocation);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glBindFragDataLocation);
   if (strcmp(name, "glBindFragDataLocationEXT") == 0)
-    return reinterpret_cast<void*>(Mock_glBindFragDataLocationEXT);
+    return reinterpret_cast<GLFunctionPointerType>(
+        Mock_glBindFragDataLocationEXT);
   if (strcmp(name, "glBindFragDataLocationIndexed") == 0)
-    return reinterpret_cast<void*>(Mock_glBindFragDataLocationIndexed);
+    return reinterpret_cast<GLFunctionPointerType>(
+        Mock_glBindFragDataLocationIndexed);
   if (strcmp(name, "glBindFragDataLocationIndexedEXT") == 0)
-    return reinterpret_cast<void*>(Mock_glBindFragDataLocationIndexedEXT);
+    return reinterpret_cast<GLFunctionPointerType>(
+        Mock_glBindFragDataLocationIndexedEXT);
   if (strcmp(name, "glBindFramebuffer") == 0)
-    return reinterpret_cast<void*>(Mock_glBindFramebuffer);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glBindFramebuffer);
   if (strcmp(name, "glBindFramebufferEXT") == 0)
-    return reinterpret_cast<void*>(Mock_glBindFramebufferEXT);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glBindFramebufferEXT);
   if (strcmp(name, "glBindImageTexture") == 0)
-    return reinterpret_cast<void*>(Mock_glBindImageTexture);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glBindImageTexture);
   if (strcmp(name, "glBindImageTextureEXT") == 0)
-    return reinterpret_cast<void*>(Mock_glBindImageTextureEXT);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glBindImageTextureEXT);
   if (strcmp(name, "glBindRenderbuffer") == 0)
-    return reinterpret_cast<void*>(Mock_glBindRenderbuffer);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glBindRenderbuffer);
   if (strcmp(name, "glBindRenderbufferEXT") == 0)
-    return reinterpret_cast<void*>(Mock_glBindRenderbufferEXT);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glBindRenderbufferEXT);
   if (strcmp(name, "glBindSampler") == 0)
-    return reinterpret_cast<void*>(Mock_glBindSampler);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glBindSampler);
   if (strcmp(name, "glBindTexture") == 0)
-    return reinterpret_cast<void*>(Mock_glBindTexture);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glBindTexture);
   if (strcmp(name, "glBindTransformFeedback") == 0)
-    return reinterpret_cast<void*>(Mock_glBindTransformFeedback);
+    return reinterpret_cast<GLFunctionPointerType>(
+        Mock_glBindTransformFeedback);
   if (strcmp(name, "glBindUniformLocationCHROMIUM") == 0)
-    return reinterpret_cast<void*>(Mock_glBindUniformLocationCHROMIUM);
+    return reinterpret_cast<GLFunctionPointerType>(
+        Mock_glBindUniformLocationCHROMIUM);
   if (strcmp(name, "glBindVertexArray") == 0)
-    return reinterpret_cast<void*>(Mock_glBindVertexArray);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glBindVertexArray);
   if (strcmp(name, "glBindVertexArrayAPPLE") == 0)
-    return reinterpret_cast<void*>(Mock_glBindVertexArrayAPPLE);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glBindVertexArrayAPPLE);
   if (strcmp(name, "glBindVertexArrayOES") == 0)
-    return reinterpret_cast<void*>(Mock_glBindVertexArrayOES);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glBindVertexArrayOES);
   if (strcmp(name, "glBlendBarrierKHR") == 0)
-    return reinterpret_cast<void*>(Mock_glBlendBarrierKHR);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glBlendBarrierKHR);
   if (strcmp(name, "glBlendBarrierNV") == 0)
-    return reinterpret_cast<void*>(Mock_glBlendBarrierNV);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glBlendBarrierNV);
   if (strcmp(name, "glBlendColor") == 0)
-    return reinterpret_cast<void*>(Mock_glBlendColor);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glBlendColor);
   if (strcmp(name, "glBlendEquation") == 0)
-    return reinterpret_cast<void*>(Mock_glBlendEquation);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glBlendEquation);
   if (strcmp(name, "glBlendEquationSeparate") == 0)
-    return reinterpret_cast<void*>(Mock_glBlendEquationSeparate);
+    return reinterpret_cast<GLFunctionPointerType>(
+        Mock_glBlendEquationSeparate);
   if (strcmp(name, "glBlendFunc") == 0)
-    return reinterpret_cast<void*>(Mock_glBlendFunc);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glBlendFunc);
   if (strcmp(name, "glBlendFuncSeparate") == 0)
-    return reinterpret_cast<void*>(Mock_glBlendFuncSeparate);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glBlendFuncSeparate);
   if (strcmp(name, "glBlitFramebuffer") == 0)
-    return reinterpret_cast<void*>(Mock_glBlitFramebuffer);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glBlitFramebuffer);
   if (strcmp(name, "glBlitFramebufferANGLE") == 0)
-    return reinterpret_cast<void*>(Mock_glBlitFramebufferANGLE);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glBlitFramebufferANGLE);
   if (strcmp(name, "glBlitFramebufferEXT") == 0)
-    return reinterpret_cast<void*>(Mock_glBlitFramebufferEXT);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glBlitFramebufferEXT);
   if (strcmp(name, "glBufferData") == 0)
-    return reinterpret_cast<void*>(Mock_glBufferData);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glBufferData);
   if (strcmp(name, "glBufferSubData") == 0)
-    return reinterpret_cast<void*>(Mock_glBufferSubData);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glBufferSubData);
   if (strcmp(name, "glCheckFramebufferStatus") == 0)
-    return reinterpret_cast<void*>(Mock_glCheckFramebufferStatus);
+    return reinterpret_cast<GLFunctionPointerType>(
+        Mock_glCheckFramebufferStatus);
   if (strcmp(name, "glCheckFramebufferStatusEXT") == 0)
-    return reinterpret_cast<void*>(Mock_glCheckFramebufferStatusEXT);
+    return reinterpret_cast<GLFunctionPointerType>(
+        Mock_glCheckFramebufferStatusEXT);
   if (strcmp(name, "glClear") == 0)
-    return reinterpret_cast<void*>(Mock_glClear);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glClear);
   if (strcmp(name, "glClearBufferfi") == 0)
-    return reinterpret_cast<void*>(Mock_glClearBufferfi);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glClearBufferfi);
   if (strcmp(name, "glClearBufferfv") == 0)
-    return reinterpret_cast<void*>(Mock_glClearBufferfv);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glClearBufferfv);
   if (strcmp(name, "glClearBufferiv") == 0)
-    return reinterpret_cast<void*>(Mock_glClearBufferiv);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glClearBufferiv);
   if (strcmp(name, "glClearBufferuiv") == 0)
-    return reinterpret_cast<void*>(Mock_glClearBufferuiv);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glClearBufferuiv);
   if (strcmp(name, "glClearColor") == 0)
-    return reinterpret_cast<void*>(Mock_glClearColor);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glClearColor);
   if (strcmp(name, "glClearDepth") == 0)
-    return reinterpret_cast<void*>(Mock_glClearDepth);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glClearDepth);
   if (strcmp(name, "glClearDepthf") == 0)
-    return reinterpret_cast<void*>(Mock_glClearDepthf);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glClearDepthf);
   if (strcmp(name, "glClearStencil") == 0)
-    return reinterpret_cast<void*>(Mock_glClearStencil);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glClearStencil);
   if (strcmp(name, "glClientWaitSync") == 0)
-    return reinterpret_cast<void*>(Mock_glClientWaitSync);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glClientWaitSync);
   if (strcmp(name, "glColorMask") == 0)
-    return reinterpret_cast<void*>(Mock_glColorMask);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glColorMask);
   if (strcmp(name, "glCompileShader") == 0)
-    return reinterpret_cast<void*>(Mock_glCompileShader);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glCompileShader);
   if (strcmp(name, "glCompressedCopyTextureCHROMIUM") == 0)
-    return reinterpret_cast<void*>(Mock_glCompressedCopyTextureCHROMIUM);
+    return reinterpret_cast<GLFunctionPointerType>(
+        Mock_glCompressedCopyTextureCHROMIUM);
   if (strcmp(name, "glCompressedTexImage2D") == 0)
-    return reinterpret_cast<void*>(Mock_glCompressedTexImage2D);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glCompressedTexImage2D);
   if (strcmp(name, "glCompressedTexImage3D") == 0)
-    return reinterpret_cast<void*>(Mock_glCompressedTexImage3D);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glCompressedTexImage3D);
   if (strcmp(name, "glCompressedTexSubImage2D") == 0)
-    return reinterpret_cast<void*>(Mock_glCompressedTexSubImage2D);
+    return reinterpret_cast<GLFunctionPointerType>(
+        Mock_glCompressedTexSubImage2D);
   if (strcmp(name, "glCompressedTexSubImage3D") == 0)
-    return reinterpret_cast<void*>(Mock_glCompressedTexSubImage3D);
+    return reinterpret_cast<GLFunctionPointerType>(
+        Mock_glCompressedTexSubImage3D);
   if (strcmp(name, "glCopyBufferSubData") == 0)
-    return reinterpret_cast<void*>(Mock_glCopyBufferSubData);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glCopyBufferSubData);
   if (strcmp(name, "glCopySubTextureCHROMIUM") == 0)
-    return reinterpret_cast<void*>(Mock_glCopySubTextureCHROMIUM);
+    return reinterpret_cast<GLFunctionPointerType>(
+        Mock_glCopySubTextureCHROMIUM);
   if (strcmp(name, "glCopyTexImage2D") == 0)
-    return reinterpret_cast<void*>(Mock_glCopyTexImage2D);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glCopyTexImage2D);
   if (strcmp(name, "glCopyTexSubImage2D") == 0)
-    return reinterpret_cast<void*>(Mock_glCopyTexSubImage2D);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glCopyTexSubImage2D);
   if (strcmp(name, "glCopyTexSubImage3D") == 0)
-    return reinterpret_cast<void*>(Mock_glCopyTexSubImage3D);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glCopyTexSubImage3D);
   if (strcmp(name, "glCopyTextureCHROMIUM") == 0)
-    return reinterpret_cast<void*>(Mock_glCopyTextureCHROMIUM);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glCopyTextureCHROMIUM);
   if (strcmp(name, "glCoverFillPathInstancedNV") == 0)
-    return reinterpret_cast<void*>(Mock_glCoverFillPathInstancedNV);
+    return reinterpret_cast<GLFunctionPointerType>(
+        Mock_glCoverFillPathInstancedNV);
   if (strcmp(name, "glCoverFillPathNV") == 0)
-    return reinterpret_cast<void*>(Mock_glCoverFillPathNV);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glCoverFillPathNV);
   if (strcmp(name, "glCoverStrokePathInstancedNV") == 0)
-    return reinterpret_cast<void*>(Mock_glCoverStrokePathInstancedNV);
+    return reinterpret_cast<GLFunctionPointerType>(
+        Mock_glCoverStrokePathInstancedNV);
   if (strcmp(name, "glCoverStrokePathNV") == 0)
-    return reinterpret_cast<void*>(Mock_glCoverStrokePathNV);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glCoverStrokePathNV);
   if (strcmp(name, "glCoverageModulationNV") == 0)
-    return reinterpret_cast<void*>(Mock_glCoverageModulationNV);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glCoverageModulationNV);
   if (strcmp(name, "glCreateProgram") == 0)
-    return reinterpret_cast<void*>(Mock_glCreateProgram);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glCreateProgram);
   if (strcmp(name, "glCreateShader") == 0)
-    return reinterpret_cast<void*>(Mock_glCreateShader);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glCreateShader);
   if (strcmp(name, "glCullFace") == 0)
-    return reinterpret_cast<void*>(Mock_glCullFace);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glCullFace);
   if (strcmp(name, "glDebugMessageCallback") == 0)
-    return reinterpret_cast<void*>(Mock_glDebugMessageCallback);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glDebugMessageCallback);
   if (strcmp(name, "glDebugMessageCallbackKHR") == 0)
-    return reinterpret_cast<void*>(Mock_glDebugMessageCallbackKHR);
+    return reinterpret_cast<GLFunctionPointerType>(
+        Mock_glDebugMessageCallbackKHR);
   if (strcmp(name, "glDebugMessageControl") == 0)
-    return reinterpret_cast<void*>(Mock_glDebugMessageControl);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glDebugMessageControl);
   if (strcmp(name, "glDebugMessageControlKHR") == 0)
-    return reinterpret_cast<void*>(Mock_glDebugMessageControlKHR);
+    return reinterpret_cast<GLFunctionPointerType>(
+        Mock_glDebugMessageControlKHR);
   if (strcmp(name, "glDebugMessageInsert") == 0)
-    return reinterpret_cast<void*>(Mock_glDebugMessageInsert);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glDebugMessageInsert);
   if (strcmp(name, "glDebugMessageInsertKHR") == 0)
-    return reinterpret_cast<void*>(Mock_glDebugMessageInsertKHR);
+    return reinterpret_cast<GLFunctionPointerType>(
+        Mock_glDebugMessageInsertKHR);
   if (strcmp(name, "glDeleteBuffers") == 0)
-    return reinterpret_cast<void*>(Mock_glDeleteBuffers);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glDeleteBuffers);
   if (strcmp(name, "glDeleteFencesAPPLE") == 0)
-    return reinterpret_cast<void*>(Mock_glDeleteFencesAPPLE);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glDeleteFencesAPPLE);
   if (strcmp(name, "glDeleteFencesNV") == 0)
-    return reinterpret_cast<void*>(Mock_glDeleteFencesNV);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glDeleteFencesNV);
   if (strcmp(name, "glDeleteFramebuffers") == 0)
-    return reinterpret_cast<void*>(Mock_glDeleteFramebuffers);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glDeleteFramebuffers);
   if (strcmp(name, "glDeleteFramebuffersEXT") == 0)
-    return reinterpret_cast<void*>(Mock_glDeleteFramebuffersEXT);
+    return reinterpret_cast<GLFunctionPointerType>(
+        Mock_glDeleteFramebuffersEXT);
   if (strcmp(name, "glDeletePathsNV") == 0)
-    return reinterpret_cast<void*>(Mock_glDeletePathsNV);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glDeletePathsNV);
   if (strcmp(name, "glDeleteProgram") == 0)
-    return reinterpret_cast<void*>(Mock_glDeleteProgram);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glDeleteProgram);
   if (strcmp(name, "glDeleteQueries") == 0)
-    return reinterpret_cast<void*>(Mock_glDeleteQueries);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glDeleteQueries);
   if (strcmp(name, "glDeleteQueriesARB") == 0)
-    return reinterpret_cast<void*>(Mock_glDeleteQueriesARB);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glDeleteQueriesARB);
   if (strcmp(name, "glDeleteQueriesEXT") == 0)
-    return reinterpret_cast<void*>(Mock_glDeleteQueriesEXT);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glDeleteQueriesEXT);
   if (strcmp(name, "glDeleteRenderbuffers") == 0)
-    return reinterpret_cast<void*>(Mock_glDeleteRenderbuffers);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glDeleteRenderbuffers);
   if (strcmp(name, "glDeleteRenderbuffersEXT") == 0)
-    return reinterpret_cast<void*>(Mock_glDeleteRenderbuffersEXT);
+    return reinterpret_cast<GLFunctionPointerType>(
+        Mock_glDeleteRenderbuffersEXT);
   if (strcmp(name, "glDeleteSamplers") == 0)
-    return reinterpret_cast<void*>(Mock_glDeleteSamplers);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glDeleteSamplers);
   if (strcmp(name, "glDeleteShader") == 0)
-    return reinterpret_cast<void*>(Mock_glDeleteShader);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glDeleteShader);
   if (strcmp(name, "glDeleteSync") == 0)
-    return reinterpret_cast<void*>(Mock_glDeleteSync);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glDeleteSync);
   if (strcmp(name, "glDeleteTextures") == 0)
-    return reinterpret_cast<void*>(Mock_glDeleteTextures);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glDeleteTextures);
   if (strcmp(name, "glDeleteTransformFeedbacks") == 0)
-    return reinterpret_cast<void*>(Mock_glDeleteTransformFeedbacks);
+    return reinterpret_cast<GLFunctionPointerType>(
+        Mock_glDeleteTransformFeedbacks);
   if (strcmp(name, "glDeleteVertexArrays") == 0)
-    return reinterpret_cast<void*>(Mock_glDeleteVertexArrays);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glDeleteVertexArrays);
   if (strcmp(name, "glDeleteVertexArraysAPPLE") == 0)
-    return reinterpret_cast<void*>(Mock_glDeleteVertexArraysAPPLE);
+    return reinterpret_cast<GLFunctionPointerType>(
+        Mock_glDeleteVertexArraysAPPLE);
   if (strcmp(name, "glDeleteVertexArraysOES") == 0)
-    return reinterpret_cast<void*>(Mock_glDeleteVertexArraysOES);
+    return reinterpret_cast<GLFunctionPointerType>(
+        Mock_glDeleteVertexArraysOES);
   if (strcmp(name, "glDepthFunc") == 0)
-    return reinterpret_cast<void*>(Mock_glDepthFunc);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glDepthFunc);
   if (strcmp(name, "glDepthMask") == 0)
-    return reinterpret_cast<void*>(Mock_glDepthMask);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glDepthMask);
   if (strcmp(name, "glDepthRange") == 0)
-    return reinterpret_cast<void*>(Mock_glDepthRange);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glDepthRange);
   if (strcmp(name, "glDepthRangef") == 0)
-    return reinterpret_cast<void*>(Mock_glDepthRangef);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glDepthRangef);
   if (strcmp(name, "glDetachShader") == 0)
-    return reinterpret_cast<void*>(Mock_glDetachShader);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glDetachShader);
   if (strcmp(name, "glDisable") == 0)
-    return reinterpret_cast<void*>(Mock_glDisable);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glDisable);
   if (strcmp(name, "glDisableVertexAttribArray") == 0)
-    return reinterpret_cast<void*>(Mock_glDisableVertexAttribArray);
+    return reinterpret_cast<GLFunctionPointerType>(
+        Mock_glDisableVertexAttribArray);
   if (strcmp(name, "glDiscardFramebufferEXT") == 0)
-    return reinterpret_cast<void*>(Mock_glDiscardFramebufferEXT);
+    return reinterpret_cast<GLFunctionPointerType>(
+        Mock_glDiscardFramebufferEXT);
   if (strcmp(name, "glDrawArrays") == 0)
-    return reinterpret_cast<void*>(Mock_glDrawArrays);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glDrawArrays);
   if (strcmp(name, "glDrawArraysInstanced") == 0)
-    return reinterpret_cast<void*>(Mock_glDrawArraysInstanced);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glDrawArraysInstanced);
   if (strcmp(name, "glDrawArraysInstancedANGLE") == 0)
-    return reinterpret_cast<void*>(Mock_glDrawArraysInstancedANGLE);
+    return reinterpret_cast<GLFunctionPointerType>(
+        Mock_glDrawArraysInstancedANGLE);
   if (strcmp(name, "glDrawArraysInstancedARB") == 0)
-    return reinterpret_cast<void*>(Mock_glDrawArraysInstancedARB);
+    return reinterpret_cast<GLFunctionPointerType>(
+        Mock_glDrawArraysInstancedARB);
   if (strcmp(name, "glDrawBuffer") == 0)
-    return reinterpret_cast<void*>(Mock_glDrawBuffer);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glDrawBuffer);
   if (strcmp(name, "glDrawBuffers") == 0)
-    return reinterpret_cast<void*>(Mock_glDrawBuffers);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glDrawBuffers);
   if (strcmp(name, "glDrawBuffersARB") == 0)
-    return reinterpret_cast<void*>(Mock_glDrawBuffersARB);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glDrawBuffersARB);
   if (strcmp(name, "glDrawBuffersEXT") == 0)
-    return reinterpret_cast<void*>(Mock_glDrawBuffersEXT);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glDrawBuffersEXT);
   if (strcmp(name, "glDrawElements") == 0)
-    return reinterpret_cast<void*>(Mock_glDrawElements);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glDrawElements);
   if (strcmp(name, "glDrawElementsInstanced") == 0)
-    return reinterpret_cast<void*>(Mock_glDrawElementsInstanced);
+    return reinterpret_cast<GLFunctionPointerType>(
+        Mock_glDrawElementsInstanced);
   if (strcmp(name, "glDrawElementsInstancedANGLE") == 0)
-    return reinterpret_cast<void*>(Mock_glDrawElementsInstancedANGLE);
+    return reinterpret_cast<GLFunctionPointerType>(
+        Mock_glDrawElementsInstancedANGLE);
   if (strcmp(name, "glDrawElementsInstancedARB") == 0)
-    return reinterpret_cast<void*>(Mock_glDrawElementsInstancedARB);
+    return reinterpret_cast<GLFunctionPointerType>(
+        Mock_glDrawElementsInstancedARB);
   if (strcmp(name, "glDrawRangeElements") == 0)
-    return reinterpret_cast<void*>(Mock_glDrawRangeElements);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glDrawRangeElements);
   if (strcmp(name, "glEGLImageTargetRenderbufferStorageOES") == 0)
-    return reinterpret_cast<void*>(Mock_glEGLImageTargetRenderbufferStorageOES);
+    return reinterpret_cast<GLFunctionPointerType>(
+        Mock_glEGLImageTargetRenderbufferStorageOES);
   if (strcmp(name, "glEGLImageTargetTexture2DOES") == 0)
-    return reinterpret_cast<void*>(Mock_glEGLImageTargetTexture2DOES);
+    return reinterpret_cast<GLFunctionPointerType>(
+        Mock_glEGLImageTargetTexture2DOES);
   if (strcmp(name, "glEnable") == 0)
-    return reinterpret_cast<void*>(Mock_glEnable);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glEnable);
   if (strcmp(name, "glEnableVertexAttribArray") == 0)
-    return reinterpret_cast<void*>(Mock_glEnableVertexAttribArray);
+    return reinterpret_cast<GLFunctionPointerType>(
+        Mock_glEnableVertexAttribArray);
   if (strcmp(name, "glEndQuery") == 0)
-    return reinterpret_cast<void*>(Mock_glEndQuery);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glEndQuery);
   if (strcmp(name, "glEndQueryARB") == 0)
-    return reinterpret_cast<void*>(Mock_glEndQueryARB);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glEndQueryARB);
   if (strcmp(name, "glEndQueryEXT") == 0)
-    return reinterpret_cast<void*>(Mock_glEndQueryEXT);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glEndQueryEXT);
   if (strcmp(name, "glEndTransformFeedback") == 0)
-    return reinterpret_cast<void*>(Mock_glEndTransformFeedback);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glEndTransformFeedback);
   if (strcmp(name, "glEndTransformFeedbackEXT") == 0)
-    return reinterpret_cast<void*>(Mock_glEndTransformFeedbackEXT);
+    return reinterpret_cast<GLFunctionPointerType>(
+        Mock_glEndTransformFeedbackEXT);
   if (strcmp(name, "glFenceSync") == 0)
-    return reinterpret_cast<void*>(Mock_glFenceSync);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glFenceSync);
   if (strcmp(name, "glFinish") == 0)
-    return reinterpret_cast<void*>(Mock_glFinish);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glFinish);
   if (strcmp(name, "glFinishFenceAPPLE") == 0)
-    return reinterpret_cast<void*>(Mock_glFinishFenceAPPLE);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glFinishFenceAPPLE);
   if (strcmp(name, "glFinishFenceNV") == 0)
-    return reinterpret_cast<void*>(Mock_glFinishFenceNV);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glFinishFenceNV);
   if (strcmp(name, "glFlush") == 0)
-    return reinterpret_cast<void*>(Mock_glFlush);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glFlush);
   if (strcmp(name, "glFlushMappedBufferRange") == 0)
-    return reinterpret_cast<void*>(Mock_glFlushMappedBufferRange);
+    return reinterpret_cast<GLFunctionPointerType>(
+        Mock_glFlushMappedBufferRange);
   if (strcmp(name, "glFramebufferRenderbuffer") == 0)
-    return reinterpret_cast<void*>(Mock_glFramebufferRenderbuffer);
+    return reinterpret_cast<GLFunctionPointerType>(
+        Mock_glFramebufferRenderbuffer);
   if (strcmp(name, "glFramebufferRenderbufferEXT") == 0)
-    return reinterpret_cast<void*>(Mock_glFramebufferRenderbufferEXT);
+    return reinterpret_cast<GLFunctionPointerType>(
+        Mock_glFramebufferRenderbufferEXT);
   if (strcmp(name, "glFramebufferTexture2D") == 0)
-    return reinterpret_cast<void*>(Mock_glFramebufferTexture2D);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glFramebufferTexture2D);
   if (strcmp(name, "glFramebufferTexture2DEXT") == 0)
-    return reinterpret_cast<void*>(Mock_glFramebufferTexture2DEXT);
+    return reinterpret_cast<GLFunctionPointerType>(
+        Mock_glFramebufferTexture2DEXT);
   if (strcmp(name, "glFramebufferTexture2DMultisampleEXT") == 0)
-    return reinterpret_cast<void*>(Mock_glFramebufferTexture2DMultisampleEXT);
+    return reinterpret_cast<GLFunctionPointerType>(
+        Mock_glFramebufferTexture2DMultisampleEXT);
   if (strcmp(name, "glFramebufferTexture2DMultisampleIMG") == 0)
-    return reinterpret_cast<void*>(Mock_glFramebufferTexture2DMultisampleIMG);
+    return reinterpret_cast<GLFunctionPointerType>(
+        Mock_glFramebufferTexture2DMultisampleIMG);
   if (strcmp(name, "glFramebufferTextureLayer") == 0)
-    return reinterpret_cast<void*>(Mock_glFramebufferTextureLayer);
+    return reinterpret_cast<GLFunctionPointerType>(
+        Mock_glFramebufferTextureLayer);
   if (strcmp(name, "glFrontFace") == 0)
-    return reinterpret_cast<void*>(Mock_glFrontFace);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glFrontFace);
   if (strcmp(name, "glGenBuffers") == 0)
-    return reinterpret_cast<void*>(Mock_glGenBuffers);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glGenBuffers);
   if (strcmp(name, "glGenFencesAPPLE") == 0)
-    return reinterpret_cast<void*>(Mock_glGenFencesAPPLE);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glGenFencesAPPLE);
   if (strcmp(name, "glGenFencesNV") == 0)
-    return reinterpret_cast<void*>(Mock_glGenFencesNV);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glGenFencesNV);
   if (strcmp(name, "glGenFramebuffers") == 0)
-    return reinterpret_cast<void*>(Mock_glGenFramebuffers);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glGenFramebuffers);
   if (strcmp(name, "glGenFramebuffersEXT") == 0)
-    return reinterpret_cast<void*>(Mock_glGenFramebuffersEXT);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glGenFramebuffersEXT);
   if (strcmp(name, "glGenPathsNV") == 0)
-    return reinterpret_cast<void*>(Mock_glGenPathsNV);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glGenPathsNV);
   if (strcmp(name, "glGenQueries") == 0)
-    return reinterpret_cast<void*>(Mock_glGenQueries);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glGenQueries);
   if (strcmp(name, "glGenQueriesARB") == 0)
-    return reinterpret_cast<void*>(Mock_glGenQueriesARB);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glGenQueriesARB);
   if (strcmp(name, "glGenQueriesEXT") == 0)
-    return reinterpret_cast<void*>(Mock_glGenQueriesEXT);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glGenQueriesEXT);
   if (strcmp(name, "glGenRenderbuffers") == 0)
-    return reinterpret_cast<void*>(Mock_glGenRenderbuffers);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glGenRenderbuffers);
   if (strcmp(name, "glGenRenderbuffersEXT") == 0)
-    return reinterpret_cast<void*>(Mock_glGenRenderbuffersEXT);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glGenRenderbuffersEXT);
   if (strcmp(name, "glGenSamplers") == 0)
-    return reinterpret_cast<void*>(Mock_glGenSamplers);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glGenSamplers);
   if (strcmp(name, "glGenTextures") == 0)
-    return reinterpret_cast<void*>(Mock_glGenTextures);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glGenTextures);
   if (strcmp(name, "glGenTransformFeedbacks") == 0)
-    return reinterpret_cast<void*>(Mock_glGenTransformFeedbacks);
+    return reinterpret_cast<GLFunctionPointerType>(
+        Mock_glGenTransformFeedbacks);
   if (strcmp(name, "glGenVertexArrays") == 0)
-    return reinterpret_cast<void*>(Mock_glGenVertexArrays);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glGenVertexArrays);
   if (strcmp(name, "glGenVertexArraysAPPLE") == 0)
-    return reinterpret_cast<void*>(Mock_glGenVertexArraysAPPLE);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glGenVertexArraysAPPLE);
   if (strcmp(name, "glGenVertexArraysOES") == 0)
-    return reinterpret_cast<void*>(Mock_glGenVertexArraysOES);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glGenVertexArraysOES);
   if (strcmp(name, "glGenerateMipmap") == 0)
-    return reinterpret_cast<void*>(Mock_glGenerateMipmap);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glGenerateMipmap);
   if (strcmp(name, "glGenerateMipmapEXT") == 0)
-    return reinterpret_cast<void*>(Mock_glGenerateMipmapEXT);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glGenerateMipmapEXT);
   if (strcmp(name, "glGetActiveAttrib") == 0)
-    return reinterpret_cast<void*>(Mock_glGetActiveAttrib);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glGetActiveAttrib);
   if (strcmp(name, "glGetActiveUniform") == 0)
-    return reinterpret_cast<void*>(Mock_glGetActiveUniform);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glGetActiveUniform);
   if (strcmp(name, "glGetActiveUniformBlockName") == 0)
-    return reinterpret_cast<void*>(Mock_glGetActiveUniformBlockName);
+    return reinterpret_cast<GLFunctionPointerType>(
+        Mock_glGetActiveUniformBlockName);
   if (strcmp(name, "glGetActiveUniformBlockiv") == 0)
-    return reinterpret_cast<void*>(Mock_glGetActiveUniformBlockiv);
+    return reinterpret_cast<GLFunctionPointerType>(
+        Mock_glGetActiveUniformBlockiv);
   if (strcmp(name, "glGetActiveUniformBlockivRobustANGLE") == 0)
-    return reinterpret_cast<void*>(Mock_glGetActiveUniformBlockivRobustANGLE);
+    return reinterpret_cast<GLFunctionPointerType>(
+        Mock_glGetActiveUniformBlockivRobustANGLE);
   if (strcmp(name, "glGetActiveUniformsiv") == 0)
-    return reinterpret_cast<void*>(Mock_glGetActiveUniformsiv);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glGetActiveUniformsiv);
   if (strcmp(name, "glGetAttachedShaders") == 0)
-    return reinterpret_cast<void*>(Mock_glGetAttachedShaders);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glGetAttachedShaders);
   if (strcmp(name, "glGetAttribLocation") == 0)
-    return reinterpret_cast<void*>(Mock_glGetAttribLocation);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glGetAttribLocation);
   if (strcmp(name, "glGetBooleani_vRobustANGLE") == 0)
-    return reinterpret_cast<void*>(Mock_glGetBooleani_vRobustANGLE);
+    return reinterpret_cast<GLFunctionPointerType>(
+        Mock_glGetBooleani_vRobustANGLE);
   if (strcmp(name, "glGetBooleanv") == 0)
-    return reinterpret_cast<void*>(Mock_glGetBooleanv);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glGetBooleanv);
   if (strcmp(name, "glGetBooleanvRobustANGLE") == 0)
-    return reinterpret_cast<void*>(Mock_glGetBooleanvRobustANGLE);
+    return reinterpret_cast<GLFunctionPointerType>(
+        Mock_glGetBooleanvRobustANGLE);
   if (strcmp(name, "glGetBufferParameteri64vRobustANGLE") == 0)
-    return reinterpret_cast<void*>(Mock_glGetBufferParameteri64vRobustANGLE);
+    return reinterpret_cast<GLFunctionPointerType>(
+        Mock_glGetBufferParameteri64vRobustANGLE);
   if (strcmp(name, "glGetBufferParameteriv") == 0)
-    return reinterpret_cast<void*>(Mock_glGetBufferParameteriv);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glGetBufferParameteriv);
   if (strcmp(name, "glGetBufferParameterivRobustANGLE") == 0)
-    return reinterpret_cast<void*>(Mock_glGetBufferParameterivRobustANGLE);
+    return reinterpret_cast<GLFunctionPointerType>(
+        Mock_glGetBufferParameterivRobustANGLE);
   if (strcmp(name, "glGetBufferPointervRobustANGLE") == 0)
-    return reinterpret_cast<void*>(Mock_glGetBufferPointervRobustANGLE);
+    return reinterpret_cast<GLFunctionPointerType>(
+        Mock_glGetBufferPointervRobustANGLE);
   if (strcmp(name, "glGetDebugMessageLog") == 0)
-    return reinterpret_cast<void*>(Mock_glGetDebugMessageLog);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glGetDebugMessageLog);
   if (strcmp(name, "glGetDebugMessageLogKHR") == 0)
-    return reinterpret_cast<void*>(Mock_glGetDebugMessageLogKHR);
+    return reinterpret_cast<GLFunctionPointerType>(
+        Mock_glGetDebugMessageLogKHR);
   if (strcmp(name, "glGetError") == 0)
-    return reinterpret_cast<void*>(Mock_glGetError);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glGetError);
   if (strcmp(name, "glGetFenceivNV") == 0)
-    return reinterpret_cast<void*>(Mock_glGetFenceivNV);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glGetFenceivNV);
   if (strcmp(name, "glGetFloatv") == 0)
-    return reinterpret_cast<void*>(Mock_glGetFloatv);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glGetFloatv);
   if (strcmp(name, "glGetFloatvRobustANGLE") == 0)
-    return reinterpret_cast<void*>(Mock_glGetFloatvRobustANGLE);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glGetFloatvRobustANGLE);
   if (strcmp(name, "glGetFragDataIndex") == 0)
-    return reinterpret_cast<void*>(Mock_glGetFragDataIndex);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glGetFragDataIndex);
   if (strcmp(name, "glGetFragDataIndexEXT") == 0)
-    return reinterpret_cast<void*>(Mock_glGetFragDataIndexEXT);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glGetFragDataIndexEXT);
   if (strcmp(name, "glGetFragDataLocation") == 0)
-    return reinterpret_cast<void*>(Mock_glGetFragDataLocation);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glGetFragDataLocation);
   if (strcmp(name, "glGetFramebufferAttachmentParameteriv") == 0)
-    return reinterpret_cast<void*>(Mock_glGetFramebufferAttachmentParameteriv);
+    return reinterpret_cast<GLFunctionPointerType>(
+        Mock_glGetFramebufferAttachmentParameteriv);
   if (strcmp(name, "glGetFramebufferAttachmentParameterivEXT") == 0)
-    return reinterpret_cast<void*>(
+    return reinterpret_cast<GLFunctionPointerType>(
         Mock_glGetFramebufferAttachmentParameterivEXT);
   if (strcmp(name, "glGetFramebufferAttachmentParameterivRobustANGLE") == 0)
-    return reinterpret_cast<void*>(
+    return reinterpret_cast<GLFunctionPointerType>(
         Mock_glGetFramebufferAttachmentParameterivRobustANGLE);
   if (strcmp(name, "glGetFramebufferParameterivRobustANGLE") == 0)
-    return reinterpret_cast<void*>(Mock_glGetFramebufferParameterivRobustANGLE);
+    return reinterpret_cast<GLFunctionPointerType>(
+        Mock_glGetFramebufferParameterivRobustANGLE);
   if (strcmp(name, "glGetGraphicsResetStatus") == 0)
-    return reinterpret_cast<void*>(Mock_glGetGraphicsResetStatus);
+    return reinterpret_cast<GLFunctionPointerType>(
+        Mock_glGetGraphicsResetStatus);
   if (strcmp(name, "glGetGraphicsResetStatusARB") == 0)
-    return reinterpret_cast<void*>(Mock_glGetGraphicsResetStatusARB);
+    return reinterpret_cast<GLFunctionPointerType>(
+        Mock_glGetGraphicsResetStatusARB);
   if (strcmp(name, "glGetGraphicsResetStatusEXT") == 0)
-    return reinterpret_cast<void*>(Mock_glGetGraphicsResetStatusEXT);
+    return reinterpret_cast<GLFunctionPointerType>(
+        Mock_glGetGraphicsResetStatusEXT);
   if (strcmp(name, "glGetGraphicsResetStatusKHR") == 0)
-    return reinterpret_cast<void*>(Mock_glGetGraphicsResetStatusKHR);
+    return reinterpret_cast<GLFunctionPointerType>(
+        Mock_glGetGraphicsResetStatusKHR);
   if (strcmp(name, "glGetInteger64i_v") == 0)
-    return reinterpret_cast<void*>(Mock_glGetInteger64i_v);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glGetInteger64i_v);
   if (strcmp(name, "glGetInteger64i_vRobustANGLE") == 0)
-    return reinterpret_cast<void*>(Mock_glGetInteger64i_vRobustANGLE);
+    return reinterpret_cast<GLFunctionPointerType>(
+        Mock_glGetInteger64i_vRobustANGLE);
   if (strcmp(name, "glGetInteger64v") == 0)
-    return reinterpret_cast<void*>(Mock_glGetInteger64v);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glGetInteger64v);
   if (strcmp(name, "glGetInteger64vRobustANGLE") == 0)
-    return reinterpret_cast<void*>(Mock_glGetInteger64vRobustANGLE);
+    return reinterpret_cast<GLFunctionPointerType>(
+        Mock_glGetInteger64vRobustANGLE);
   if (strcmp(name, "glGetIntegeri_v") == 0)
-    return reinterpret_cast<void*>(Mock_glGetIntegeri_v);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glGetIntegeri_v);
   if (strcmp(name, "glGetIntegeri_vRobustANGLE") == 0)
-    return reinterpret_cast<void*>(Mock_glGetIntegeri_vRobustANGLE);
+    return reinterpret_cast<GLFunctionPointerType>(
+        Mock_glGetIntegeri_vRobustANGLE);
   if (strcmp(name, "glGetIntegerv") == 0)
-    return reinterpret_cast<void*>(Mock_glGetIntegerv);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glGetIntegerv);
   if (strcmp(name, "glGetIntegervRobustANGLE") == 0)
-    return reinterpret_cast<void*>(Mock_glGetIntegervRobustANGLE);
+    return reinterpret_cast<GLFunctionPointerType>(
+        Mock_glGetIntegervRobustANGLE);
   if (strcmp(name, "glGetInternalformativ") == 0)
-    return reinterpret_cast<void*>(Mock_glGetInternalformativ);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glGetInternalformativ);
   if (strcmp(name, "glGetInternalformativRobustANGLE") == 0)
-    return reinterpret_cast<void*>(Mock_glGetInternalformativRobustANGLE);
+    return reinterpret_cast<GLFunctionPointerType>(
+        Mock_glGetInternalformativRobustANGLE);
   if (strcmp(name, "glGetMultisamplefvRobustANGLE") == 0)
-    return reinterpret_cast<void*>(Mock_glGetMultisamplefvRobustANGLE);
+    return reinterpret_cast<GLFunctionPointerType>(
+        Mock_glGetMultisamplefvRobustANGLE);
   if (strcmp(name, "glGetObjectLabel") == 0)
-    return reinterpret_cast<void*>(Mock_glGetObjectLabel);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glGetObjectLabel);
   if (strcmp(name, "glGetObjectLabelKHR") == 0)
-    return reinterpret_cast<void*>(Mock_glGetObjectLabelKHR);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glGetObjectLabelKHR);
   if (strcmp(name, "glGetObjectPtrLabel") == 0)
-    return reinterpret_cast<void*>(Mock_glGetObjectPtrLabel);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glGetObjectPtrLabel);
   if (strcmp(name, "glGetObjectPtrLabelKHR") == 0)
-    return reinterpret_cast<void*>(Mock_glGetObjectPtrLabelKHR);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glGetObjectPtrLabelKHR);
   if (strcmp(name, "glGetPointerv") == 0)
-    return reinterpret_cast<void*>(Mock_glGetPointerv);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glGetPointerv);
   if (strcmp(name, "glGetPointervKHR") == 0)
-    return reinterpret_cast<void*>(Mock_glGetPointervKHR);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glGetPointervKHR);
   if (strcmp(name, "glGetPointervRobustANGLERobustANGLE") == 0)
-    return reinterpret_cast<void*>(Mock_glGetPointervRobustANGLERobustANGLE);
+    return reinterpret_cast<GLFunctionPointerType>(
+        Mock_glGetPointervRobustANGLERobustANGLE);
   if (strcmp(name, "glGetProgramBinary") == 0)
-    return reinterpret_cast<void*>(Mock_glGetProgramBinary);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glGetProgramBinary);
   if (strcmp(name, "glGetProgramBinaryOES") == 0)
-    return reinterpret_cast<void*>(Mock_glGetProgramBinaryOES);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glGetProgramBinaryOES);
   if (strcmp(name, "glGetProgramInfoLog") == 0)
-    return reinterpret_cast<void*>(Mock_glGetProgramInfoLog);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glGetProgramInfoLog);
   if (strcmp(name, "glGetProgramInterfaceiv") == 0)
-    return reinterpret_cast<void*>(Mock_glGetProgramInterfaceiv);
+    return reinterpret_cast<GLFunctionPointerType>(
+        Mock_glGetProgramInterfaceiv);
   if (strcmp(name, "glGetProgramInterfaceivRobustANGLE") == 0)
-    return reinterpret_cast<void*>(Mock_glGetProgramInterfaceivRobustANGLE);
+    return reinterpret_cast<GLFunctionPointerType>(
+        Mock_glGetProgramInterfaceivRobustANGLE);
   if (strcmp(name, "glGetProgramResourceLocation") == 0)
-    return reinterpret_cast<void*>(Mock_glGetProgramResourceLocation);
+    return reinterpret_cast<GLFunctionPointerType>(
+        Mock_glGetProgramResourceLocation);
   if (strcmp(name, "glGetProgramResourceName") == 0)
-    return reinterpret_cast<void*>(Mock_glGetProgramResourceName);
+    return reinterpret_cast<GLFunctionPointerType>(
+        Mock_glGetProgramResourceName);
   if (strcmp(name, "glGetProgramResourceiv") == 0)
-    return reinterpret_cast<void*>(Mock_glGetProgramResourceiv);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glGetProgramResourceiv);
   if (strcmp(name, "glGetProgramiv") == 0)
-    return reinterpret_cast<void*>(Mock_glGetProgramiv);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glGetProgramiv);
   if (strcmp(name, "glGetProgramivRobustANGLE") == 0)
-    return reinterpret_cast<void*>(Mock_glGetProgramivRobustANGLE);
+    return reinterpret_cast<GLFunctionPointerType>(
+        Mock_glGetProgramivRobustANGLE);
   if (strcmp(name, "glGetQueryObjecti64v") == 0)
-    return reinterpret_cast<void*>(Mock_glGetQueryObjecti64v);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glGetQueryObjecti64v);
   if (strcmp(name, "glGetQueryObjecti64vEXT") == 0)
-    return reinterpret_cast<void*>(Mock_glGetQueryObjecti64vEXT);
+    return reinterpret_cast<GLFunctionPointerType>(
+        Mock_glGetQueryObjecti64vEXT);
   if (strcmp(name, "glGetQueryObjecti64vRobustANGLE") == 0)
-    return reinterpret_cast<void*>(Mock_glGetQueryObjecti64vRobustANGLE);
+    return reinterpret_cast<GLFunctionPointerType>(
+        Mock_glGetQueryObjecti64vRobustANGLE);
   if (strcmp(name, "glGetQueryObjectiv") == 0)
-    return reinterpret_cast<void*>(Mock_glGetQueryObjectiv);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glGetQueryObjectiv);
   if (strcmp(name, "glGetQueryObjectivARB") == 0)
-    return reinterpret_cast<void*>(Mock_glGetQueryObjectivARB);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glGetQueryObjectivARB);
   if (strcmp(name, "glGetQueryObjectivEXT") == 0)
-    return reinterpret_cast<void*>(Mock_glGetQueryObjectivEXT);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glGetQueryObjectivEXT);
   if (strcmp(name, "glGetQueryObjectivRobustANGLE") == 0)
-    return reinterpret_cast<void*>(Mock_glGetQueryObjectivRobustANGLE);
+    return reinterpret_cast<GLFunctionPointerType>(
+        Mock_glGetQueryObjectivRobustANGLE);
   if (strcmp(name, "glGetQueryObjectui64v") == 0)
-    return reinterpret_cast<void*>(Mock_glGetQueryObjectui64v);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glGetQueryObjectui64v);
   if (strcmp(name, "glGetQueryObjectui64vEXT") == 0)
-    return reinterpret_cast<void*>(Mock_glGetQueryObjectui64vEXT);
+    return reinterpret_cast<GLFunctionPointerType>(
+        Mock_glGetQueryObjectui64vEXT);
   if (strcmp(name, "glGetQueryObjectui64vRobustANGLE") == 0)
-    return reinterpret_cast<void*>(Mock_glGetQueryObjectui64vRobustANGLE);
+    return reinterpret_cast<GLFunctionPointerType>(
+        Mock_glGetQueryObjectui64vRobustANGLE);
   if (strcmp(name, "glGetQueryObjectuiv") == 0)
-    return reinterpret_cast<void*>(Mock_glGetQueryObjectuiv);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glGetQueryObjectuiv);
   if (strcmp(name, "glGetQueryObjectuivARB") == 0)
-    return reinterpret_cast<void*>(Mock_glGetQueryObjectuivARB);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glGetQueryObjectuivARB);
   if (strcmp(name, "glGetQueryObjectuivEXT") == 0)
-    return reinterpret_cast<void*>(Mock_glGetQueryObjectuivEXT);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glGetQueryObjectuivEXT);
   if (strcmp(name, "glGetQueryObjectuivRobustANGLE") == 0)
-    return reinterpret_cast<void*>(Mock_glGetQueryObjectuivRobustANGLE);
+    return reinterpret_cast<GLFunctionPointerType>(
+        Mock_glGetQueryObjectuivRobustANGLE);
   if (strcmp(name, "glGetQueryiv") == 0)
-    return reinterpret_cast<void*>(Mock_glGetQueryiv);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glGetQueryiv);
   if (strcmp(name, "glGetQueryivARB") == 0)
-    return reinterpret_cast<void*>(Mock_glGetQueryivARB);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glGetQueryivARB);
   if (strcmp(name, "glGetQueryivEXT") == 0)
-    return reinterpret_cast<void*>(Mock_glGetQueryivEXT);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glGetQueryivEXT);
   if (strcmp(name, "glGetQueryivRobustANGLE") == 0)
-    return reinterpret_cast<void*>(Mock_glGetQueryivRobustANGLE);
+    return reinterpret_cast<GLFunctionPointerType>(
+        Mock_glGetQueryivRobustANGLE);
   if (strcmp(name, "glGetRenderbufferParameteriv") == 0)
-    return reinterpret_cast<void*>(Mock_glGetRenderbufferParameteriv);
+    return reinterpret_cast<GLFunctionPointerType>(
+        Mock_glGetRenderbufferParameteriv);
   if (strcmp(name, "glGetRenderbufferParameterivEXT") == 0)
-    return reinterpret_cast<void*>(Mock_glGetRenderbufferParameterivEXT);
+    return reinterpret_cast<GLFunctionPointerType>(
+        Mock_glGetRenderbufferParameterivEXT);
   if (strcmp(name, "glGetRenderbufferParameterivRobustANGLE") == 0)
-    return reinterpret_cast<void*>(
+    return reinterpret_cast<GLFunctionPointerType>(
         Mock_glGetRenderbufferParameterivRobustANGLE);
   if (strcmp(name, "glGetSamplerParameterIivRobustANGLE") == 0)
-    return reinterpret_cast<void*>(Mock_glGetSamplerParameterIivRobustANGLE);
+    return reinterpret_cast<GLFunctionPointerType>(
+        Mock_glGetSamplerParameterIivRobustANGLE);
   if (strcmp(name, "glGetSamplerParameterIuivRobustANGLE") == 0)
-    return reinterpret_cast<void*>(Mock_glGetSamplerParameterIuivRobustANGLE);
+    return reinterpret_cast<GLFunctionPointerType>(
+        Mock_glGetSamplerParameterIuivRobustANGLE);
   if (strcmp(name, "glGetSamplerParameterfv") == 0)
-    return reinterpret_cast<void*>(Mock_glGetSamplerParameterfv);
+    return reinterpret_cast<GLFunctionPointerType>(
+        Mock_glGetSamplerParameterfv);
   if (strcmp(name, "glGetSamplerParameterfvRobustANGLE") == 0)
-    return reinterpret_cast<void*>(Mock_glGetSamplerParameterfvRobustANGLE);
+    return reinterpret_cast<GLFunctionPointerType>(
+        Mock_glGetSamplerParameterfvRobustANGLE);
   if (strcmp(name, "glGetSamplerParameteriv") == 0)
-    return reinterpret_cast<void*>(Mock_glGetSamplerParameteriv);
+    return reinterpret_cast<GLFunctionPointerType>(
+        Mock_glGetSamplerParameteriv);
   if (strcmp(name, "glGetSamplerParameterivRobustANGLE") == 0)
-    return reinterpret_cast<void*>(Mock_glGetSamplerParameterivRobustANGLE);
+    return reinterpret_cast<GLFunctionPointerType>(
+        Mock_glGetSamplerParameterivRobustANGLE);
   if (strcmp(name, "glGetShaderInfoLog") == 0)
-    return reinterpret_cast<void*>(Mock_glGetShaderInfoLog);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glGetShaderInfoLog);
   if (strcmp(name, "glGetShaderPrecisionFormat") == 0)
-    return reinterpret_cast<void*>(Mock_glGetShaderPrecisionFormat);
+    return reinterpret_cast<GLFunctionPointerType>(
+        Mock_glGetShaderPrecisionFormat);
   if (strcmp(name, "glGetShaderSource") == 0)
-    return reinterpret_cast<void*>(Mock_glGetShaderSource);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glGetShaderSource);
   if (strcmp(name, "glGetShaderiv") == 0)
-    return reinterpret_cast<void*>(Mock_glGetShaderiv);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glGetShaderiv);
   if (strcmp(name, "glGetShaderivRobustANGLE") == 0)
-    return reinterpret_cast<void*>(Mock_glGetShaderivRobustANGLE);
+    return reinterpret_cast<GLFunctionPointerType>(
+        Mock_glGetShaderivRobustANGLE);
   if (strcmp(name, "glGetString") == 0)
-    return reinterpret_cast<void*>(Mock_glGetString);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glGetString);
   if (strcmp(name, "glGetStringi") == 0)
-    return reinterpret_cast<void*>(Mock_glGetStringi);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glGetStringi);
   if (strcmp(name, "glGetSynciv") == 0)
-    return reinterpret_cast<void*>(Mock_glGetSynciv);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glGetSynciv);
   if (strcmp(name, "glGetTexLevelParameterfv") == 0)
-    return reinterpret_cast<void*>(Mock_glGetTexLevelParameterfv);
+    return reinterpret_cast<GLFunctionPointerType>(
+        Mock_glGetTexLevelParameterfv);
   if (strcmp(name, "glGetTexLevelParameterfvRobustANGLE") == 0)
-    return reinterpret_cast<void*>(Mock_glGetTexLevelParameterfvRobustANGLE);
+    return reinterpret_cast<GLFunctionPointerType>(
+        Mock_glGetTexLevelParameterfvRobustANGLE);
   if (strcmp(name, "glGetTexLevelParameteriv") == 0)
-    return reinterpret_cast<void*>(Mock_glGetTexLevelParameteriv);
+    return reinterpret_cast<GLFunctionPointerType>(
+        Mock_glGetTexLevelParameteriv);
   if (strcmp(name, "glGetTexLevelParameterivRobustANGLE") == 0)
-    return reinterpret_cast<void*>(Mock_glGetTexLevelParameterivRobustANGLE);
+    return reinterpret_cast<GLFunctionPointerType>(
+        Mock_glGetTexLevelParameterivRobustANGLE);
   if (strcmp(name, "glGetTexParameterIivRobustANGLE") == 0)
-    return reinterpret_cast<void*>(Mock_glGetTexParameterIivRobustANGLE);
+    return reinterpret_cast<GLFunctionPointerType>(
+        Mock_glGetTexParameterIivRobustANGLE);
   if (strcmp(name, "glGetTexParameterIuivRobustANGLE") == 0)
-    return reinterpret_cast<void*>(Mock_glGetTexParameterIuivRobustANGLE);
+    return reinterpret_cast<GLFunctionPointerType>(
+        Mock_glGetTexParameterIuivRobustANGLE);
   if (strcmp(name, "glGetTexParameterfv") == 0)
-    return reinterpret_cast<void*>(Mock_glGetTexParameterfv);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glGetTexParameterfv);
   if (strcmp(name, "glGetTexParameterfvRobustANGLE") == 0)
-    return reinterpret_cast<void*>(Mock_glGetTexParameterfvRobustANGLE);
+    return reinterpret_cast<GLFunctionPointerType>(
+        Mock_glGetTexParameterfvRobustANGLE);
   if (strcmp(name, "glGetTexParameteriv") == 0)
-    return reinterpret_cast<void*>(Mock_glGetTexParameteriv);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glGetTexParameteriv);
   if (strcmp(name, "glGetTexParameterivRobustANGLE") == 0)
-    return reinterpret_cast<void*>(Mock_glGetTexParameterivRobustANGLE);
+    return reinterpret_cast<GLFunctionPointerType>(
+        Mock_glGetTexParameterivRobustANGLE);
   if (strcmp(name, "glGetTransformFeedbackVarying") == 0)
-    return reinterpret_cast<void*>(Mock_glGetTransformFeedbackVarying);
+    return reinterpret_cast<GLFunctionPointerType>(
+        Mock_glGetTransformFeedbackVarying);
   if (strcmp(name, "glGetTransformFeedbackVaryingEXT") == 0)
-    return reinterpret_cast<void*>(Mock_glGetTransformFeedbackVaryingEXT);
+    return reinterpret_cast<GLFunctionPointerType>(
+        Mock_glGetTransformFeedbackVaryingEXT);
   if (strcmp(name, "glGetTranslatedShaderSourceANGLE") == 0)
-    return reinterpret_cast<void*>(Mock_glGetTranslatedShaderSourceANGLE);
+    return reinterpret_cast<GLFunctionPointerType>(
+        Mock_glGetTranslatedShaderSourceANGLE);
   if (strcmp(name, "glGetUniformBlockIndex") == 0)
-    return reinterpret_cast<void*>(Mock_glGetUniformBlockIndex);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glGetUniformBlockIndex);
   if (strcmp(name, "glGetUniformIndices") == 0)
-    return reinterpret_cast<void*>(Mock_glGetUniformIndices);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glGetUniformIndices);
   if (strcmp(name, "glGetUniformLocation") == 0)
-    return reinterpret_cast<void*>(Mock_glGetUniformLocation);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glGetUniformLocation);
   if (strcmp(name, "glGetUniformfv") == 0)
-    return reinterpret_cast<void*>(Mock_glGetUniformfv);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glGetUniformfv);
   if (strcmp(name, "glGetUniformfvRobustANGLE") == 0)
-    return reinterpret_cast<void*>(Mock_glGetUniformfvRobustANGLE);
+    return reinterpret_cast<GLFunctionPointerType>(
+        Mock_glGetUniformfvRobustANGLE);
   if (strcmp(name, "glGetUniformiv") == 0)
-    return reinterpret_cast<void*>(Mock_glGetUniformiv);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glGetUniformiv);
   if (strcmp(name, "glGetUniformivRobustANGLE") == 0)
-    return reinterpret_cast<void*>(Mock_glGetUniformivRobustANGLE);
+    return reinterpret_cast<GLFunctionPointerType>(
+        Mock_glGetUniformivRobustANGLE);
   if (strcmp(name, "glGetUniformuiv") == 0)
-    return reinterpret_cast<void*>(Mock_glGetUniformuiv);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glGetUniformuiv);
   if (strcmp(name, "glGetUniformuivRobustANGLE") == 0)
-    return reinterpret_cast<void*>(Mock_glGetUniformuivRobustANGLE);
+    return reinterpret_cast<GLFunctionPointerType>(
+        Mock_glGetUniformuivRobustANGLE);
   if (strcmp(name, "glGetVertexAttribIivRobustANGLE") == 0)
-    return reinterpret_cast<void*>(Mock_glGetVertexAttribIivRobustANGLE);
+    return reinterpret_cast<GLFunctionPointerType>(
+        Mock_glGetVertexAttribIivRobustANGLE);
   if (strcmp(name, "glGetVertexAttribIuivRobustANGLE") == 0)
-    return reinterpret_cast<void*>(Mock_glGetVertexAttribIuivRobustANGLE);
+    return reinterpret_cast<GLFunctionPointerType>(
+        Mock_glGetVertexAttribIuivRobustANGLE);
   if (strcmp(name, "glGetVertexAttribPointerv") == 0)
-    return reinterpret_cast<void*>(Mock_glGetVertexAttribPointerv);
+    return reinterpret_cast<GLFunctionPointerType>(
+        Mock_glGetVertexAttribPointerv);
   if (strcmp(name, "glGetVertexAttribPointervRobustANGLE") == 0)
-    return reinterpret_cast<void*>(Mock_glGetVertexAttribPointervRobustANGLE);
+    return reinterpret_cast<GLFunctionPointerType>(
+        Mock_glGetVertexAttribPointervRobustANGLE);
   if (strcmp(name, "glGetVertexAttribfv") == 0)
-    return reinterpret_cast<void*>(Mock_glGetVertexAttribfv);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glGetVertexAttribfv);
   if (strcmp(name, "glGetVertexAttribfvRobustANGLE") == 0)
-    return reinterpret_cast<void*>(Mock_glGetVertexAttribfvRobustANGLE);
+    return reinterpret_cast<GLFunctionPointerType>(
+        Mock_glGetVertexAttribfvRobustANGLE);
   if (strcmp(name, "glGetVertexAttribiv") == 0)
-    return reinterpret_cast<void*>(Mock_glGetVertexAttribiv);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glGetVertexAttribiv);
   if (strcmp(name, "glGetVertexAttribivRobustANGLE") == 0)
-    return reinterpret_cast<void*>(Mock_glGetVertexAttribivRobustANGLE);
+    return reinterpret_cast<GLFunctionPointerType>(
+        Mock_glGetVertexAttribivRobustANGLE);
   if (strcmp(name, "glGetnUniformfvRobustANGLE") == 0)
-    return reinterpret_cast<void*>(Mock_glGetnUniformfvRobustANGLE);
+    return reinterpret_cast<GLFunctionPointerType>(
+        Mock_glGetnUniformfvRobustANGLE);
   if (strcmp(name, "glGetnUniformivRobustANGLE") == 0)
-    return reinterpret_cast<void*>(Mock_glGetnUniformivRobustANGLE);
+    return reinterpret_cast<GLFunctionPointerType>(
+        Mock_glGetnUniformivRobustANGLE);
   if (strcmp(name, "glGetnUniformuivRobustANGLE") == 0)
-    return reinterpret_cast<void*>(Mock_glGetnUniformuivRobustANGLE);
+    return reinterpret_cast<GLFunctionPointerType>(
+        Mock_glGetnUniformuivRobustANGLE);
   if (strcmp(name, "glHint") == 0)
-    return reinterpret_cast<void*>(Mock_glHint);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glHint);
   if (strcmp(name, "glInsertEventMarkerEXT") == 0)
-    return reinterpret_cast<void*>(Mock_glInsertEventMarkerEXT);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glInsertEventMarkerEXT);
   if (strcmp(name, "glInvalidateFramebuffer") == 0)
-    return reinterpret_cast<void*>(Mock_glInvalidateFramebuffer);
+    return reinterpret_cast<GLFunctionPointerType>(
+        Mock_glInvalidateFramebuffer);
   if (strcmp(name, "glInvalidateSubFramebuffer") == 0)
-    return reinterpret_cast<void*>(Mock_glInvalidateSubFramebuffer);
+    return reinterpret_cast<GLFunctionPointerType>(
+        Mock_glInvalidateSubFramebuffer);
   if (strcmp(name, "glIsBuffer") == 0)
-    return reinterpret_cast<void*>(Mock_glIsBuffer);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glIsBuffer);
   if (strcmp(name, "glIsEnabled") == 0)
-    return reinterpret_cast<void*>(Mock_glIsEnabled);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glIsEnabled);
   if (strcmp(name, "glIsFenceAPPLE") == 0)
-    return reinterpret_cast<void*>(Mock_glIsFenceAPPLE);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glIsFenceAPPLE);
   if (strcmp(name, "glIsFenceNV") == 0)
-    return reinterpret_cast<void*>(Mock_glIsFenceNV);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glIsFenceNV);
   if (strcmp(name, "glIsFramebuffer") == 0)
-    return reinterpret_cast<void*>(Mock_glIsFramebuffer);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glIsFramebuffer);
   if (strcmp(name, "glIsFramebufferEXT") == 0)
-    return reinterpret_cast<void*>(Mock_glIsFramebufferEXT);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glIsFramebufferEXT);
   if (strcmp(name, "glIsPathNV") == 0)
-    return reinterpret_cast<void*>(Mock_glIsPathNV);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glIsPathNV);
   if (strcmp(name, "glIsProgram") == 0)
-    return reinterpret_cast<void*>(Mock_glIsProgram);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glIsProgram);
   if (strcmp(name, "glIsQuery") == 0)
-    return reinterpret_cast<void*>(Mock_glIsQuery);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glIsQuery);
   if (strcmp(name, "glIsQueryARB") == 0)
-    return reinterpret_cast<void*>(Mock_glIsQueryARB);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glIsQueryARB);
   if (strcmp(name, "glIsQueryEXT") == 0)
-    return reinterpret_cast<void*>(Mock_glIsQueryEXT);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glIsQueryEXT);
   if (strcmp(name, "glIsRenderbuffer") == 0)
-    return reinterpret_cast<void*>(Mock_glIsRenderbuffer);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glIsRenderbuffer);
   if (strcmp(name, "glIsRenderbufferEXT") == 0)
-    return reinterpret_cast<void*>(Mock_glIsRenderbufferEXT);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glIsRenderbufferEXT);
   if (strcmp(name, "glIsSampler") == 0)
-    return reinterpret_cast<void*>(Mock_glIsSampler);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glIsSampler);
   if (strcmp(name, "glIsShader") == 0)
-    return reinterpret_cast<void*>(Mock_glIsShader);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glIsShader);
   if (strcmp(name, "glIsSync") == 0)
-    return reinterpret_cast<void*>(Mock_glIsSync);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glIsSync);
   if (strcmp(name, "glIsTexture") == 0)
-    return reinterpret_cast<void*>(Mock_glIsTexture);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glIsTexture);
   if (strcmp(name, "glIsTransformFeedback") == 0)
-    return reinterpret_cast<void*>(Mock_glIsTransformFeedback);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glIsTransformFeedback);
   if (strcmp(name, "glIsVertexArray") == 0)
-    return reinterpret_cast<void*>(Mock_glIsVertexArray);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glIsVertexArray);
   if (strcmp(name, "glIsVertexArrayAPPLE") == 0)
-    return reinterpret_cast<void*>(Mock_glIsVertexArrayAPPLE);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glIsVertexArrayAPPLE);
   if (strcmp(name, "glIsVertexArrayOES") == 0)
-    return reinterpret_cast<void*>(Mock_glIsVertexArrayOES);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glIsVertexArrayOES);
   if (strcmp(name, "glLineWidth") == 0)
-    return reinterpret_cast<void*>(Mock_glLineWidth);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glLineWidth);
   if (strcmp(name, "glLinkProgram") == 0)
-    return reinterpret_cast<void*>(Mock_glLinkProgram);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glLinkProgram);
   if (strcmp(name, "glMapBuffer") == 0)
-    return reinterpret_cast<void*>(Mock_glMapBuffer);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glMapBuffer);
   if (strcmp(name, "glMapBufferOES") == 0)
-    return reinterpret_cast<void*>(Mock_glMapBufferOES);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glMapBufferOES);
   if (strcmp(name, "glMapBufferRange") == 0)
-    return reinterpret_cast<void*>(Mock_glMapBufferRange);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glMapBufferRange);
   if (strcmp(name, "glMapBufferRangeEXT") == 0)
-    return reinterpret_cast<void*>(Mock_glMapBufferRangeEXT);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glMapBufferRangeEXT);
   if (strcmp(name, "glMatrixLoadIdentityEXT") == 0)
-    return reinterpret_cast<void*>(Mock_glMatrixLoadIdentityEXT);
+    return reinterpret_cast<GLFunctionPointerType>(
+        Mock_glMatrixLoadIdentityEXT);
   if (strcmp(name, "glMatrixLoadfEXT") == 0)
-    return reinterpret_cast<void*>(Mock_glMatrixLoadfEXT);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glMatrixLoadfEXT);
   if (strcmp(name, "glMemoryBarrier") == 0)
-    return reinterpret_cast<void*>(Mock_glMemoryBarrier);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glMemoryBarrier);
   if (strcmp(name, "glMemoryBarrierEXT") == 0)
-    return reinterpret_cast<void*>(Mock_glMemoryBarrierEXT);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glMemoryBarrierEXT);
   if (strcmp(name, "glObjectLabel") == 0)
-    return reinterpret_cast<void*>(Mock_glObjectLabel);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glObjectLabel);
   if (strcmp(name, "glObjectLabelKHR") == 0)
-    return reinterpret_cast<void*>(Mock_glObjectLabelKHR);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glObjectLabelKHR);
   if (strcmp(name, "glObjectPtrLabel") == 0)
-    return reinterpret_cast<void*>(Mock_glObjectPtrLabel);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glObjectPtrLabel);
   if (strcmp(name, "glObjectPtrLabelKHR") == 0)
-    return reinterpret_cast<void*>(Mock_glObjectPtrLabelKHR);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glObjectPtrLabelKHR);
   if (strcmp(name, "glPathCommandsNV") == 0)
-    return reinterpret_cast<void*>(Mock_glPathCommandsNV);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glPathCommandsNV);
   if (strcmp(name, "glPathParameterfNV") == 0)
-    return reinterpret_cast<void*>(Mock_glPathParameterfNV);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glPathParameterfNV);
   if (strcmp(name, "glPathParameteriNV") == 0)
-    return reinterpret_cast<void*>(Mock_glPathParameteriNV);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glPathParameteriNV);
   if (strcmp(name, "glPathStencilFuncNV") == 0)
-    return reinterpret_cast<void*>(Mock_glPathStencilFuncNV);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glPathStencilFuncNV);
   if (strcmp(name, "glPauseTransformFeedback") == 0)
-    return reinterpret_cast<void*>(Mock_glPauseTransformFeedback);
+    return reinterpret_cast<GLFunctionPointerType>(
+        Mock_glPauseTransformFeedback);
   if (strcmp(name, "glPixelStorei") == 0)
-    return reinterpret_cast<void*>(Mock_glPixelStorei);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glPixelStorei);
   if (strcmp(name, "glPointParameteri") == 0)
-    return reinterpret_cast<void*>(Mock_glPointParameteri);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glPointParameteri);
   if (strcmp(name, "glPolygonOffset") == 0)
-    return reinterpret_cast<void*>(Mock_glPolygonOffset);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glPolygonOffset);
   if (strcmp(name, "glPopDebugGroup") == 0)
-    return reinterpret_cast<void*>(Mock_glPopDebugGroup);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glPopDebugGroup);
   if (strcmp(name, "glPopDebugGroupKHR") == 0)
-    return reinterpret_cast<void*>(Mock_glPopDebugGroupKHR);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glPopDebugGroupKHR);
   if (strcmp(name, "glPopGroupMarkerEXT") == 0)
-    return reinterpret_cast<void*>(Mock_glPopGroupMarkerEXT);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glPopGroupMarkerEXT);
   if (strcmp(name, "glPrimitiveRestartIndex") == 0)
-    return reinterpret_cast<void*>(Mock_glPrimitiveRestartIndex);
+    return reinterpret_cast<GLFunctionPointerType>(
+        Mock_glPrimitiveRestartIndex);
   if (strcmp(name, "glProgramBinary") == 0)
-    return reinterpret_cast<void*>(Mock_glProgramBinary);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glProgramBinary);
   if (strcmp(name, "glProgramBinaryOES") == 0)
-    return reinterpret_cast<void*>(Mock_glProgramBinaryOES);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glProgramBinaryOES);
   if (strcmp(name, "glProgramParameteri") == 0)
-    return reinterpret_cast<void*>(Mock_glProgramParameteri);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glProgramParameteri);
   if (strcmp(name, "glProgramPathFragmentInputGenNV") == 0)
-    return reinterpret_cast<void*>(Mock_glProgramPathFragmentInputGenNV);
+    return reinterpret_cast<GLFunctionPointerType>(
+        Mock_glProgramPathFragmentInputGenNV);
   if (strcmp(name, "glPushDebugGroup") == 0)
-    return reinterpret_cast<void*>(Mock_glPushDebugGroup);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glPushDebugGroup);
   if (strcmp(name, "glPushDebugGroupKHR") == 0)
-    return reinterpret_cast<void*>(Mock_glPushDebugGroupKHR);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glPushDebugGroupKHR);
   if (strcmp(name, "glPushGroupMarkerEXT") == 0)
-    return reinterpret_cast<void*>(Mock_glPushGroupMarkerEXT);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glPushGroupMarkerEXT);
   if (strcmp(name, "glQueryCounter") == 0)
-    return reinterpret_cast<void*>(Mock_glQueryCounter);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glQueryCounter);
   if (strcmp(name, "glQueryCounterEXT") == 0)
-    return reinterpret_cast<void*>(Mock_glQueryCounterEXT);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glQueryCounterEXT);
   if (strcmp(name, "glReadBuffer") == 0)
-    return reinterpret_cast<void*>(Mock_glReadBuffer);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glReadBuffer);
   if (strcmp(name, "glReadPixels") == 0)
-    return reinterpret_cast<void*>(Mock_glReadPixels);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glReadPixels);
   if (strcmp(name, "glReadPixelsRobustANGLE") == 0)
-    return reinterpret_cast<void*>(Mock_glReadPixelsRobustANGLE);
+    return reinterpret_cast<GLFunctionPointerType>(
+        Mock_glReadPixelsRobustANGLE);
   if (strcmp(name, "glReadnPixelsRobustANGLE") == 0)
-    return reinterpret_cast<void*>(Mock_glReadnPixelsRobustANGLE);
+    return reinterpret_cast<GLFunctionPointerType>(
+        Mock_glReadnPixelsRobustANGLE);
   if (strcmp(name, "glReleaseShaderCompiler") == 0)
-    return reinterpret_cast<void*>(Mock_glReleaseShaderCompiler);
+    return reinterpret_cast<GLFunctionPointerType>(
+        Mock_glReleaseShaderCompiler);
   if (strcmp(name, "glRenderbufferStorage") == 0)
-    return reinterpret_cast<void*>(Mock_glRenderbufferStorage);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glRenderbufferStorage);
   if (strcmp(name, "glRenderbufferStorageEXT") == 0)
-    return reinterpret_cast<void*>(Mock_glRenderbufferStorageEXT);
+    return reinterpret_cast<GLFunctionPointerType>(
+        Mock_glRenderbufferStorageEXT);
   if (strcmp(name, "glRenderbufferStorageMultisample") == 0)
-    return reinterpret_cast<void*>(Mock_glRenderbufferStorageMultisample);
+    return reinterpret_cast<GLFunctionPointerType>(
+        Mock_glRenderbufferStorageMultisample);
   if (strcmp(name, "glRenderbufferStorageMultisampleANGLE") == 0)
-    return reinterpret_cast<void*>(Mock_glRenderbufferStorageMultisampleANGLE);
+    return reinterpret_cast<GLFunctionPointerType>(
+        Mock_glRenderbufferStorageMultisampleANGLE);
   if (strcmp(name, "glRenderbufferStorageMultisampleEXT") == 0)
-    return reinterpret_cast<void*>(Mock_glRenderbufferStorageMultisampleEXT);
+    return reinterpret_cast<GLFunctionPointerType>(
+        Mock_glRenderbufferStorageMultisampleEXT);
   if (strcmp(name, "glRenderbufferStorageMultisampleIMG") == 0)
-    return reinterpret_cast<void*>(Mock_glRenderbufferStorageMultisampleIMG);
+    return reinterpret_cast<GLFunctionPointerType>(
+        Mock_glRenderbufferStorageMultisampleIMG);
   if (strcmp(name, "glResumeTransformFeedback") == 0)
-    return reinterpret_cast<void*>(Mock_glResumeTransformFeedback);
+    return reinterpret_cast<GLFunctionPointerType>(
+        Mock_glResumeTransformFeedback);
   if (strcmp(name, "glSampleCoverage") == 0)
-    return reinterpret_cast<void*>(Mock_glSampleCoverage);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glSampleCoverage);
   if (strcmp(name, "glSamplerParameterIivRobustANGLE") == 0)
-    return reinterpret_cast<void*>(Mock_glSamplerParameterIivRobustANGLE);
+    return reinterpret_cast<GLFunctionPointerType>(
+        Mock_glSamplerParameterIivRobustANGLE);
   if (strcmp(name, "glSamplerParameterIuivRobustANGLE") == 0)
-    return reinterpret_cast<void*>(Mock_glSamplerParameterIuivRobustANGLE);
+    return reinterpret_cast<GLFunctionPointerType>(
+        Mock_glSamplerParameterIuivRobustANGLE);
   if (strcmp(name, "glSamplerParameterf") == 0)
-    return reinterpret_cast<void*>(Mock_glSamplerParameterf);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glSamplerParameterf);
   if (strcmp(name, "glSamplerParameterfv") == 0)
-    return reinterpret_cast<void*>(Mock_glSamplerParameterfv);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glSamplerParameterfv);
   if (strcmp(name, "glSamplerParameterfvRobustANGLE") == 0)
-    return reinterpret_cast<void*>(Mock_glSamplerParameterfvRobustANGLE);
+    return reinterpret_cast<GLFunctionPointerType>(
+        Mock_glSamplerParameterfvRobustANGLE);
   if (strcmp(name, "glSamplerParameteri") == 0)
-    return reinterpret_cast<void*>(Mock_glSamplerParameteri);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glSamplerParameteri);
   if (strcmp(name, "glSamplerParameteriv") == 0)
-    return reinterpret_cast<void*>(Mock_glSamplerParameteriv);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glSamplerParameteriv);
   if (strcmp(name, "glSamplerParameterivRobustANGLE") == 0)
-    return reinterpret_cast<void*>(Mock_glSamplerParameterivRobustANGLE);
+    return reinterpret_cast<GLFunctionPointerType>(
+        Mock_glSamplerParameterivRobustANGLE);
   if (strcmp(name, "glScissor") == 0)
-    return reinterpret_cast<void*>(Mock_glScissor);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glScissor);
   if (strcmp(name, "glSetFenceAPPLE") == 0)
-    return reinterpret_cast<void*>(Mock_glSetFenceAPPLE);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glSetFenceAPPLE);
   if (strcmp(name, "glSetFenceNV") == 0)
-    return reinterpret_cast<void*>(Mock_glSetFenceNV);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glSetFenceNV);
   if (strcmp(name, "glShaderBinary") == 0)
-    return reinterpret_cast<void*>(Mock_glShaderBinary);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glShaderBinary);
   if (strcmp(name, "glShaderSource") == 0)
-    return reinterpret_cast<void*>(Mock_glShaderSource);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glShaderSource);
   if (strcmp(name, "glStencilFillPathInstancedNV") == 0)
-    return reinterpret_cast<void*>(Mock_glStencilFillPathInstancedNV);
+    return reinterpret_cast<GLFunctionPointerType>(
+        Mock_glStencilFillPathInstancedNV);
   if (strcmp(name, "glStencilFillPathNV") == 0)
-    return reinterpret_cast<void*>(Mock_glStencilFillPathNV);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glStencilFillPathNV);
   if (strcmp(name, "glStencilFunc") == 0)
-    return reinterpret_cast<void*>(Mock_glStencilFunc);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glStencilFunc);
   if (strcmp(name, "glStencilFuncSeparate") == 0)
-    return reinterpret_cast<void*>(Mock_glStencilFuncSeparate);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glStencilFuncSeparate);
   if (strcmp(name, "glStencilMask") == 0)
-    return reinterpret_cast<void*>(Mock_glStencilMask);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glStencilMask);
   if (strcmp(name, "glStencilMaskSeparate") == 0)
-    return reinterpret_cast<void*>(Mock_glStencilMaskSeparate);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glStencilMaskSeparate);
   if (strcmp(name, "glStencilOp") == 0)
-    return reinterpret_cast<void*>(Mock_glStencilOp);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glStencilOp);
   if (strcmp(name, "glStencilOpSeparate") == 0)
-    return reinterpret_cast<void*>(Mock_glStencilOpSeparate);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glStencilOpSeparate);
   if (strcmp(name, "glStencilStrokePathInstancedNV") == 0)
-    return reinterpret_cast<void*>(Mock_glStencilStrokePathInstancedNV);
+    return reinterpret_cast<GLFunctionPointerType>(
+        Mock_glStencilStrokePathInstancedNV);
   if (strcmp(name, "glStencilStrokePathNV") == 0)
-    return reinterpret_cast<void*>(Mock_glStencilStrokePathNV);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glStencilStrokePathNV);
   if (strcmp(name, "glStencilThenCoverFillPathInstancedNV") == 0)
-    return reinterpret_cast<void*>(Mock_glStencilThenCoverFillPathInstancedNV);
+    return reinterpret_cast<GLFunctionPointerType>(
+        Mock_glStencilThenCoverFillPathInstancedNV);
   if (strcmp(name, "glStencilThenCoverFillPathNV") == 0)
-    return reinterpret_cast<void*>(Mock_glStencilThenCoverFillPathNV);
+    return reinterpret_cast<GLFunctionPointerType>(
+        Mock_glStencilThenCoverFillPathNV);
   if (strcmp(name, "glStencilThenCoverStrokePathInstancedNV") == 0)
-    return reinterpret_cast<void*>(
+    return reinterpret_cast<GLFunctionPointerType>(
         Mock_glStencilThenCoverStrokePathInstancedNV);
   if (strcmp(name, "glStencilThenCoverStrokePathNV") == 0)
-    return reinterpret_cast<void*>(Mock_glStencilThenCoverStrokePathNV);
+    return reinterpret_cast<GLFunctionPointerType>(
+        Mock_glStencilThenCoverStrokePathNV);
   if (strcmp(name, "glTestFenceAPPLE") == 0)
-    return reinterpret_cast<void*>(Mock_glTestFenceAPPLE);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glTestFenceAPPLE);
   if (strcmp(name, "glTestFenceNV") == 0)
-    return reinterpret_cast<void*>(Mock_glTestFenceNV);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glTestFenceNV);
   if (strcmp(name, "glTexImage2D") == 0)
-    return reinterpret_cast<void*>(Mock_glTexImage2D);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glTexImage2D);
   if (strcmp(name, "glTexImage2DRobustANGLE") == 0)
-    return reinterpret_cast<void*>(Mock_glTexImage2DRobustANGLE);
+    return reinterpret_cast<GLFunctionPointerType>(
+        Mock_glTexImage2DRobustANGLE);
   if (strcmp(name, "glTexImage3D") == 0)
-    return reinterpret_cast<void*>(Mock_glTexImage3D);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glTexImage3D);
   if (strcmp(name, "glTexImage3DRobustANGLE") == 0)
-    return reinterpret_cast<void*>(Mock_glTexImage3DRobustANGLE);
+    return reinterpret_cast<GLFunctionPointerType>(
+        Mock_glTexImage3DRobustANGLE);
   if (strcmp(name, "glTexParameterIivRobustANGLE") == 0)
-    return reinterpret_cast<void*>(Mock_glTexParameterIivRobustANGLE);
+    return reinterpret_cast<GLFunctionPointerType>(
+        Mock_glTexParameterIivRobustANGLE);
   if (strcmp(name, "glTexParameterIuivRobustANGLE") == 0)
-    return reinterpret_cast<void*>(Mock_glTexParameterIuivRobustANGLE);
+    return reinterpret_cast<GLFunctionPointerType>(
+        Mock_glTexParameterIuivRobustANGLE);
   if (strcmp(name, "glTexParameterf") == 0)
-    return reinterpret_cast<void*>(Mock_glTexParameterf);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glTexParameterf);
   if (strcmp(name, "glTexParameterfv") == 0)
-    return reinterpret_cast<void*>(Mock_glTexParameterfv);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glTexParameterfv);
   if (strcmp(name, "glTexParameterfvRobustANGLE") == 0)
-    return reinterpret_cast<void*>(Mock_glTexParameterfvRobustANGLE);
+    return reinterpret_cast<GLFunctionPointerType>(
+        Mock_glTexParameterfvRobustANGLE);
   if (strcmp(name, "glTexParameteri") == 0)
-    return reinterpret_cast<void*>(Mock_glTexParameteri);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glTexParameteri);
   if (strcmp(name, "glTexParameteriv") == 0)
-    return reinterpret_cast<void*>(Mock_glTexParameteriv);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glTexParameteriv);
   if (strcmp(name, "glTexParameterivRobustANGLE") == 0)
-    return reinterpret_cast<void*>(Mock_glTexParameterivRobustANGLE);
+    return reinterpret_cast<GLFunctionPointerType>(
+        Mock_glTexParameterivRobustANGLE);
   if (strcmp(name, "glTexStorage2D") == 0)
-    return reinterpret_cast<void*>(Mock_glTexStorage2D);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glTexStorage2D);
   if (strcmp(name, "glTexStorage2DEXT") == 0)
-    return reinterpret_cast<void*>(Mock_glTexStorage2DEXT);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glTexStorage2DEXT);
   if (strcmp(name, "glTexStorage3D") == 0)
-    return reinterpret_cast<void*>(Mock_glTexStorage3D);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glTexStorage3D);
   if (strcmp(name, "glTexSubImage2D") == 0)
-    return reinterpret_cast<void*>(Mock_glTexSubImage2D);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glTexSubImage2D);
   if (strcmp(name, "glTexSubImage2DRobustANGLE") == 0)
-    return reinterpret_cast<void*>(Mock_glTexSubImage2DRobustANGLE);
+    return reinterpret_cast<GLFunctionPointerType>(
+        Mock_glTexSubImage2DRobustANGLE);
   if (strcmp(name, "glTexSubImage3D") == 0)
-    return reinterpret_cast<void*>(Mock_glTexSubImage3D);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glTexSubImage3D);
   if (strcmp(name, "glTexSubImage3DRobustANGLE") == 0)
-    return reinterpret_cast<void*>(Mock_glTexSubImage3DRobustANGLE);
+    return reinterpret_cast<GLFunctionPointerType>(
+        Mock_glTexSubImage3DRobustANGLE);
   if (strcmp(name, "glTransformFeedbackVaryings") == 0)
-    return reinterpret_cast<void*>(Mock_glTransformFeedbackVaryings);
+    return reinterpret_cast<GLFunctionPointerType>(
+        Mock_glTransformFeedbackVaryings);
   if (strcmp(name, "glTransformFeedbackVaryingsEXT") == 0)
-    return reinterpret_cast<void*>(Mock_glTransformFeedbackVaryingsEXT);
+    return reinterpret_cast<GLFunctionPointerType>(
+        Mock_glTransformFeedbackVaryingsEXT);
   if (strcmp(name, "glUniform1f") == 0)
-    return reinterpret_cast<void*>(Mock_glUniform1f);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glUniform1f);
   if (strcmp(name, "glUniform1fv") == 0)
-    return reinterpret_cast<void*>(Mock_glUniform1fv);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glUniform1fv);
   if (strcmp(name, "glUniform1i") == 0)
-    return reinterpret_cast<void*>(Mock_glUniform1i);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glUniform1i);
   if (strcmp(name, "glUniform1iv") == 0)
-    return reinterpret_cast<void*>(Mock_glUniform1iv);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glUniform1iv);
   if (strcmp(name, "glUniform1ui") == 0)
-    return reinterpret_cast<void*>(Mock_glUniform1ui);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glUniform1ui);
   if (strcmp(name, "glUniform1uiv") == 0)
-    return reinterpret_cast<void*>(Mock_glUniform1uiv);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glUniform1uiv);
   if (strcmp(name, "glUniform2f") == 0)
-    return reinterpret_cast<void*>(Mock_glUniform2f);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glUniform2f);
   if (strcmp(name, "glUniform2fv") == 0)
-    return reinterpret_cast<void*>(Mock_glUniform2fv);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glUniform2fv);
   if (strcmp(name, "glUniform2i") == 0)
-    return reinterpret_cast<void*>(Mock_glUniform2i);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glUniform2i);
   if (strcmp(name, "glUniform2iv") == 0)
-    return reinterpret_cast<void*>(Mock_glUniform2iv);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glUniform2iv);
   if (strcmp(name, "glUniform2ui") == 0)
-    return reinterpret_cast<void*>(Mock_glUniform2ui);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glUniform2ui);
   if (strcmp(name, "glUniform2uiv") == 0)
-    return reinterpret_cast<void*>(Mock_glUniform2uiv);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glUniform2uiv);
   if (strcmp(name, "glUniform3f") == 0)
-    return reinterpret_cast<void*>(Mock_glUniform3f);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glUniform3f);
   if (strcmp(name, "glUniform3fv") == 0)
-    return reinterpret_cast<void*>(Mock_glUniform3fv);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glUniform3fv);
   if (strcmp(name, "glUniform3i") == 0)
-    return reinterpret_cast<void*>(Mock_glUniform3i);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glUniform3i);
   if (strcmp(name, "glUniform3iv") == 0)
-    return reinterpret_cast<void*>(Mock_glUniform3iv);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glUniform3iv);
   if (strcmp(name, "glUniform3ui") == 0)
-    return reinterpret_cast<void*>(Mock_glUniform3ui);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glUniform3ui);
   if (strcmp(name, "glUniform3uiv") == 0)
-    return reinterpret_cast<void*>(Mock_glUniform3uiv);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glUniform3uiv);
   if (strcmp(name, "glUniform4f") == 0)
-    return reinterpret_cast<void*>(Mock_glUniform4f);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glUniform4f);
   if (strcmp(name, "glUniform4fv") == 0)
-    return reinterpret_cast<void*>(Mock_glUniform4fv);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glUniform4fv);
   if (strcmp(name, "glUniform4i") == 0)
-    return reinterpret_cast<void*>(Mock_glUniform4i);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glUniform4i);
   if (strcmp(name, "glUniform4iv") == 0)
-    return reinterpret_cast<void*>(Mock_glUniform4iv);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glUniform4iv);
   if (strcmp(name, "glUniform4ui") == 0)
-    return reinterpret_cast<void*>(Mock_glUniform4ui);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glUniform4ui);
   if (strcmp(name, "glUniform4uiv") == 0)
-    return reinterpret_cast<void*>(Mock_glUniform4uiv);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glUniform4uiv);
   if (strcmp(name, "glUniformBlockBinding") == 0)
-    return reinterpret_cast<void*>(Mock_glUniformBlockBinding);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glUniformBlockBinding);
   if (strcmp(name, "glUniformMatrix2fv") == 0)
-    return reinterpret_cast<void*>(Mock_glUniformMatrix2fv);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glUniformMatrix2fv);
   if (strcmp(name, "glUniformMatrix2x3fv") == 0)
-    return reinterpret_cast<void*>(Mock_glUniformMatrix2x3fv);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glUniformMatrix2x3fv);
   if (strcmp(name, "glUniformMatrix2x4fv") == 0)
-    return reinterpret_cast<void*>(Mock_glUniformMatrix2x4fv);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glUniformMatrix2x4fv);
   if (strcmp(name, "glUniformMatrix3fv") == 0)
-    return reinterpret_cast<void*>(Mock_glUniformMatrix3fv);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glUniformMatrix3fv);
   if (strcmp(name, "glUniformMatrix3x2fv") == 0)
-    return reinterpret_cast<void*>(Mock_glUniformMatrix3x2fv);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glUniformMatrix3x2fv);
   if (strcmp(name, "glUniformMatrix3x4fv") == 0)
-    return reinterpret_cast<void*>(Mock_glUniformMatrix3x4fv);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glUniformMatrix3x4fv);
   if (strcmp(name, "glUniformMatrix4fv") == 0)
-    return reinterpret_cast<void*>(Mock_glUniformMatrix4fv);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glUniformMatrix4fv);
   if (strcmp(name, "glUniformMatrix4x2fv") == 0)
-    return reinterpret_cast<void*>(Mock_glUniformMatrix4x2fv);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glUniformMatrix4x2fv);
   if (strcmp(name, "glUniformMatrix4x3fv") == 0)
-    return reinterpret_cast<void*>(Mock_glUniformMatrix4x3fv);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glUniformMatrix4x3fv);
   if (strcmp(name, "glUnmapBuffer") == 0)
-    return reinterpret_cast<void*>(Mock_glUnmapBuffer);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glUnmapBuffer);
   if (strcmp(name, "glUnmapBufferOES") == 0)
-    return reinterpret_cast<void*>(Mock_glUnmapBufferOES);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glUnmapBufferOES);
   if (strcmp(name, "glUseProgram") == 0)
-    return reinterpret_cast<void*>(Mock_glUseProgram);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glUseProgram);
   if (strcmp(name, "glValidateProgram") == 0)
-    return reinterpret_cast<void*>(Mock_glValidateProgram);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glValidateProgram);
   if (strcmp(name, "glVertexAttrib1f") == 0)
-    return reinterpret_cast<void*>(Mock_glVertexAttrib1f);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glVertexAttrib1f);
   if (strcmp(name, "glVertexAttrib1fv") == 0)
-    return reinterpret_cast<void*>(Mock_glVertexAttrib1fv);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glVertexAttrib1fv);
   if (strcmp(name, "glVertexAttrib2f") == 0)
-    return reinterpret_cast<void*>(Mock_glVertexAttrib2f);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glVertexAttrib2f);
   if (strcmp(name, "glVertexAttrib2fv") == 0)
-    return reinterpret_cast<void*>(Mock_glVertexAttrib2fv);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glVertexAttrib2fv);
   if (strcmp(name, "glVertexAttrib3f") == 0)
-    return reinterpret_cast<void*>(Mock_glVertexAttrib3f);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glVertexAttrib3f);
   if (strcmp(name, "glVertexAttrib3fv") == 0)
-    return reinterpret_cast<void*>(Mock_glVertexAttrib3fv);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glVertexAttrib3fv);
   if (strcmp(name, "glVertexAttrib4f") == 0)
-    return reinterpret_cast<void*>(Mock_glVertexAttrib4f);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glVertexAttrib4f);
   if (strcmp(name, "glVertexAttrib4fv") == 0)
-    return reinterpret_cast<void*>(Mock_glVertexAttrib4fv);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glVertexAttrib4fv);
   if (strcmp(name, "glVertexAttribDivisor") == 0)
-    return reinterpret_cast<void*>(Mock_glVertexAttribDivisor);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glVertexAttribDivisor);
   if (strcmp(name, "glVertexAttribDivisorANGLE") == 0)
-    return reinterpret_cast<void*>(Mock_glVertexAttribDivisorANGLE);
+    return reinterpret_cast<GLFunctionPointerType>(
+        Mock_glVertexAttribDivisorANGLE);
   if (strcmp(name, "glVertexAttribDivisorARB") == 0)
-    return reinterpret_cast<void*>(Mock_glVertexAttribDivisorARB);
+    return reinterpret_cast<GLFunctionPointerType>(
+        Mock_glVertexAttribDivisorARB);
   if (strcmp(name, "glVertexAttribI4i") == 0)
-    return reinterpret_cast<void*>(Mock_glVertexAttribI4i);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glVertexAttribI4i);
   if (strcmp(name, "glVertexAttribI4iv") == 0)
-    return reinterpret_cast<void*>(Mock_glVertexAttribI4iv);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glVertexAttribI4iv);
   if (strcmp(name, "glVertexAttribI4ui") == 0)
-    return reinterpret_cast<void*>(Mock_glVertexAttribI4ui);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glVertexAttribI4ui);
   if (strcmp(name, "glVertexAttribI4uiv") == 0)
-    return reinterpret_cast<void*>(Mock_glVertexAttribI4uiv);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glVertexAttribI4uiv);
   if (strcmp(name, "glVertexAttribIPointer") == 0)
-    return reinterpret_cast<void*>(Mock_glVertexAttribIPointer);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glVertexAttribIPointer);
   if (strcmp(name, "glVertexAttribPointer") == 0)
-    return reinterpret_cast<void*>(Mock_glVertexAttribPointer);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glVertexAttribPointer);
   if (strcmp(name, "glViewport") == 0)
-    return reinterpret_cast<void*>(Mock_glViewport);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glViewport);
   if (strcmp(name, "glWaitSync") == 0)
-    return reinterpret_cast<void*>(Mock_glWaitSync);
-  return reinterpret_cast<void*>(&MockInvalidFunction);
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glWaitSync);
+  return reinterpret_cast<GLFunctionPointerType>(&MockInvalidFunction);
 }
 
 }  // namespace gl
diff --git a/ui/gl/gl_implementation.cc b/ui/gl/gl_implementation.cc
index c0c5317..0b88b820 100644
--- a/ui/gl/gl_implementation.cc
+++ b/ui/gl/gl_implementation.cc
@@ -123,19 +123,19 @@
   g_get_proc_address = proc;
 }
 
-void* GetGLProcAddress(const char* name) {
+GLFunctionPointerType GetGLProcAddress(const char* name) {
   DCHECK(g_gl_implementation != kGLImplementationNone);
 
   if (g_libraries) {
     for (size_t i = 0; i < g_libraries->size(); ++i) {
-      void* proc = base::GetFunctionPointerFromNativeLibrary((*g_libraries)[i],
-                                                             name);
+      GLFunctionPointerType proc = reinterpret_cast<GLFunctionPointerType>(
+          base::GetFunctionPointerFromNativeLibrary((*g_libraries)[i], name));
       if (proc)
         return proc;
     }
   }
   if (g_get_proc_address) {
-    void* proc = g_get_proc_address(name);
+    GLFunctionPointerType proc = g_get_proc_address(name);
     if (proc)
       return proc;
   }
diff --git a/ui/gl/gl_implementation.h b/ui/gl/gl_implementation.h
index 3566f36..dc42507 100644
--- a/ui/gl/gl_implementation.h
+++ b/ui/gl/gl_implementation.h
@@ -35,10 +35,11 @@
   bool direct_rendering;
 };
 
+using GLFunctionPointerType = void (*)();
 #if defined(OS_WIN)
-typedef void* (WINAPI *GLGetProcAddressProc)(const char* name);
+typedef GLFunctionPointerType(WINAPI* GLGetProcAddressProc)(const char* name);
 #else
-typedef void* (*GLGetProcAddressProc)(const char* name);
+typedef GLFunctionPointerType (*GLGetProcAddressProc)(const char* name);
 #endif
 
 // Initialize stub methods for drawing operations in the GL bindings. The
@@ -99,7 +100,7 @@
 // and when querying functions from the EGL library supplied by Android, it may
 // return a function that prints a log message about the function being
 // unsupported.
-GL_EXPORT void* GetGLProcAddress(const char* name);
+GL_EXPORT GLFunctionPointerType GetGLProcAddress(const char* name);
 
 // Helper for fetching the OpenGL extensions from the current context.
 // This helper abstracts over differences between the desktop OpenGL
diff --git a/ui/gl/gl_mock.h b/ui/gl/gl_mock.h
index 4a7969a7..c91c981 100644
--- a/ui/gl/gl_mock.h
+++ b/ui/gl/gl_mock.h
@@ -14,6 +14,8 @@
 
 namespace gl {
 
+using GLFunctionPointerType = void (*)();
+
 class MockGLInterface {
  public:
   MockGLInterface();
@@ -24,7 +26,8 @@
   static void SetGLInterface(MockGLInterface* gl_interface);
 
   // Find an entry point to the mock GL implementation.
-  static void* GL_BINDING_CALL GetGLProcAddress(const char* name);
+  static GLFunctionPointerType GL_BINDING_CALL
+  GetGLProcAddress(const char* name);
 
   // Include the auto-generated parts of this class. We split this because
   // it means we can easily edit the non-auto generated parts right here in
diff --git a/ui/gl/glx_api_unittest.cc b/ui/gl/glx_api_unittest.cc
index 16f50f4..c36c725 100644
--- a/ui/gl/glx_api_unittest.cc
+++ b/ui/gl/glx_api_unittest.cc
@@ -57,9 +57,11 @@
     return static_cast<GLXContext>(nullptr);
   }
 
-  static void* GL_BINDING_CALL FakeGLGetProcAddress(const char *proc) {
+  static GLFunctionPointerType GL_BINDING_CALL
+  FakeGLGetProcAddress(const char* proc) {
     if (!strcmp("glXCreateContextAttribsARB", proc)) {
-      return reinterpret_cast<void *>(&FakeCreateContextAttribsARB);
+      return reinterpret_cast<GLFunctionPointerType>(
+          &FakeCreateContextAttribsARB);
     }
     return NULL;
   }
diff --git a/ui/gl/wgl_api_unittest.cc b/ui/gl/wgl_api_unittest.cc
index 65fed32..041d3dfa 100644
--- a/ui/gl/wgl_api_unittest.cc
+++ b/ui/gl/wgl_api_unittest.cc
@@ -25,8 +25,7 @@
     g_driver_wgl.fn.wglGetExtensionsStringARBFn = &FakeGetExtensionsStringARB;
     g_driver_wgl.fn.wglGetExtensionsStringEXTFn = &FakeGetExtensionsStringEXT;
     SetGLImplementation(kGLImplementationDesktopGL);
-    SetGLGetProcAddressProc(
-        static_cast<GLGetProcAddressProc>(&FakeGLGetProcAddress));
+    SetGLGetProcAddressProc(&FakeGLGetProcAddress);
   }
 
   void TearDown() override {
@@ -65,7 +64,7 @@
         stringify(wglGetExtensionsStringEXT()));
   }
 
-  static void* GL_BINDING_CALL FakeGLGetProcAddress(const char *proc) {
+  static GLFunctionPointerType WINAPI FakeGLGetProcAddress(const char* proc) {
     return NULL;
   }
 
diff --git a/ui/ozone/platform/cast/surface_factory_cast.cc b/ui/ozone/platform/cast/surface_factory_cast.cc
index 21ff4a2..04e3cfa 100644
--- a/ui/ozone/platform/cast/surface_factory_cast.cc
+++ b/ui/ozone/platform/cast/surface_factory_cast.cc
@@ -321,7 +321,8 @@
 
   void* lib_egl = egl_platform_->GetEglLibrary();
   void* lib_gles2 = egl_platform_->GetGles2Library();
-  gl::GLGetProcAddressProc gl_proc = egl_platform_->GetGLProcAddressProc();
+  gl::GLGetProcAddressProc gl_proc = reinterpret_cast<gl::GLGetProcAddressProc>(
+      egl_platform_->GetGLProcAddressProc());
   if (!lib_egl || !lib_gles2 || !gl_proc) {
     return false;
   }
diff --git a/ui/views/mus/BUILD.gn b/ui/views/mus/BUILD.gn
index 884ea98..f891743b 100644
--- a/ui/views/mus/BUILD.gn
+++ b/ui/views/mus/BUILD.gn
@@ -72,7 +72,7 @@
     "//ui/native_theme",
     "//ui/platform_window",
     "//ui/platform_window/mojo",
-    "//ui/platform_window/mojo:interfaces_cpp_sources",
+    "//ui/platform_window/mojo:interfaces",
     "//ui/platform_window/stub",
     "//ui/views",
     "//ui/wm",