diff --git a/ash/mus/system_tray_delegate_mus.cc b/ash/mus/system_tray_delegate_mus.cc
index dc4a1dc..5093f3c 100644
--- a/ash/mus/system_tray_delegate_mus.cc
+++ b/ash/mus/system_tray_delegate_mus.cc
@@ -4,8 +4,6 @@
 
 #include "ash/mus/system_tray_delegate_mus.h"
 
-#include "ash/session/session_controller.h"
-#include "ash/shell.h"
 #include "ash/system/networking_config_delegate.h"
 
 namespace ash {
@@ -36,10 +34,6 @@
 SystemTrayDelegateMus::~SystemTrayDelegateMus() {
 }
 
-LoginStatus SystemTrayDelegateMus::GetUserLoginStatus() const {
-  return Shell::Get()->session_controller()->login_status();
-}
-
 NetworkingConfigDelegate* SystemTrayDelegateMus::GetNetworkingConfigDelegate()
     const {
   return networking_config_delegate_.get();
diff --git a/ash/mus/system_tray_delegate_mus.h b/ash/mus/system_tray_delegate_mus.h
index 8160966..61133d6 100644
--- a/ash/mus/system_tray_delegate_mus.h
+++ b/ash/mus/system_tray_delegate_mus.h
@@ -25,7 +25,6 @@
 
  private:
   // SystemTrayDelegate:
-  LoginStatus GetUserLoginStatus() const override;
   NetworkingConfigDelegate* GetNetworkingConfigDelegate() const override;
 
   std::unique_ptr<NetworkingConfigDelegate> networking_config_delegate_;
diff --git a/ash/root_window_controller_unittest.cc b/ash/root_window_controller_unittest.cc
index 3992e8d..4693707 100644
--- a/ash/root_window_controller_unittest.cc
+++ b/ash/root_window_controller_unittest.cc
@@ -11,7 +11,6 @@
 #include "ash/session/session_controller.h"
 #include "ash/shell.h"
 #include "ash/shell_port.h"
-#include "ash/system/tray/system_tray_delegate.h"
 #include "ash/test/ash_test_base.h"
 #include "ash/test/test_session_controller_client.h"
 #include "ash/wm/system_modal_container_layout_manager.h"
@@ -387,8 +386,7 @@
   UpdateDisplay("600x600");
   RootWindowController* controller =
       ShellPort::Get()->GetPrimaryRootWindowController();
-  EXPECT_EQ(LoginStatus::USER,
-            Shell::Get()->system_tray_delegate()->GetUserLoginStatus());
+  EXPECT_TRUE(Shell::Get()->session_controller()->IsActiveUserSessionStarted());
   EXPECT_EQ(GetLayoutManager(controller, kShellWindowId_SystemModalContainer),
             controller->GetSystemModalLayoutManager(NULL));
 
@@ -399,8 +397,7 @@
                 WmWindow::Get(session_modal_widget->GetNativeWindow())));
 
   Shell::Get()->session_controller()->LockScreenAndFlushForTest();
-  EXPECT_EQ(LoginStatus::LOCKED,
-            Shell::Get()->system_tray_delegate()->GetUserLoginStatus());
+  EXPECT_TRUE(Shell::Get()->session_controller()->IsScreenLocked());
   EXPECT_EQ(
       GetLayoutManager(controller, kShellWindowId_LockSystemModalContainer),
       controller->GetSystemModalLayoutManager(nullptr));
@@ -426,8 +423,6 @@
   // Configure login screen environment.
   SessionController* session_controller = Shell::Get()->session_controller();
   SetUserLoggedIn(false);
-  EXPECT_EQ(LoginStatus::NOT_LOGGED_IN,
-            Shell::Get()->system_tray_delegate()->GetUserLoginStatus());
   EXPECT_EQ(0, session_controller->NumberOfLoggedInUsers());
   EXPECT_FALSE(session_controller->IsActiveUserSessionStarted());
 
@@ -450,8 +445,6 @@
   // Configure user session environment.
   SetUserLoggedIn(true);
   SetSessionStarted(true);
-  EXPECT_EQ(LoginStatus::USER,
-            Shell::Get()->system_tray_delegate()->GetUserLoginStatus());
   EXPECT_EQ(1, session_controller->NumberOfLoggedInUsers());
   EXPECT_TRUE(session_controller->IsActiveUserSessionStarted());
   EXPECT_EQ(GetLayoutManager(controller, kShellWindowId_SystemModalContainer),
diff --git a/ash/session/session_controller.cc b/ash/session/session_controller.cc
index 1e12bb1..a4edf2d 100644
--- a/ash/session/session_controller.cc
+++ b/ash/session/session_controller.cc
@@ -146,6 +146,15 @@
   return active_user_type == user_manager::USER_TYPE_CHILD;
 }
 
+bool SessionController::IsKioskSession() const {
+  if (!IsActiveUserSessionStarted())
+    return false;
+
+  user_manager::UserType active_user_type = GetUserSession(0)->type;
+  return active_user_type == user_manager::USER_TYPE_KIOSK_APP ||
+         active_user_type == user_manager::USER_TYPE_ARC_KIOSK_APP;
+}
+
 void SessionController::LockScreen() {
   if (client_)
     client_->RequestLockScreen();
diff --git a/ash/session/session_controller.h b/ash/session/session_controller.h
index df963941..15bb98f 100644
--- a/ash/session/session_controller.h
+++ b/ash/session/session_controller.h
@@ -91,6 +91,10 @@
   // Returns true if the current user is a child account.
   bool IsUserChild() const;
 
+  // Returns true if the current user session is a kiosk session (either
+  // chrome app kiosk or ARC kiosk).
+  bool IsKioskSession() const;
+
   // Locks the screen. The locking happens asynchronously.
   void LockScreen();
 
diff --git a/ash/shelf/wm_shelf.cc b/ash/shelf/wm_shelf.cc
index bd0cbd6..6abaf16 100644
--- a/ash/shelf/wm_shelf.cc
+++ b/ash/shelf/wm_shelf.cc
@@ -17,7 +17,6 @@
 #include "ash/shelf/shelf_widget.h"
 #include "ash/shelf/wm_shelf_observer.h"
 #include "ash/shell.h"
-#include "ash/system/tray/system_tray_delegate.h"
 #include "ash/wm_window.h"
 #include "base/logging.h"
 #include "base/memory/ptr_util.h"
@@ -79,8 +78,8 @@
   if (Shell::Get()->session_controller()->IsUserSupervised())
     return false;
 
-  LoginStatus login_status =
-      Shell::Get()->system_tray_delegate()->GetUserLoginStatus();
+  const LoginStatus login_status =
+      Shell::Get()->session_controller()->login_status();
 
   switch (login_status) {
     case LoginStatus::LOCKED:
diff --git a/ash/system/overview/overview_button_tray.cc b/ash/system/overview/overview_button_tray.cc
index 331f445..ff57710 100644
--- a/ash/system/overview/overview_button_tray.cc
+++ b/ash/system/overview/overview_button_tray.cc
@@ -10,7 +10,6 @@
 #include "ash/shell.h"
 #include "ash/shell_port.h"
 #include "ash/strings/grit/ash_strings.h"
-#include "ash/system/tray/system_tray_delegate.h"
 #include "ash/system/tray/tray_constants.h"
 #include "ash/system/tray/tray_container.h"
 #include "ash/wm/maximize_mode/maximize_mode_controller.h"
@@ -107,10 +106,7 @@
       !session_controller->IsScreenLocked() &&
       session_controller->GetSessionState() ==
           session_manager::SessionState::ACTIVE &&
-      shell->system_tray_delegate()->GetUserLoginStatus() !=
-          LoginStatus::KIOSK_APP &&
-      shell->system_tray_delegate()->GetUserLoginStatus() !=
-          LoginStatus::ARC_KIOSK_APP);
+      !session_controller->IsKioskSession());
 }
 
 }  // namespace ash
diff --git a/ash/system/palette/palette_tray.cc b/ash/system/palette/palette_tray.cc
index d248d518..ead0f4b4 100644
--- a/ash/system/palette/palette_tray.cc
+++ b/ash/system/palette/palette_tray.cc
@@ -17,7 +17,6 @@
 #include "ash/system/palette/palette_utils.h"
 #include "ash/system/tray/system_menu_button.h"
 #include "ash/system/tray/system_tray_controller.h"
-#include "ash/system/tray/system_tray_delegate.h"
 #include "ash/system/tray/tray_bubble_wrapper.h"
 #include "ash/system/tray/tray_constants.h"
 #include "ash/system/tray/tray_container.h"
@@ -66,8 +65,7 @@
   return !session_controller->IsUserSessionBlocked() &&
          session_controller->GetSessionState() ==
              session_manager::SessionState::ACTIVE &&
-         Shell::Get()->system_tray_delegate()->GetUserLoginStatus() !=
-             LoginStatus::KIOSK_APP;
+         !session_controller->IsKioskSession();
 }
 
 // Returns true if the |palette_tray| is on an internal display or on every
diff --git a/ash/system/power/tablet_power_button_controller_unittest.cc b/ash/system/power/tablet_power_button_controller_unittest.cc
index c11d4493..161772c 100644
--- a/ash/system/power/tablet_power_button_controller_unittest.cc
+++ b/ash/system/power/tablet_power_button_controller_unittest.cc
@@ -141,7 +141,7 @@
 TEST_F(TabletPowerButtonControllerTest, LockScreenIfRequired) {
   Initialize(LoginStatus::USER);
   SetShouldLockScreenAutomatically(true);
-  EXPECT_FALSE(GetLockedState());
+  ASSERT_FALSE(GetLockedState());
 
   // On User logged in status, power-button-press-release should lock screen if
   // automatic screen-locking was requested.
@@ -261,7 +261,7 @@
   PressPowerButton();
   ReleasePowerButton();
   power_manager_client_->SendBrightnessChanged(0, false);
-  EXPECT_TRUE(GetBacklightsForcedOff());
+  ASSERT_TRUE(GetBacklightsForcedOff());
   power_manager_client_->SendSuspendImminent();
   // There is a power button pressed here, but PowerButtonEvent is sent later.
   // Because of backlights forced off, resuming system will not restore
@@ -298,7 +298,7 @@
   PressPowerButton();
   ReleasePowerButton();
   power_manager_client_->SendBrightnessChanged(0, false);
-  EXPECT_TRUE(GetBacklightsForcedOff());
+  ASSERT_TRUE(GetBacklightsForcedOff());
   generator_->PressKey(ui::VKEY_L, ui::EF_NONE);
   power_manager_client_->SendBrightnessChanged(kNonZeroBrightness, false);
   EXPECT_FALSE(GetBacklightsForcedOff());
@@ -307,7 +307,7 @@
   PressPowerButton();
   ReleasePowerButton();
   power_manager_client_->SendBrightnessChanged(0, false);
-  EXPECT_TRUE(GetBacklightsForcedOff());
+  ASSERT_TRUE(GetBacklightsForcedOff());
   generator_->MoveMouseBy(1, 1);
   power_manager_client_->SendBrightnessChanged(kNonZeroBrightness, false);
   EXPECT_FALSE(GetBacklightsForcedOff());
@@ -316,7 +316,7 @@
   PressPowerButton();
   ReleasePowerButton();
   power_manager_client_->SendBrightnessChanged(0, false);
-  EXPECT_TRUE(GetBacklightsForcedOff());
+  ASSERT_TRUE(GetBacklightsForcedOff());
   generator_->set_flags(ui::EF_IS_SYNTHESIZED);
   generator_->MoveMouseBy(1, 1);
   generator_->set_flags(ui::EF_NONE);
@@ -331,7 +331,7 @@
   PressPowerButton();
   ReleasePowerButton();
   power_manager_client_->SendBrightnessChanged(0, false);
-  EXPECT_TRUE(GetBacklightsForcedOff());
+  ASSERT_TRUE(GetBacklightsForcedOff());
   generator_->PressKey(ui::VKEY_L, ui::EF_NONE);
   EXPECT_TRUE(GetBacklightsForcedOff());
 
@@ -350,6 +350,7 @@
   // There are two |power_key_pressed| events and |power_key_released| events
   // generated for each pressing and releasing, and multiple repeating pressed
   // events depending on holding.
+  ASSERT_EQ(0, power_manager_client_->num_set_backlights_forced_off_calls());
   tablet_controller_->OnKeyEvent(&power_key_pressed);
   tablet_controller_->OnKeyEvent(&power_key_pressed);
   PressPowerButton();
@@ -364,10 +365,10 @@
 
 // Tests that under (1) tablet power button pressed/released, (2) keyboard/mouse
 // events on laptop mode when screen is off, requesting/stopping backlights
-// forced off should also set corresponding touch screen state in local pref.
-TEST_F(TabletPowerButtonControllerTest, TouchScreenState) {
+// forced off should also set corresponding touchscreen state in local pref.
+TEST_F(TabletPowerButtonControllerTest, TouchscreenState) {
   // Tests tablet power button.
-  EXPECT_TRUE(shell_delegate_->IsTouchscreenEnabledInPrefs(true));
+  ASSERT_TRUE(shell_delegate_->IsTouchscreenEnabledInPrefs(true));
   PressPowerButton();
   ReleasePowerButton();
   power_manager_client_->SendBrightnessChanged(0, false);
@@ -383,8 +384,8 @@
   PressPowerButton();
   ReleasePowerButton();
   power_manager_client_->SendBrightnessChanged(0, false);
-  EXPECT_TRUE(GetBacklightsForcedOff());
-  EXPECT_FALSE(shell_delegate_->IsTouchscreenEnabledInPrefs(true));
+  ASSERT_TRUE(GetBacklightsForcedOff());
+  ASSERT_FALSE(shell_delegate_->IsTouchscreenEnabledInPrefs(true));
   generator_->PressKey(ui::VKEY_L, ui::EF_NONE);
   power_manager_client_->SendBrightnessChanged(kNonZeroBrightness, false);
   EXPECT_TRUE(shell_delegate_->IsTouchscreenEnabledInPrefs(true));
@@ -393,8 +394,8 @@
   PressPowerButton();
   ReleasePowerButton();
   power_manager_client_->SendBrightnessChanged(0, false);
-  EXPECT_TRUE(GetBacklightsForcedOff());
-  EXPECT_FALSE(shell_delegate_->IsTouchscreenEnabledInPrefs(true));
+  ASSERT_TRUE(GetBacklightsForcedOff());
+  ASSERT_FALSE(shell_delegate_->IsTouchscreenEnabledInPrefs(true));
   generator_->MoveMouseBy(1, 1);
   power_manager_client_->SendBrightnessChanged(kNonZeroBrightness, false);
   EXPECT_TRUE(shell_delegate_->IsTouchscreenEnabledInPrefs(true));
@@ -406,7 +407,7 @@
        EnterOrLeaveMaximizeModeWhilePressingPowerButton) {
   Initialize(LoginStatus::USER);
   SetShouldLockScreenAutomatically(true);
-  EXPECT_FALSE(GetLockedState());
+  ASSERT_FALSE(GetLockedState());
 
   power_manager_client_->SendPowerButtonEvent(true, tick_clock_->NowTicks());
   EXPECT_TRUE(test_api_->ShutdownTimerIsRunning());
@@ -457,7 +458,7 @@
   PressPowerButton();
   ReleasePowerButton();
   power_manager_client_->SendBrightnessChanged(0, false);
-  EXPECT_TRUE(GetBacklightsForcedOff());
+  ASSERT_TRUE(GetBacklightsForcedOff());
 
   // Test that a pressing-releasing operation after a short duration, backlights
   // forced off is stopped since we don't drop request for power button pressed.
diff --git a/ash/system/session/logout_confirmation_controller.cc b/ash/system/session/logout_confirmation_controller.cc
index d3b29cf..7fab21d 100644
--- a/ash/system/session/logout_confirmation_controller.cc
+++ b/ash/system/session/logout_confirmation_controller.cc
@@ -10,7 +10,6 @@
 #include "ash/session/session_controller.h"
 #include "ash/shell.h"
 #include "ash/system/session/logout_confirmation_dialog.h"
-#include "ash/system/tray/system_tray_delegate.h"
 #include "ash/system/tray/system_tray_notifier.h"
 #include "base/location.h"
 #include "base/time/default_tick_clock.h"
@@ -91,7 +90,7 @@
 }
 
 void LogoutConfirmationController::OnLastWindowClosed() {
-  if (Shell::Get()->system_tray_delegate()->GetUserLoginStatus() !=
+  if (Shell::Get()->session_controller()->login_status() !=
       LoginStatus::PUBLIC) {
     return;
   }
diff --git a/ash/system/status_area_widget.cc b/ash/system/status_area_widget.cc
index 0c7d58b..0a0a7af 100644
--- a/ash/system/status_area_widget.cc
+++ b/ash/system/status_area_widget.cc
@@ -7,6 +7,7 @@
 #include "ash/public/cpp/config.h"
 #include "ash/public/cpp/shell_window_ids.h"
 #include "ash/root_window_controller.h"
+#include "ash/session/session_controller.h"
 #include "ash/shelf/wm_shelf.h"
 #include "ash/shell.h"
 #include "ash/system/ime_menu/ime_menu_tray.h"
@@ -74,7 +75,8 @@
   ime_menu_tray_->Initialize();
   overview_button_tray_->Initialize();
   UpdateAfterShelfAlignmentChange();
-  UpdateAfterLoginStatusChange(delegate->GetUserLoginStatus());
+  UpdateAfterLoginStatusChange(
+      Shell::Get()->session_controller()->login_status());
 }
 
 void StatusAreaWidget::Shutdown() {
diff --git a/ash/system/tray/system_tray.cc b/ash/system/tray/system_tray.cc
index cc76d10..f16a8991 100644
--- a/ash/system/tray/system_tray.cc
+++ b/ash/system/tray/system_tray.cc
@@ -127,8 +127,8 @@
                 TrayBubbleView::InitParams* init_params,
                 bool is_persistent) {
     DCHECK(anchor);
-    LoginStatus login_status =
-        Shell::Get()->system_tray_delegate()->GetUserLoginStatus();
+    const LoginStatus login_status =
+        Shell::Get()->session_controller()->login_status();
     bubble_->InitView(anchor, login_status, init_params);
     bubble_->bubble_view()->set_anchor_view_insets(anchor_insets);
     bubble_wrapper_.reset(new TrayBubbleWrapper(tray, bubble_->bubble_view()));
@@ -293,9 +293,8 @@
   SystemTrayItem* item_ptr = item.get();
   items_.push_back(std::move(item));
 
-  SystemTrayDelegate* delegate = Shell::Get()->system_tray_delegate();
-  views::View* tray_item =
-      item_ptr->CreateTrayView(delegate->GetUserLoginStatus());
+  views::View* tray_item = item_ptr->CreateTrayView(
+      Shell::Get()->session_controller()->login_status());
   item_ptr->UpdateAfterShelfAlignmentChange();
 
   if (tray_item) {
@@ -433,13 +432,8 @@
                            BubbleCreationType creation_type,
                            bool persistent) {
   // No system tray bubbles in kiosk mode.
-  SystemTrayDelegate* system_tray_delegate =
-      Shell::Get()->system_tray_delegate();
-  if (system_tray_delegate->GetUserLoginStatus() == LoginStatus::KIOSK_APP ||
-      system_tray_delegate->GetUserLoginStatus() ==
-          LoginStatus::ARC_KIOSK_APP) {
+  if (Shell::Get()->session_controller()->IsKioskSession())
     return;
-  }
 
   // Destroy any existing bubble and create a new one.
   SystemTrayBubble::BubbleType bubble_type =
diff --git a/ash/system/tray/system_tray_bubble.cc b/ash/system/tray/system_tray_bubble.cc
index 73a2d12..d712b25 100644
--- a/ash/system/tray/system_tray_bubble.cc
+++ b/ash/system/tray/system_tray_bubble.cc
@@ -7,6 +7,7 @@
 #include <utility>
 #include <vector>
 
+#include "ash/session/session_controller.h"
 #include "ash/shell.h"
 #include "ash/shell_port.h"
 #include "ash/system/tray/system_tray.h"
@@ -142,7 +143,7 @@
 
   items_ = items;
   bubble_type_ = bubble_type;
-  CreateItemViews(Shell::Get()->system_tray_delegate()->GetUserLoginStatus());
+  CreateItemViews(Shell::Get()->session_controller()->login_status());
 
   // Close bubble view if we failed to create the item view.
   if (!bubble_view_->has_children()) {
diff --git a/ash/system/tray/system_tray_delegate.cc b/ash/system/tray/system_tray_delegate.cc
index ef90879..e8f58c7 100644
--- a/ash/system/tray/system_tray_delegate.cc
+++ b/ash/system/tray/system_tray_delegate.cc
@@ -15,10 +15,6 @@
 
 void SystemTrayDelegate::Initialize() {}
 
-LoginStatus SystemTrayDelegate::GetUserLoginStatus() const {
-  return LoginStatus::NOT_LOGGED_IN;
-}
-
 void SystemTrayDelegate::ShowUserLogin() {}
 
 void SystemTrayDelegate::GetCurrentIME(IMEInfo* info) {}
diff --git a/ash/system/tray/system_tray_delegate.h b/ash/system/tray/system_tray_delegate.h
index d8bb583..c0c5b0bf 100644
--- a/ash/system/tray/system_tray_delegate.h
+++ b/ash/system/tray/system_tray_delegate.h
@@ -10,7 +10,6 @@
 #include <vector>
 
 #include "ash/ash_export.h"
-#include "ash/login_status.h"
 #include "base/callback_forward.h"
 #include "base/files/file_path.h"
 #include "base/i18n/time_formatting.h"
@@ -48,9 +47,6 @@
   // Called after SystemTray has been instantiated.
   virtual void Initialize();
 
-  // Gets information about the active user.
-  virtual LoginStatus GetUserLoginStatus() const;
-
   // Shows login UI to add other users to this session.
   virtual void ShowUserLogin();
 
diff --git a/ash/system/tray_accessibility.cc b/ash/system/tray_accessibility.cc
index 3e866f8..854e81cc 100644
--- a/ash/system/tray_accessibility.cc
+++ b/ash/system/tray_accessibility.cc
@@ -7,6 +7,7 @@
 #include "ash/accessibility_delegate.h"
 #include "ash/accessibility_types.h"
 #include "ash/resources/vector_icons/vector_icons.h"
+#include "ash/session/session_controller.h"
 #include "ash/session/session_state_delegate.h"
 #include "ash/shell.h"
 #include "ash/shell_port.h"
@@ -14,7 +15,6 @@
 #include "ash/system/tray/hover_highlight_view.h"
 #include "ash/system/tray/system_tray.h"
 #include "ash/system/tray/system_tray_controller.h"
-#include "ash/system/tray/system_tray_delegate.h"
 #include "ash/system/tray/system_tray_notifier.h"
 #include "ash/system/tray/tray_constants.h"
 #include "ash/system/tray/tray_details_view.h"
@@ -91,7 +91,7 @@
 }
 
 LoginStatus GetCurrentLoginStatus() {
-  return Shell::Get()->system_tray_delegate()->GetUserLoginStatus();
+  return Shell::Get()->session_controller()->login_status();
 }
 
 }  // namespace
diff --git a/ash/test/status_area_widget_test_helper.cc b/ash/test/status_area_widget_test_helper.cc
index 5b7e496..134473e 100644
--- a/ash/test/status_area_widget_test_helper.cc
+++ b/ash/test/status_area_widget_test_helper.cc
@@ -5,14 +5,14 @@
 #include "ash/test/status_area_widget_test_helper.h"
 
 #include "ash/root_window_controller.h"
+#include "ash/session/session_controller.h"
 #include "ash/shell.h"
 #include "ash/system/status_area_widget.h"
-#include "ash/system/tray/system_tray_delegate.h"
 
 namespace ash {
 
 LoginStatus StatusAreaWidgetTestHelper::GetUserLoginStatus() {
-  return Shell::Get()->system_tray_delegate()->GetUserLoginStatus();
+  return Shell::Get()->session_controller()->login_status();
 }
 
 StatusAreaWidget* StatusAreaWidgetTestHelper::GetStatusAreaWidget() {
diff --git a/ash/test/test_system_tray_delegate.cc b/ash/test/test_system_tray_delegate.cc
index d3ea55c..9e47cc9 100644
--- a/ash/test/test_system_tray_delegate.cc
+++ b/ash/test/test_system_tray_delegate.cc
@@ -6,8 +6,6 @@
 
 #include <string>
 
-#include "ash/login_status.h"
-#include "ash/session/session_controller.h"
 #include "ash/shell.h"
 #include "base/time/time.h"
 
@@ -36,10 +34,6 @@
   ime_list_ = list;
 }
 
-LoginStatus TestSystemTrayDelegate::GetUserLoginStatus() const {
-  return Shell::Get()->session_controller()->login_status();
-}
-
 bool TestSystemTrayDelegate::GetSessionStartTime(
     base::TimeTicks* session_start_time) {
   // Just returns TimeTicks::Now(), so the remaining time is always the
diff --git a/ash/test/test_system_tray_delegate.h b/ash/test/test_system_tray_delegate.h
index b9e8f0c..86e17f4 100644
--- a/ash/test/test_system_tray_delegate.h
+++ b/ash/test/test_system_tray_delegate.h
@@ -32,7 +32,6 @@
   void SetAvailableIMEList(const IMEInfoList& list);
 
   // SystemTrayDelegate:
-  LoginStatus GetUserLoginStatus() const override;
   bool GetSessionStartTime(base::TimeTicks* session_start_time) override;
   bool GetSessionLengthLimit(base::TimeDelta* session_length_limit) override;
   void GetCurrentIME(IMEInfo* info) override;
diff --git a/ash/wm/maximize_mode/maximize_mode_event_handler.cc b/ash/wm/maximize_mode/maximize_mode_event_handler.cc
index 7f93103..0a38ac8 100644
--- a/ash/wm/maximize_mode/maximize_mode_event_handler.cc
+++ b/ash/wm/maximize_mode/maximize_mode_event_handler.cc
@@ -6,7 +6,6 @@
 
 #include "ash/session/session_controller.h"
 #include "ash/shell.h"
-#include "ash/system/tray/system_tray_delegate.h"
 #include "ash/wm/window_state.h"
 #include "ash/wm/window_util.h"
 #include "ash/wm/wm_event.h"
@@ -55,13 +54,8 @@
   }
 
   // Do not exit fullscreen in kiosk mode.
-  SystemTrayDelegate* system_tray_delegate =
-      Shell::Get()->system_tray_delegate();
-  if (system_tray_delegate->GetUserLoginStatus() == LoginStatus::KIOSK_APP ||
-      system_tray_delegate->GetUserLoginStatus() ==
-          LoginStatus::ARC_KIOSK_APP) {
+  if (Shell::Get()->session_controller()->IsKioskSession())
     return false;
-  }
 
   WMEvent toggle_fullscreen(WM_EVENT_TOGGLE_FULLSCREEN);
   window->GetWindowState()->OnWMEvent(&toggle_fullscreen);
diff --git a/ash/wm/overview/window_selector_controller.cc b/ash/wm/overview/window_selector_controller.cc
index 86057fd..006a1a7 100644
--- a/ash/wm/overview/window_selector_controller.cc
+++ b/ash/wm/overview/window_selector_controller.cc
@@ -9,7 +9,6 @@
 #include "ash/session/session_controller.h"
 #include "ash/shell.h"
 #include "ash/shell_port.h"
-#include "ash/system/tray/system_tray_delegate.h"
 #include "ash/wm/mru_window_tracker.h"
 #include "ash/wm/overview/window_selector.h"
 #include "ash/wm/screen_pinning_controller.h"
@@ -35,15 +34,11 @@
   // Don't allow a window overview if the screen is locked or a modal dialog is
   // open or running in kiosk app session.
   SessionController* session_controller = Shell::Get()->session_controller();
-  SystemTrayDelegate* system_tray_delegate =
-      Shell::Get()->system_tray_delegate();
   return session_controller->IsActiveUserSessionStarted() &&
          !session_controller->IsScreenLocked() &&
          !ShellPort::Get()->IsSystemModalWindowOpen() &&
          !Shell::Get()->screen_pinning_controller()->IsPinned() &&
-         system_tray_delegate->GetUserLoginStatus() != LoginStatus::KIOSK_APP &&
-         system_tray_delegate->GetUserLoginStatus() !=
-             LoginStatus::ARC_KIOSK_APP;
+         !session_controller->IsKioskSession();
 }
 
 bool WindowSelectorController::ToggleOverview() {
diff --git a/base/run_loop_unittest.cc b/base/run_loop_unittest.cc
index dcddcfbf..ea596c60 100644
--- a/base/run_loop_unittest.cc
+++ b/base/run_loop_unittest.cc
@@ -201,7 +201,7 @@
                                             RunLoop nested_run_loop;
                                             nested_run_loop.RunUntilIdle();
                                           }));
-  EXPECT_DEATH({ run_loop_.RunUntilIdle(); }, "Check failed");
+  EXPECT_DEATH({ run_loop_.RunUntilIdle(); }, "");
 }
 #endif  // defined(GTEST_HAS_DEATH_TEST) && !defined(OS_ANDROID)
 
diff --git a/cc/trees/layer_tree_host_impl_unittest.cc b/cc/trees/layer_tree_host_impl_unittest.cc
index 774d26f..bf60a07 100644
--- a/cc/trees/layer_tree_host_impl_unittest.cc
+++ b/cc/trees/layer_tree_host_impl_unittest.cc
@@ -2919,6 +2919,7 @@
     LayerImpl* root = host_impl_->active_tree()->InnerViewportContainerLayer();
     scrollbar->SetScrollElementId(scroll->element_id());
     root->test_properties()->AddChild(std::move(scrollbar));
+    scroll->set_needs_show_scrollbars(true);
     host_impl_->active_tree()->BuildPropertyTreesForTesting();
     host_impl_->active_tree()->DidBecomeActive();
     host_impl_->active_tree()->HandleScrollbarShowRequestsFromMain();
@@ -3222,6 +3223,7 @@
   scrollbar->SetPosition(gfx::PointF(90, 0));
   container->test_properties()->AddChild(std::move(scrollbar));
   host_impl_->pending_tree()->PushPageScaleFromMainThread(1.f, 1.f, 1.f);
+  scroll->set_needs_show_scrollbars(true);
   host_impl_->pending_tree()->BuildPropertyTreesForTesting();
   host_impl_->ActivateSyncTree();
 
diff --git a/cc/trees/layer_tree_impl.cc b/cc/trees/layer_tree_impl.cc
index e94a5a6..1fe5871 100644
--- a/cc/trees/layer_tree_impl.cc
+++ b/cc/trees/layer_tree_impl.cc
@@ -1699,8 +1699,6 @@
       std::pair<int, int>(layer->scroll_clip_layer_id(), layer->id()));
 
   DidUpdateScrollState(layer->id());
-
-  layer->set_needs_show_scrollbars(true);
 }
 
 void LayerTreeImpl::UnregisterScrollLayer(LayerImpl* layer) {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/TabularContextMenuListAdapter.java b/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/TabularContextMenuListAdapter.java
index 0865a76..eed6bef 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/TabularContextMenuListAdapter.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/TabularContextMenuListAdapter.java
@@ -5,6 +5,7 @@
 package org.chromium.chrome.browser.contextmenu;
 
 import android.app.Activity;
+import android.content.Intent;
 import android.graphics.drawable.Drawable;
 import android.util.Pair;
 import android.view.LayoutInflater;
@@ -84,8 +85,9 @@
         viewHolder.mIcon.setVisibility(icon != null ? View.VISIBLE : View.INVISIBLE);
 
         if (menuItem == ContextMenuItem.SHARE_IMAGE) {
+            Intent shareIntent = ShareHelper.getShareImageIntent(null);
             final Pair<Drawable, CharSequence> shareInfo =
-                    ShareHelper.getShareableIconAndName(mActivity);
+                    ShareHelper.getShareableIconAndName(mActivity, shareIntent);
             if (shareInfo.first != null) {
                 viewHolder.mShareIcon.setImageDrawable(shareInfo.first);
                 viewHolder.mShareIcon.setVisibility(View.VISIBLE);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/share/ShareHelper.java b/chrome/android/java/src/org/chromium/chrome/browser/share/ShareHelper.java
index 79ef3a3..57d11a6 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/share/ShareHelper.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/share/ShareHelper.java
@@ -363,15 +363,19 @@
                 if (ApplicationStatus.getStateForApplication()
                         != ApplicationState.HAS_DESTROYED_ACTIVITIES) {
                     Uri imageUri = ApiCompatibilityUtils.getUriForImageCaptureFile(saveFile);
-
+                    Intent shareIntent = getShareImageIntent(imageUri);
                     if (name == null) {
-                        Intent chooserIntent = Intent.createChooser(getShareImageIntent(imageUri),
-                                activity.getString(R.string.share_link_chooser_title));
-                        fireIntent(activity, chooserIntent);
+                        if (TargetChosenReceiver.isSupported()) {
+                            TargetChosenReceiver.sendChooserIntent(
+                                    true, activity, shareIntent, null);
+                        } else {
+                            Intent chooserIntent = Intent.createChooser(shareIntent,
+                                    activity.getString(R.string.share_link_chooser_title));
+                            fireIntent(activity, chooserIntent);
+                        }
                     } else {
-                        Intent imageIntent = getShareImageIntent(imageUri);
-                        imageIntent.setComponent(name);
-                        fireIntent(activity, imageIntent);
+                        shareIntent.setComponent(name);
+                        fireIntent(activity, shareIntent);
                     }
                 }
             }
@@ -548,7 +552,8 @@
      * @param item The menu item that is used for direct share
      */
     public static void configureDirectShareMenuItem(Activity activity, MenuItem item) {
-        Pair<Drawable, CharSequence> directShare = getShareableIconAndName(activity);
+        Intent shareIntent = getShareIntent(activity, "", "", "", null, null);
+        Pair<Drawable, CharSequence> directShare = getShareableIconAndName(activity, shareIntent);
         Drawable directShareIcon = directShare.first;
         CharSequence directShareTitle = directShare.second;
 
@@ -562,19 +567,20 @@
     /**
      * Get the icon and name of the most recently shared app within chrome.
      * @param activity Activity that is used to access the package manager.
+     * @param shareIntent Intent used to get list of apps support sharing.
      * @return The Image and the String of the recently shared Icon.
      */
-    public static Pair<Drawable, CharSequence> getShareableIconAndName(Activity activity) {
+    public static Pair<Drawable, CharSequence> getShareableIconAndName(
+            Activity activity, Intent shareIntent) {
         Drawable directShareIcon = null;
         CharSequence directShareTitle = null;
 
         final ComponentName component = getLastShareComponentName();
         boolean isComponentValid = false;
         if (component != null) {
-            Intent intent = getShareIntent(activity, "", "", "", null, null);
-            intent.setPackage(component.getPackageName());
+            shareIntent.setPackage(component.getPackageName());
             PackageManager manager = activity.getPackageManager();
-            List<ResolveInfo> resolveInfoList = manager.queryIntentActivities(intent, 0);
+            List<ResolveInfo> resolveInfoList = manager.queryIntentActivities(shareIntent, 0);
             for (ResolveInfo info : resolveInfoList) {
                 ActivityInfo ai = info.activityInfo;
                 if (component.equals(new ComponentName(ai.applicationInfo.packageName, ai.name))) {
@@ -677,7 +683,12 @@
         return intent;
     }
 
-    private static Intent getShareImageIntent(Uri imageUri) {
+    /**
+     * Creates an Intent to share an image.
+     * @param imageUri The Uri of the image.
+     * @return The Intent used to share the image.
+     */
+    public static Intent getShareImageIntent(Uri imageUri) {
         Intent intent = new Intent(Intent.ACTION_SEND);
         intent.addFlags(ApiCompatibilityUtils.getActivityNewDocumentFlag());
         intent.setType("image/jpeg");
diff --git a/chrome/android/java/strings/android_chrome_strings.grd b/chrome/android/java/strings/android_chrome_strings.grd
index 82c4a0c..09d3e740 100644
--- a/chrome/android/java/strings/android_chrome_strings.grd
+++ b/chrome/android/java/strings/android_chrome_strings.grd
@@ -741,7 +741,7 @@
       <message name="IDS_POPUP_PERMISSION_TITLE" desc="Title of the permission to display pop-up windows [CHAR-LIMIT=32]">
         Pop-ups
       </message>
-      <message name="IDS_SUBRESOURCE_FILTER_PERMISSION_TITLE" desc="Title for the subresource filter permission [CHAR-LIMIT=32]">
+      <message name="IDS_SUBRESOURCE_FILTER_PERMISSION_TITLE" desc="Title for the subresource filter permission [CHAR-LIMIT=32]" translateable="false">
          Subresource Filter
       </message>
       <message name="IDS_PUSH_NOTIFICATIONS_PERMISSION_TITLE" desc="Title for the permission for showing push notifications [CHAR-LIMIT=32]">
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd
index a0499698..6d45463 100644
--- a/chrome/app/generated_resources.grd
+++ b/chrome/app/generated_resources.grd
@@ -12093,13 +12093,13 @@
       Chrome has blocked parts of this site to protect you from potentially deceptive content.
     </message>
 
-    <message name="IDS_SUBRESOURCE_FILTER_HEADER" desc="Header for the subresource filter setting on the Content Settings dialog.">
+    <message name="IDS_SUBRESOURCE_FILTER_HEADER" desc="Header for the subresource filter setting on the Content Settings dialog." translateable="false">
        Subresource Filter
     </message>
-    <message name="IDS_SUBRESOURCE_FILTER_ALLOW_RADIO" desc="A radio button on the Content Settings dialog to allow subresource filter use.">
+    <message name="IDS_SUBRESOURCE_FILTER_ALLOW_RADIO" desc="A radio button on the Content Settings dialog to allow subresource filter use." translateable="false">
       Allow the Subresource Filter to perform filtering
     </message>
-    <message name="IDS_SUBRESOURCE_FILTER_BLOCK_RADIO" desc="A radio button on the Content Settings dialog to block subresource filter use on all sites.">
+    <message name="IDS_SUBRESOURCE_FILTER_BLOCK_RADIO" desc="A radio button on the Content Settings dialog to block subresource filter use on all sites." translateable="false">
       Do not allow Subresource Filter to perform filtering
     </message>
     <if expr="is_win">
diff --git a/chrome/browser/apps/guest_view/web_view_browsertest.cc b/chrome/browser/apps/guest_view/web_view_browsertest.cc
index 1ae79da2..571a7e2 100644
--- a/chrome/browser/apps/guest_view/web_view_browsertest.cc
+++ b/chrome/browser/apps/guest_view/web_view_browsertest.cc
@@ -1518,7 +1518,6 @@
   TestHelper("testContentLoadEvent", "web_view/shim", NO_TEST_SERVER);
 }
 
-// TODO(fsamuel): Enable this test once <webview> can run in a detached state.
 IN_PROC_BROWSER_TEST_P(WebViewTest, Shim_TestContentLoadEventWithDisplayNone) {
   TestHelper("testContentLoadEventWithDisplayNone",
              "web_view/shim",
diff --git a/chrome/browser/net/errorpage_browsertest.cc b/chrome/browser/net/errorpage_browsertest.cc
index b6eca091..a328daa 100644
--- a/chrome/browser/net/errorpage_browsertest.cc
+++ b/chrome/browser/net/errorpage_browsertest.cc
@@ -94,11 +94,21 @@
 // space handling may be weird.
 bool WARN_UNUSED_RESULT IsDisplayingText(Browser* browser,
                                          const std::string& text) {
-  std::string command = base::StringPrintf(
-      "var textContent = document.body.innerText.toLowerCase();"
-      "var hasText = textContent.indexOf('%s'.toLowerCase()) >= 0;"
-      "domAutomationController.send(hasText);",
-      text.c_str());
+  // clang-format off
+  std::string command = base::StringPrintf(R"(
+    function isNodeVisible(node) {
+      if (!node || node.classList.contains('hidden'))
+        return false;
+      if (!node.parentElement)
+        return true;
+      // Otherwise, we must check all parent nodes
+      return isNodeVisible(node.parentElement);
+    }
+    var node = document.evaluate("//*[contains(text(),'%s')]", document,
+      null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;
+    domAutomationController.send(isNodeVisible(node));
+  )", text.c_str());
+  // clang-format on
   bool result = false;
   EXPECT_TRUE(content::ExecuteScriptAndExtractBool(
       browser->tab_strip_model()->GetActiveWebContents(), command, &result));
@@ -1120,8 +1130,9 @@
   EXPECT_EQ(2, interceptor()->requests());
 
   ToggleHelpBox(browser());
-  EXPECT_TRUE(IsDisplayingText(browser(), l10n_util::GetStringUTF8(
-      IDS_ERRORPAGES_SUGGESTION_CHECK_CONNECTION_HEADER)));
+  EXPECT_TRUE(IsDisplayingText(
+      browser(), l10n_util::GetStringUTF8(
+                     IDS_ERRORPAGES_SUGGESTION_CHECK_CONNECTION_HEADER)));
 
   content::WebContents* web_contents =
     browser()->tab_strip_model()->GetActiveWebContents();
@@ -1129,8 +1140,9 @@
   web_contents->GetMainFrame()->ExecuteJavaScriptForTests(
       base::ASCIIToUTF16("document.getElementById('reload-button').click();"));
   nav_observer.Wait();
-  EXPECT_FALSE(IsDisplayingText(browser(), l10n_util::GetStringUTF8(
-      IDS_ERRORPAGES_SUGGESTION_CHECK_CONNECTION_HEADER)));
+  EXPECT_FALSE(IsDisplayingText(
+      browser(), l10n_util::GetStringUTF8(
+                     IDS_ERRORPAGES_SUGGESTION_CHECK_CONNECTION_HEADER)));
 }
 
 // Make sure that a same document navigation does not cause issues with the
diff --git a/chrome/browser/resources/bookmark_manager/js/compiled_resources.gyp b/chrome/browser/resources/bookmark_manager/js/compiled_resources.gyp
deleted file mode 100644
index 9a71933..0000000
--- a/chrome/browser/resources/bookmark_manager/js/compiled_resources.gyp
+++ /dev/null
@@ -1,54 +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.
-{
-  'targets': [
-    {
-      'target_name': 'main',
-      'variables': {
-        'depends': [
-          '../../../../../third_party/jstemplate/compiled_resources.gyp:jstemplate',
-          '../../../../../ui/webui/resources/js/cr.js',
-          '../../../../../ui/webui/resources/js/cr/event_target.js',
-          '../../../../../ui/webui/resources/js/cr/link_controller.js',
-          '../../../../../ui/webui/resources/js/cr/ui.js',
-          '../../../../../ui/webui/resources/js/cr/ui/array_data_model.js',
-          '../../../../../ui/webui/resources/js/cr/ui/command.js',
-          '../../../../../ui/webui/resources/js/cr/ui/context_menu_button.js',
-          '../../../../../ui/webui/resources/js/cr/ui/context_menu_handler.js',
-          '../../../../../ui/webui/resources/js/cr/ui/focus_outline_manager.js',
-          '../../../../../ui/webui/resources/js/cr/ui/list.js',
-          '../../../../../ui/webui/resources/js/cr/ui/list_item.js',
-          '../../../../../ui/webui/resources/js/cr/ui/list_selection_controller.js',
-          '../../../../../ui/webui/resources/js/cr/ui/list_selection_model.js',
-          '../../../../../ui/webui/resources/js/cr/ui/menu.js',
-          '../../../../../ui/webui/resources/js/cr/ui/menu_button.js',
-          '../../../../../ui/webui/resources/js/cr/ui/menu_item.js',
-          '../../../../../ui/webui/resources/js/cr/ui/position_util.js',
-          '../../../../../ui/webui/resources/js/cr/ui/splitter.js',
-          '../../../../../ui/webui/resources/js/cr/ui/touch_handler.js',
-          '../../../../../ui/webui/resources/js/cr/ui/tree.js',
-          '../../../../../ui/webui/resources/js/event_tracker.js',
-          '../../../../../ui/webui/resources/js/compiled_resources.gyp:i18n_template_no_process',
-          '../../../../../ui/webui/resources/js/load_time_data.js',
-          '../../../../../ui/webui/resources/js/promise_resolver.js',
-          '../../../../../ui/webui/resources/js/util.js',
-          '../../../../../ui/webui/resources/js/icon.js',
-          '../../../../../chrome/browser/resources/bookmark_manager/js/bmm.js',
-          '../../../../../chrome/browser/resources/bookmark_manager/js/bmm/bookmark_list.js',
-          '../../../../../chrome/browser/resources/bookmark_manager/js/bmm/bookmark_tree.js',
-          '../../../../../chrome/browser/resources/bookmark_manager/js/dnd.js',
-        ],
-        'externs': [
-          '<(EXTERNS_DIR)/bookmark_manager_private.js',
-          '<(EXTERNS_DIR)/chrome_send.js',
-          '<(EXTERNS_DIR)/chrome_extensions.js',
-          '<(EXTERNS_DIR)/metrics_private.js',
-          '<(EXTERNS_DIR)/system_private.js',
-          '../../../../../ui/webui/resources/js/template_data_externs.js',
-        ],
-      },
-      'includes': ['../../../../../third_party/closure_compiler/compile_js.gypi'],
-    }
-  ],
-}
diff --git a/chrome/browser/resources/bookmark_manager/js/compiled_resources2.gyp b/chrome/browser/resources/bookmark_manager/js/compiled_resources2.gyp
new file mode 100644
index 0000000..30dce813
--- /dev/null
+++ b/chrome/browser/resources/bookmark_manager/js/compiled_resources2.gyp
@@ -0,0 +1,57 @@
+# 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.
+{
+  'targets': [
+    {
+      'target_name': 'bookmark_manager',
+      'variables': {
+        'script_args': ['--custom_sources'],
+        'source_files': [
+          '<(DEPTH)/third_party/jstemplate/util.js',
+          '<(DEPTH)/ui/webui/resources/js/assert.js',
+          '<(DEPTH)/third_party/jstemplate/jsevalcontext.js',
+          '<(DEPTH)/third_party/jstemplate/jstemplate.js',
+          '<(DEPTH)/ui/webui/resources/js/cr.js',
+          '<(DEPTH)/ui/webui/resources/js/cr/event_target.js',
+          '<(DEPTH)/ui/webui/resources/js/cr/link_controller.js',
+          '<(DEPTH)/ui/webui/resources/js/cr/ui.js',
+          '<(DEPTH)/ui/webui/resources/js/cr/ui/array_data_model.js',
+          '<(DEPTH)/ui/webui/resources/js/cr/ui/command.js',
+          '<(DEPTH)/ui/webui/resources/js/cr/ui/context_menu_button.js',
+          '<(DEPTH)/ui/webui/resources/js/cr/ui/context_menu_handler.js',
+          '<(DEPTH)/ui/webui/resources/js/cr/ui/focus_outline_manager.js',
+          '<(DEPTH)/ui/webui/resources/js/cr/ui/list.js',
+          '<(DEPTH)/ui/webui/resources/js/cr/ui/list_item.js',
+          '<(DEPTH)/ui/webui/resources/js/cr/ui/list_selection_controller.js',
+          '<(DEPTH)/ui/webui/resources/js/cr/ui/list_selection_model.js',
+          '<(DEPTH)/ui/webui/resources/js/cr/ui/menu.js',
+          '<(DEPTH)/ui/webui/resources/js/cr/ui/menu_button.js',
+          '<(DEPTH)/ui/webui/resources/js/cr/ui/menu_item.js',
+          '<(DEPTH)/ui/webui/resources/js/cr/ui/position_util.js',
+          '<(DEPTH)/ui/webui/resources/js/cr/ui/splitter.js',
+          '<(DEPTH)/ui/webui/resources/js/cr/ui/touch_handler.js',
+          '<(DEPTH)/ui/webui/resources/js/cr/ui/tree.js',
+          '<(DEPTH)/ui/webui/resources/js/event_tracker.js',
+          '<(DEPTH)/ui/webui/resources/js/i18n_template_no_process.js',
+          '<(DEPTH)/ui/webui/resources/js/load_time_data.js',
+          '<(DEPTH)/ui/webui/resources/js/promise_resolver.js',
+          '<(DEPTH)/ui/webui/resources/js/util.js',
+          '<(DEPTH)/ui/webui/resources/js/icon.js',
+          '<(DEPTH)/chrome/browser/resources/bookmark_manager/js/bmm.js',
+          '<(DEPTH)/chrome/browser/resources/bookmark_manager/js/bmm/bookmark_list.js',
+          '<(DEPTH)/chrome/browser/resources/bookmark_manager/js/bmm/bookmark_tree.js',
+          '<(DEPTH)/chrome/browser/resources/bookmark_manager/js/dnd.js',
+          '<(DEPTH)/chrome/browser/resources/bookmark_manager/js/main.js',
+          '<(DEPTH)/third_party/closure_compiler/externs/bookmark_manager_private.js',
+          '<(DEPTH)/third_party/closure_compiler/externs//chrome_send.js',
+          '<(DEPTH)/third_party/closure_compiler/externs//chrome_extensions.js',
+          '<(DEPTH)/third_party/closure_compiler/externs//metrics_private.js',
+          '<(DEPTH)/third_party/closure_compiler/externs//system_private.js',
+          '<(DEPTH)/ui/webui/resources/js/template_data_externs.js',
+        ],
+      },
+      'includes': ['../../../../../third_party/closure_compiler/compile_js2.gypi'],
+    }
+  ],
+}
diff --git a/chrome/browser/resources/print_preview/native_layer.js b/chrome/browser/resources/print_preview/native_layer.js
index 19c5a532..ce994f02 100644
--- a/chrome/browser/resources/print_preview/native_layer.js
+++ b/chrome/browser/resources/print_preview/native_layer.js
@@ -277,10 +277,11 @@
      * @param {!print_preview.PrintTicketStore} printTicketStore Used to get the
      *     state of the print ticket.
      * @param {!print_preview.DocumentInfo} documentInfo Document data model.
+     * @param {boolean} generateDraft Tell the renderer to re-render.
      * @param {number} requestId ID of the preview request.
      */
     startGetPreview: function(
-        destination, printTicketStore, documentInfo, requestId) {
+        destination, printTicketStore, documentInfo, generateDraft, requestId) {
       assert(printTicketStore.isTicketValidForPreview(),
              'Trying to generate preview when ticket is not valid');
 
@@ -294,7 +295,7 @@
         'isFirstRequest': requestId == 0,
         'requestID': requestId,
         'previewModifiable': documentInfo.isModifiable,
-        'generateDraftData': documentInfo.isModifiable,
+        'generateDraftData': generateDraft,
         'fitToPageEnabled': printTicketStore.fitToPage.getValue(),
         'scaleFactor': printTicketStore.scaling.getValueAsNumber(),
         // NOTE: Even though the following fields don't directly relate to the
@@ -341,8 +342,7 @@
       chrome.send(
           'getPreview',
           [JSON.stringify(ticket),
-           requestId > 0 ? documentInfo.pageCount : -1,
-           documentInfo.isModifiable]);
+           requestId > 0 ? documentInfo.pageCount : -1]);
     },
 
     /**
diff --git a/chrome/browser/resources/print_preview/preview_generator.js b/chrome/browser/resources/print_preview/preview_generator.js
index 6a955e05..cea7187 100644
--- a/chrome/browser/resources/print_preview/preview_generator.js
+++ b/chrome/browser/resources/print_preview/preview_generator.js
@@ -58,6 +58,14 @@
     this.inFlightRequestId_ = -1;
 
     /**
+     * Whether the current in flight request requires generating draft pages for
+     * print preview. This is true only for modifiable documents when the print
+     * settings has changed sufficiently to require re-rendering.
+     * @private {boolean}
+     */
+    this.generateDraft_ = false;
+
+    /**
      * Media size to generate preview with. {@code null} indicates default size.
      * @type {cp.cdd.MediaSizeTicketItem}
      * @private
@@ -173,7 +181,8 @@
           !this.destinationStore_.selectedDestination) {
         return false;
       }
-      if (!this.hasPreviewChanged_()) {
+      var previewChanged = this.hasPreviewChanged_();
+      if (!previewChanged && !this.hasPreviewPageRangeChanged_()) {
         // Changes to these ticket items might not trigger a new preview, but
         // they still need to be recorded.
         this.marginsType_ = this.printTicketStore_.marginsType.getValue();
@@ -195,10 +204,12 @@
       this.selectedDestination_ = this.destinationStore_.selectedDestination;
 
       this.inFlightRequestId_++;
+      this.generateDraft_ = this.documentInfo_.isModifiable && previewChanged;
       this.nativeLayer_.startGetPreview(
           this.destinationStore_.selectedDestination,
           this.printTicketStore_,
           this.documentInfo_,
+          this.generateDraft_,
           this.inFlightRequestId_);
       return true;
     },
@@ -273,8 +284,9 @@
     },
 
     /**
-     * @return {boolean} Whether the print ticket has changed sufficiently to
-     *     determine whether a new preview request should be issued.
+     * @return {boolean} Whether the print ticket, excluding the page range, has
+     *     changed sufficiently to determine whether a new preview request
+     *     should be issued.
      * @private
      */
     hasPreviewChanged_: function() {
@@ -286,9 +298,6 @@
           !ticketStore.color.isValueEqual(this.colorValue_) ||
           !ticketStore.scaling.isValueEqual(this.scalingValue_) ||
           !ticketStore.fitToPage.isValueEqual(this.isFitToPageEnabled_) ||
-          this.pageRanges_ == null ||
-          !areRangesEqual(ticketStore.pageRange.getPageRanges(),
-                          this.pageRanges_) ||
           (!ticketStore.marginsType.isValueEqual(this.marginsType_) &&
               !ticketStore.marginsType.isValueEqual(
                   print_preview.ticket_items.MarginsTypeValue.CUSTOM)) ||
@@ -305,6 +314,17 @@
     },
 
     /**
+     * @return {boolean} Whether the page range in the print ticket has changed.
+     * @private
+     */
+    hasPreviewPageRangeChanged_: function() {
+      return this.pageRanges_ == null ||
+          !areRangesEqual(
+              this.printTicketStore_.pageRange.getPageRanges(),
+              this.pageRanges_);
+    },
+
+    /**
      * Called when the page layout of the document is ready. Always occurs
      * as a result of a preview request.
      * @param {Event} event Contains layout info about the document.
@@ -387,9 +407,10 @@
       if (this.inFlightRequestId_ != event.previewResponseId) {
         return; // Ignore old response.
       }
-      // Dispatch a PREVIEW_START event since non-modifiable documents don't
-      // trigger PAGE_READY events.
-      if (!this.documentInfo_.isModifiable) {
+      if (!this.generateDraft_) {
+        // Dispatch a PREVIEW_START event since not generating a draft PDF,
+        // which includes print preview for non-modifiable documents, does not
+        // trigger PAGE_READY events.
         this.dispatchPreviewStartEvent_(event.previewUid, 0);
       }
       cr.dispatchSimpleEvent(this, PreviewGenerator.EventType.DOCUMENT_READY);
diff --git a/chrome/browser/ui/ash/system_tray_delegate_chromeos.cc b/chrome/browser/ui/ash/system_tray_delegate_chromeos.cc
index d7017a8..9559179 100644
--- a/chrome/browser/ui/ash/system_tray_delegate_chromeos.cc
+++ b/chrome/browser/ui/ash/system_tray_delegate_chromeos.cc
@@ -104,7 +104,8 @@
   // Register notifications on construction so that events such as
   // PROFILE_CREATED do not get missed if they happen before Initialize().
   registrar_.reset(new content::NotificationRegistrar);
-  if (GetUserLoginStatus() == ash::LoginStatus::NOT_LOGGED_IN) {
+  if (SystemTrayClient::GetUserLoginStatus() ==
+      ash::LoginStatus::NOT_LOGGED_IN) {
     registrar_->Add(this,
                     chrome::NOTIFICATION_SESSION_STARTED,
                     content::NotificationService::AllSources());
@@ -164,10 +165,6 @@
   StopObservingAppWindowRegistry();
 }
 
-ash::LoginStatus SystemTrayDelegateChromeOS::GetUserLoginStatus() const {
-  return SystemTrayClient::GetUserLoginStatus();
-}
-
 void SystemTrayDelegateChromeOS::ShowUserLogin() {
   if (!ash::Shell::Get()->shell_delegate()->IsMultiProfilesEnabled())
     return;
diff --git a/chrome/browser/ui/ash/system_tray_delegate_chromeos.h b/chrome/browser/ui/ash/system_tray_delegate_chromeos.h
index 90959f8f..0bf46f43 100644
--- a/chrome/browser/ui/ash/system_tray_delegate_chromeos.h
+++ b/chrome/browser/ui/ash/system_tray_delegate_chromeos.h
@@ -47,7 +47,6 @@
 
   // Overridden from ash::SystemTrayDelegate:
   void Initialize() override;
-  ash::LoginStatus GetUserLoginStatus() const override;
   void ShowUserLogin() override;
   void GetCurrentIME(ash::IMEInfo* info) override;
   void GetAvailableIMEList(ash::IMEInfoList* list) override;
diff --git a/chrome/browser/ui/autofill/save_card_bubble_controller_impl.cc b/chrome/browser/ui/autofill/save_card_bubble_controller_impl.cc
index 186d083f..b963de5 100644
--- a/chrome/browser/ui/autofill/save_card_bubble_controller_impl.cc
+++ b/chrome/browser/ui/autofill/save_card_bubble_controller_impl.cc
@@ -50,6 +50,10 @@
 void SaveCardBubbleControllerImpl::ShowBubbleForLocalSave(
     const CreditCard& card,
     const base::Closure& save_card_callback) {
+  // Don't show the bubble if it's already visible.
+  if (save_card_bubble_view_)
+    return;
+
   is_uploading_ = false;
   is_reshow_ = false;
   should_cvc_be_requested_ = false;
@@ -71,6 +75,10 @@
     std::unique_ptr<base::DictionaryValue> legal_message,
     bool should_cvc_be_requested,
     const base::Closure& save_card_callback) {
+  // Don't show the bubble if it's already visible.
+  if (save_card_bubble_view_)
+    return;
+
   is_uploading_ = true;
   is_reshow_ = false;
   should_cvc_be_requested_ = should_cvc_be_requested;
@@ -102,6 +110,10 @@
 }
 
 void SaveCardBubbleControllerImpl::ReshowBubble() {
+  // Don't show the bubble if it's already visible.
+  if (save_card_bubble_view_)
+    return;
+
   is_reshow_ = true;
   AutofillMetrics::LogSaveCardPromptMetric(
       AutofillMetrics::SAVE_CARD_PROMPT_SHOW_REQUESTED, is_uploading_,
diff --git a/chrome/browser/ui/autofill/save_card_bubble_controller_impl_unittest.cc b/chrome/browser/ui/autofill/save_card_bubble_controller_impl_unittest.cc
index c149d8c..34c55bf1 100644
--- a/chrome/browser/ui/autofill/save_card_bubble_controller_impl_unittest.cc
+++ b/chrome/browser/ui/autofill/save_card_bubble_controller_impl_unittest.cc
@@ -467,4 +467,60 @@
                  1)));
 }
 
+TEST_F(SaveCardBubbleControllerImplTest, OnlyOneActiveBubble_RepeatedLocal) {
+  base::HistogramTester histogram_tester;
+  ShowLocalBubble();
+  ShowLocalBubble();
+  ShowLocalBubble();
+  EXPECT_THAT(
+      histogram_tester.GetAllSamples(
+          "Autofill.SaveCreditCardPrompt.Local.FirstShow"),
+      ElementsAre(Bucket(AutofillMetrics::SAVE_CARD_PROMPT_SHOW_REQUESTED, 1),
+                  Bucket(AutofillMetrics::SAVE_CARD_PROMPT_SHOWN, 1)));
+}
+
+TEST_F(SaveCardBubbleControllerImplTest, OnlyOneActiveBubble_RepeatedUpload) {
+  base::HistogramTester histogram_tester;
+  ShowUploadBubble();
+  ShowUploadBubble();
+  ShowUploadBubble();
+  EXPECT_THAT(
+      histogram_tester.GetAllSamples(
+          "Autofill.SaveCreditCardPrompt.Upload.FirstShow"),
+      ElementsAre(Bucket(AutofillMetrics::SAVE_CARD_PROMPT_SHOW_REQUESTED, 1),
+                  Bucket(AutofillMetrics::SAVE_CARD_PROMPT_SHOWN, 1)));
+}
+
+TEST_F(SaveCardBubbleControllerImplTest, OnlyOneActiveBubble_LocalThenUpload) {
+  base::HistogramTester histogram_tester;
+  ShowLocalBubble();
+  ShowUploadBubble();
+  ShowUploadBubble();
+  EXPECT_THAT(
+      histogram_tester.GetAllSamples(
+          "Autofill.SaveCreditCardPrompt.Local.FirstShow"),
+      ElementsAre(Bucket(AutofillMetrics::SAVE_CARD_PROMPT_SHOW_REQUESTED, 1),
+                  Bucket(AutofillMetrics::SAVE_CARD_PROMPT_SHOWN, 1)));
+  EXPECT_TRUE(
+      histogram_tester
+          .GetAllSamples("Autofill.SaveCreditCardPrompt.Upload.FirstShow")
+          .empty());
+}
+
+TEST_F(SaveCardBubbleControllerImplTest, OnlyOneActiveBubble_UploadThenLocal) {
+  base::HistogramTester histogram_tester;
+  ShowUploadBubble();
+  ShowLocalBubble();
+  ShowLocalBubble();
+  EXPECT_THAT(
+      histogram_tester.GetAllSamples(
+          "Autofill.SaveCreditCardPrompt.Upload.FirstShow"),
+      ElementsAre(Bucket(AutofillMetrics::SAVE_CARD_PROMPT_SHOW_REQUESTED, 1),
+                  Bucket(AutofillMetrics::SAVE_CARD_PROMPT_SHOWN, 1)));
+  EXPECT_TRUE(
+      histogram_tester
+          .GetAllSamples("Autofill.SaveCreditCardPrompt.Local.FirstShow")
+          .empty());
+}
+
 }  // namespace autofill
diff --git a/chrome/browser/ui/cocoa/browser_window_controller_private.mm b/chrome/browser/ui/cocoa/browser_window_controller_private.mm
index ce0e044..5883198 100644
--- a/chrome/browser/ui/cocoa/browser_window_controller_private.mm
+++ b/chrome/browser/ui/cocoa/browser_window_controller_private.mm
@@ -1015,6 +1015,21 @@
     return;
   }
 
+  // Removing the location bar from the window causes it to resign first
+  // responder. Remember the location bar's focus state in order to restore
+  // it before returning.
+  BOOL locationBarHadFocus = [toolbarController_ locationBarHasFocus];
+  FullscreenToolbarVisibilityLockController* visibilityLockController = nil;
+  if (locationBarHadFocus) {
+    // The location bar, by being focused, has a visibility lock on the toolbar,
+    // and the location bar's removal from the view hierarchy will allow the
+    // toolbar to hide. Create a temporary visibility lock on the toolbar for
+    // the duration of the view hierarchy change.
+    visibilityLockController = [self fullscreenToolbarVisibilityLockController];
+    [visibilityLockController lockToolbarVisibilityForOwner:self
+                                              withAnimation:NO];
+  }
+
   // Remove all subviews that aren't the tabContentArea.
   for (NSView* view in [[[self.chromeContentView subviews] copy] autorelease]) {
     if (view != tabContentArea)
@@ -1037,6 +1052,14 @@
                             positioned:NSWindowAbove
                             relativeTo:nil];
   }
+
+  // Restore the location bar's focus state and remove the temporary visibility
+  // lock.
+  if (locationBarHadFocus) {
+    [self focusLocationBar:YES];
+    [visibilityLockController releaseToolbarVisibilityForOwner:self
+                                                 withAnimation:NO];
+  }
 }
 
 + (BOOL)systemSettingsRequireMavericksAppKitFullscreenHack {
diff --git a/chrome/browser/ui/cocoa/browser_window_controller_unittest.mm b/chrome/browser/ui/cocoa/browser_window_controller_unittest.mm
index d89b541..dc61742 100644
--- a/chrome/browser/ui/cocoa/browser_window_controller_unittest.mm
+++ b/chrome/browser/ui/cocoa/browser_window_controller_unittest.mm
@@ -8,12 +8,13 @@
 
 #include "base/mac/mac_util.h"
 #import "base/mac/scoped_nsobject.h"
+#import "base/mac/scoped_objc_class_swizzler.h"
 #include "chrome/app/chrome_command_ids.h"
 #include "chrome/browser/chrome_notification_types.h"
 #include "chrome/browser/ui/browser_list.h"
 #include "chrome/browser/ui/browser_window.h"
-#include "chrome/browser/ui/cocoa/find_bar/find_bar_bridge.h"
 #import "chrome/browser/ui/cocoa/fast_resize_view.h"
+#include "chrome/browser/ui/cocoa/find_bar/find_bar_bridge.h"
 #include "chrome/browser/ui/cocoa/tabs/tab_strip_view.h"
 #include "chrome/browser/ui/cocoa/test/cocoa_profile_test.h"
 #include "chrome/browser/ui/cocoa/test/run_loop_testing.h"
@@ -43,6 +44,7 @@
 - (NSView*)toolbarView;
 - (NSView*)bookmarkView;
 - (BOOL)bookmarkBarVisible;
+- (void)dontFocusLocationBar:(BOOL)selectAll;
 @end
 
 @implementation BrowserWindowController (ExposedForTesting)
@@ -65,6 +67,9 @@
 - (BOOL)bookmarkBarVisible {
   return [bookmarkBarController_ isVisible];
 }
+
+- (void)dontFocusLocationBar:(BOOL)selectAll {
+}
 @end
 
 class BrowserWindowControllerTest : public CocoaProfileTest {
@@ -808,6 +813,20 @@
   [controller_ showWindow:nil];
   EXPECT_FALSE([controller_ isInAnyFullscreenMode]);
 
+  // The fix for http://crbug.com/447740 , where the omnibox would lose focus
+  // when switching between normal and fullscreen modes, makes some changes to
+  // -[BrowserWindowController setContentViewSubviews:]. Those changes appear
+  // to have extended the lifetime of the browser window during this test -
+  // specifically, the browser window is no longer visible, but it has not been
+  // fully freed (possibly being kept around by a reference from the
+  // autocompleteTextView). As a result the window still appears in
+  // -[NSApplication windows] and causes the test to fail. To get around this
+  // problem, I disable -[BrowserWindowController focusLocationBar:] and later
+  // force the window to clear its first responder.
+  base::mac::ScopedObjCClassSwizzler tmpSwizzler(
+      [BrowserWindowController class], @selector(focusLocationBar:),
+      @selector(dontFocusLocationBar:));
+
   [controller_ enterBrowserFullscreen];
   WaitForFullScreenTransition();
   EXPECT_TRUE([controller_ isInAnyFullscreenMode]);
@@ -815,6 +834,8 @@
   [controller_ exitAnyFullscreen];
   WaitForFullScreenTransition();
   EXPECT_FALSE([controller_ isInAnyFullscreenMode]);
+
+  [[controller_ window] makeFirstResponder:nil];
 }
 
 // If this test fails, it is usually a sign that the bots have some sort of
@@ -832,6 +853,12 @@
   chrome::testing::NSRunLoopRunAllPending();
   EXPECT_TRUE(IsFrontWindow([controller_ window]));
 
+  // See the comment in TestFullscreen for an explanation of this
+  // swizzling and the makeFirstResponder:nil call below.
+  base::mac::ScopedObjCClassSwizzler tmpSwizzler(
+      [BrowserWindowController class], @selector(focusLocationBar:),
+      @selector(dontFocusLocationBar:));
+
   [controller_ enterBrowserFullscreen];
   WaitForFullScreenTransition();
   [controller_ activate];
@@ -840,6 +867,8 @@
   // We have to cleanup after ourselves by unfullscreening.
   [controller_ exitAnyFullscreen];
   WaitForFullScreenTransition();
+
+  [[controller_ window] makeFirstResponder:nil];
 }
 
 @implementation BrowserWindowControllerFakeFullscreen
diff --git a/chrome/browser/ui/cocoa/location_bar/location_bar_decoration.mm b/chrome/browser/ui/cocoa/location_bar/location_bar_decoration.mm
index 58c2e23..a2ad4ef 100644
--- a/chrome/browser/ui/cocoa/location_bar/location_bar_decoration.mm
+++ b/chrome/browser/ui/cocoa/location_bar/location_bar_decoration.mm
@@ -16,10 +16,9 @@
 
 namespace {
 
-// Color values for the bubble decoration divider.
-const CGFloat kDividerAlpha = 38.0;
-const CGFloat kDividerGrayScale = 0.0;
-const CGFloat kDividerIncognitoGrayScale = 1.0;
+// Gray values for the bubble decoration divider.
+const CGFloat kDividerGray = 0xFFA6A6A6;
+const CGFloat kDividerGrayIncognito = 0xFFDFDFDF;
 
 // Color values for the hover and pressed background.
 const SkColor kHoverBackgroundColor = 0x14000000;
@@ -409,10 +408,10 @@
 
 NSColor* LocationBarDecoration::GetDividerColor(
     bool location_bar_is_dark) const {
-  CGFloat gray_scale =
-      location_bar_is_dark ? kDividerIncognitoGrayScale : kDividerGrayScale;
-  return
-      [NSColor colorWithCalibratedWhite:gray_scale alpha:kDividerAlpha / 255.0];
+  if (location_bar_is_dark) {
+    return skia::SkColorToSRGBNSColor(kDividerGrayIncognito);
+  }
+  return skia::SkColorToSRGBNSColor(kDividerGray);
 }
 
 const gfx::VectorIcon* LocationBarDecoration::GetMaterialVectorIcon() const {
diff --git a/chrome/browser/ui/cocoa/location_bar/security_state_bubble_decoration.mm b/chrome/browser/ui/cocoa/location_bar/security_state_bubble_decoration.mm
index 2a8efd6d..f3878f1 100644
--- a/chrome/browser/ui/cocoa/location_bar/security_state_bubble_decoration.mm
+++ b/chrome/browser/ui/cocoa/location_bar/security_state_bubble_decoration.mm
@@ -32,6 +32,9 @@
 // Padding between the label and icon/divider.
 CGFloat kLabelPadding = 4.0;
 
+// Divider height.
+CGFloat kDividerHeight = 16.0;
+
 // Inset for the background.
 const CGFloat kBackgroundYInset = 4.0;
 
@@ -232,15 +235,29 @@
 
     // Draw the divider.
     if (state() == DecorationMouseState::NONE && !active()) {
-      const CGFloat divider_x_position =
-          is_rtl ? NSMinX(decoration_frame) + DividerPadding()
-                 : NSMaxX(decoration_frame) - DividerPadding();
       NSBezierPath* line = [NSBezierPath bezierPath];
       [line setLineWidth:line_width];
-      [line moveToPoint:NSMakePoint(divider_x_position,
-                                    NSMinY(decoration_frame))];
-      [line lineToPoint:NSMakePoint(divider_x_position,
-                                    NSMaxY(decoration_frame))];
+      NSPoint divider_origin = NSZeroPoint;
+      divider_origin.x = is_rtl ? NSMinX(decoration_frame) + DividerPadding()
+                                : NSMaxX(decoration_frame) - DividerPadding();
+      // Screen pixels lay between integral coordinates in user space. If you
+      // draw a line from (16, 16) to (16, 32), Core Graphics maps that line to
+      // the pixels that lay along x=16.5. In order to achieve a line that
+      // appears to lay along x=16, CG will perform dithering. To get a crisp
+      // line, you have to specify x=16.5, so translating by 1/2 the 1-pixel
+      // line width creates the crisp line we want. We subtract to better center
+      // the line between the label and URL.
+      divider_origin.x -= line_width / 2.;
+      CGFloat divider_y_frame_offset =
+          (NSHeight(decoration_frame) - kDividerHeight) / 2.0;
+      divider_origin.y = NSMinY(decoration_frame) + divider_y_frame_offset;
+      // Adjust the divider origin by 1/2 a point to visually center the
+      // divider vertically on Retina.
+      if (line_width < 1) {
+        divider_origin.y -= 0.5;
+      }
+      [line moveToPoint:divider_origin];
+      [line relativeLineToPoint:NSMakePoint(0, kDividerHeight)];
 
       NSColor* divider_color = GetDividerColor(in_dark_mode);
       CGFloat divider_alpha =
diff --git a/chrome/browser/ui/cocoa/toolbar/toolbar_controller.h b/chrome/browser/ui/cocoa/toolbar/toolbar_controller.h
index 0cfae25..a99f014 100644
--- a/chrome/browser/ui/cocoa/toolbar/toolbar_controller.h
+++ b/chrome/browser/ui/cocoa/toolbar/toolbar_controller.h
@@ -120,6 +120,9 @@
 // Called by the |locationBar_| when it has been added to its window.
 - (void)locationBarWasAddedToWindow;
 
+// Return YES if the location bar is the first responder.
+- (BOOL)locationBarHasFocus;
+
 // Make the location bar the first responder, if possible.
 - (void)focusLocationBar:(BOOL)selectAll;
 
diff --git a/chrome/browser/ui/cocoa/toolbar/toolbar_controller.mm b/chrome/browser/ui/cocoa/toolbar/toolbar_controller.mm
index 473b4381..5146ca8 100644
--- a/chrome/browser/ui/cocoa/toolbar/toolbar_controller.mm
+++ b/chrome/browser/ui/cocoa/toolbar/toolbar_controller.mm
@@ -596,6 +596,10 @@
   locationBarView_->OnAddedToWindow();
 }
 
+- (BOOL)locationBarHasFocus {
+  return [autocompleteTextFieldEditor_ window] != nil;
+}
+
 - (void)focusLocationBar:(BOOL)selectAll {
   if (locationBarView_.get()) {
     locationBarView_->FocusLocation(selectAll ? true : false);
diff --git a/chrome/browser/ui/webui/print_preview/print_preview_handler.cc b/chrome/browser/ui/webui/print_preview/print_preview_handler.cc
index db11b37..0f0bde1 100644
--- a/chrome/browser/ui/webui/print_preview/print_preview_handler.cc
+++ b/chrome/browser/ui/webui/print_preview/print_preview_handler.cc
@@ -737,7 +737,7 @@
 }
 
 void PrintPreviewHandler::HandleGetPreview(const base::ListValue* args) {
-  DCHECK_EQ(3U, args->GetSize());
+  DCHECK_EQ(2U, args->GetSize());
   std::unique_ptr<base::DictionaryValue> settings = GetSettingsDictionary(args);
   if (!settings)
     return;
@@ -794,18 +794,20 @@
   DCHECK(success);
 
   if (!generate_draft_data) {
-    double draft_page_count_double = -1;
-    success = args->GetDouble(1, &draft_page_count_double);
-    DCHECK(success);
-    int draft_page_count = static_cast<int>(draft_page_count_double);
-
-    bool preview_modifiable = false;
-    success = args->GetBoolean(2, &preview_modifiable);
+    int page_count = -1;
+    success = args->GetInteger(1, &page_count);
     DCHECK(success);
 
-    if (draft_page_count != -1 && preview_modifiable &&
-        print_preview_ui()->GetAvailableDraftPageCount() != draft_page_count) {
-      settings->SetBoolean(printing::kSettingGenerateDraftData, true);
+    if (page_count != -1) {
+      bool preview_modifiable = false;
+      success = settings->GetBoolean(printing::kSettingPreviewModifiable,
+                                     &preview_modifiable);
+      DCHECK(success);
+
+      if (preview_modifiable &&
+          print_preview_ui()->GetAvailableDraftPageCount() != page_count) {
+        settings->SetBoolean(printing::kSettingGenerateDraftData, true);
+      }
     }
   }
 
diff --git a/chrome/common/extensions/api/_permission_features.json b/chrome/common/extensions/api/_permission_features.json
index 7f1f660..23d93030 100644
--- a/chrome/common/extensions/api/_permission_features.json
+++ b/chrome/common/extensions/api/_permission_features.json
@@ -279,7 +279,7 @@
   "enterprise.platformKeys": {
     "channel": "stable",
     "platforms": ["chromeos"],
-    "extension_types": ["extension", "legacy_packaged_app"],
+    "extension_types": ["extension", "platform_app", "legacy_packaged_app"],
     "location": "policy"
   },
   "enterprise.platformKeysPrivate": {
diff --git a/chrome/renderer/chrome_content_renderer_client.cc b/chrome/renderer/chrome_content_renderer_client.cc
index 77d68f04..041c18ce 100644
--- a/chrome/renderer/chrome_content_renderer_client.cc
+++ b/chrome/renderer/chrome_content_renderer_client.cc
@@ -625,7 +625,6 @@
 
 bool ChromeContentRendererClient::OverrideCreatePlugin(
     content::RenderFrame* render_frame,
-    WebLocalFrame* frame,
     const WebPluginParams& params,
     WebPlugin** plugin) {
   std::string orig_mime_type = params.mime_type.Utf8();
@@ -640,13 +639,15 @@
 #if BUILDFLAG(ENABLE_PLUGINS)
   ChromeViewHostMsg_GetPluginInfo_Output output;
   render_frame->Send(new ChromeViewHostMsg_GetPluginInfo(
-      render_frame->GetRoutingID(), url, frame->Top()->GetSecurityOrigin(),
-      orig_mime_type, &output));
-  *plugin = CreatePlugin(render_frame, frame, params, output);
+      render_frame->GetRoutingID(), url,
+      render_frame->GetWebFrame()->Top()->GetSecurityOrigin(), orig_mime_type,
+      &output));
+  *plugin = CreatePlugin(render_frame, params, output);
 #else  // !BUILDFLAG(ENABLE_PLUGINS)
   PluginUMAReporter::GetInstance()->ReportPluginMissing(orig_mime_type, url);
-  *plugin = NonLoadablePluginPlaceholder::CreateNotSupportedPlugin(
-                render_frame, frame, params)->plugin();
+  auto* placeholder = NonLoadablePluginPlaceholder::CreateNotSupportedPlugin(
+      render_frame, params);
+  *plugin = placeholder->plugin();
 
 #endif  // BUILDFLAG(ENABLE_PLUGINS)
   return true;
@@ -655,8 +656,9 @@
 WebPlugin* ChromeContentRendererClient::CreatePluginReplacement(
     content::RenderFrame* render_frame,
     const base::FilePath& plugin_path) {
-  return NonLoadablePluginPlaceholder::CreateErrorPlugin(render_frame,
-                                                         plugin_path)->plugin();
+  auto* placeholder = NonLoadablePluginPlaceholder::CreateErrorPlugin(
+      render_frame, plugin_path);
+  return placeholder->plugin();
 }
 
 void ChromeContentRendererClient::DeferMediaLoad(
@@ -683,9 +685,9 @@
 }
 
 #if BUILDFLAG(ENABLE_PLUGINS)
+// static
 WebPlugin* ChromeContentRendererClient::CreatePlugin(
     content::RenderFrame* render_frame,
-    WebLocalFrame* frame,
     const WebPluginParams& original_params,
     const ChromeViewHostMsg_GetPluginInfo_Output& output) {
   const WebPluginInfo& info = output.plugin;
@@ -704,15 +706,15 @@
       orig_mime_type == content::kBrowserPluginMimeType) {
     PluginUMAReporter::GetInstance()->ReportPluginMissing(orig_mime_type, url);
     placeholder = ChromePluginPlaceholder::CreateLoadableMissingPlugin(
-        render_frame, frame, original_params);
+        render_frame, original_params);
   } else {
     // TODO(bauerb): This should be in content/.
     WebPluginParams params(original_params);
-    for (size_t i = 0; i < info.mime_types.size(); ++i) {
-      if (info.mime_types[i].mime_type == actual_mime_type) {
-        AppendParams(info.mime_types[i].additional_param_names,
-                     info.mime_types[i].additional_param_values,
-                     &params.attribute_names, &params.attribute_values);
+    for (const auto& mime_type : info.mime_types) {
+      if (mime_type.mime_type == actual_mime_type) {
+        AppendParams(mime_type.additional_param_names,
+                     mime_type.additional_param_values, &params.attribute_names,
+                     &params.attribute_values);
         break;
       }
     }
@@ -738,13 +740,14 @@
       status = ChromeViewHostMsg_GetPluginInfo_Status::kAllowed;
     }
 
-    auto create_blocked_plugin = [&render_frame, &frame, &params, &info,
-                                  &identifier, &group_name](
-        int template_id, const base::string16& message) {
+    auto create_blocked_plugin = [&render_frame, &params, &info, &identifier,
+                                  &group_name](int template_id,
+                                               const base::string16& message) {
       return ChromePluginPlaceholder::CreateBlockedPlugin(
-          render_frame, frame, params, info, identifier, group_name,
-          template_id, message, PowerSaverInfo());
+          render_frame, params, info, identifier, group_name, template_id,
+          message, PowerSaverInfo());
     };
+    WebLocalFrame* frame = render_frame->GetWebFrame();
     switch (status) {
       case ChromeViewHostMsg_GetPluginInfo_Status::kNotFound: {
         NOTREACHED();
@@ -829,7 +832,7 @@
             !power_saver_info.poster_attribute.empty() ||
             power_saver_info.power_saver_enabled) {
           placeholder = ChromePluginPlaceholder::CreateBlockedPlugin(
-              render_frame, frame, params, info, identifier, group_name,
+              render_frame, params, info, identifier, group_name,
               power_saver_info.poster_attribute.empty()
                   ? IDR_BLOCKED_PLUGIN_HTML
                   : IDR_PLUGIN_POSTER_HTML,
@@ -847,7 +850,7 @@
         }
 
         // Same-origin and whitelisted-origin plugins skip the placeholder.
-        return render_frame->CreatePlugin(frame, info, params, nullptr);
+        return render_frame->CreatePlugin(info, params, nullptr);
       }
       case ChromeViewHostMsg_GetPluginInfo_Status::kDisabled: {
         PluginUMAReporter::GetInstance()->ReportPluginDisabled(orig_mime_type,
diff --git a/chrome/renderer/chrome_content_renderer_client.h b/chrome/renderer/chrome_content_renderer_client.h
index a2ed877..dd11c11a 100644
--- a/chrome/renderer/chrome_content_renderer_client.h
+++ b/chrome/renderer/chrome_content_renderer_client.h
@@ -111,7 +111,6 @@
   SkBitmap* GetSadPluginBitmap() override;
   SkBitmap* GetSadWebViewBitmap() override;
   bool OverrideCreatePlugin(content::RenderFrame* render_frame,
-                            blink::WebLocalFrame* frame,
                             const blink::WebPluginParams& params,
                             blink::WebPlugin** plugin) override;
   blink::WebPlugin* CreatePluginReplacement(
@@ -202,7 +201,6 @@
 #if BUILDFLAG(ENABLE_PLUGINS)
   static blink::WebPlugin* CreatePlugin(
       content::RenderFrame* render_frame,
-      blink::WebLocalFrame* frame,
       const blink::WebPluginParams& params,
       const ChromeViewHostMsg_GetPluginInfo_Output& output);
 #endif
diff --git a/chrome/renderer/plugins/chrome_plugin_placeholder.cc b/chrome/renderer/plugins/chrome_plugin_placeholder.cc
index 1ce8165..d2e557d 100644
--- a/chrome/renderer/plugins/chrome_plugin_placeholder.cc
+++ b/chrome/renderer/plugins/chrome_plugin_placeholder.cc
@@ -49,7 +49,7 @@
 using content::RenderView;
 
 namespace {
-const ChromePluginPlaceholder* g_last_active_menu = NULL;
+const ChromePluginPlaceholder* g_last_active_menu = nullptr;
 }  // namespace
 
 gin::WrapperInfo ChromePluginPlaceholder::kWrapperInfo = {
@@ -57,12 +57,10 @@
 
 ChromePluginPlaceholder::ChromePluginPlaceholder(
     content::RenderFrame* render_frame,
-    blink::WebLocalFrame* frame,
     const blink::WebPluginParams& params,
     const std::string& html_data,
     const base::string16& title)
     : plugins::LoadablePluginPlaceholder(render_frame,
-                                         frame,
                                          params,
                                          html_data),
       status_(ChromeViewHostMsg_GetPluginInfo_Status::kAllowed),
@@ -86,7 +84,6 @@
 // static
 ChromePluginPlaceholder* ChromePluginPlaceholder::CreateLoadableMissingPlugin(
     content::RenderFrame* render_frame,
-    blink::WebLocalFrame* frame,
     const blink::WebPluginParams& params) {
   const base::StringPiece template_html(
       ResourceBundle::GetSharedInstance().GetRawDataResource(
@@ -99,14 +96,13 @@
   std::string html_data = webui::GetI18nTemplateHtml(template_html, &values);
 
   // Will destroy itself when its WebViewPlugin is going away.
-  return new ChromePluginPlaceholder(render_frame, frame, params, html_data,
+  return new ChromePluginPlaceholder(render_frame, params, html_data,
                                      params.mime_type.Utf16());
 }
 
 // static
 ChromePluginPlaceholder* ChromePluginPlaceholder::CreateBlockedPlugin(
     content::RenderFrame* render_frame,
-    blink::WebLocalFrame* frame,
     const blink::WebPluginParams& params,
     const content::WebPluginInfo& info,
     const std::string& identifier,
@@ -120,8 +116,8 @@
   values.SetString("hide", l10n_util::GetStringUTF8(IDS_PLUGIN_HIDE));
   values.SetString(
       "pluginType",
-      frame->View()->MainFrame()->IsWebLocalFrame() &&
-              frame->View()->MainFrame()->GetDocument().IsPluginDocument()
+      render_frame->IsMainFrame() &&
+              render_frame->GetWebFrame()->GetDocument().IsPluginDocument()
           ? "document"
           : "embedded");
 
@@ -130,8 +126,8 @@
     values.SetString("baseurl", power_saver_info.base_url.spec());
 
     if (!power_saver_info.custom_poster_size.IsEmpty()) {
-      float zoom_factor =
-          blink::WebView::ZoomLevelToZoomFactor(frame->View()->ZoomLevel());
+      float zoom_factor = blink::WebView::ZoomLevelToZoomFactor(
+          render_frame->GetWebFrame()->View()->ZoomLevel());
       int width =
           roundf(power_saver_info.custom_poster_size.width() / zoom_factor);
       int height =
@@ -149,8 +145,8 @@
   std::string html_data = webui::GetI18nTemplateHtml(template_html, &values);
 
   // |blocked_plugin| will destroy itself when its WebViewPlugin is going away.
-  ChromePluginPlaceholder* blocked_plugin = new ChromePluginPlaceholder(
-      render_frame, frame, params, html_data, name);
+  ChromePluginPlaceholder* blocked_plugin =
+      new ChromePluginPlaceholder(render_frame, params, html_data, name);
 
   if (!power_saver_info.poster_attribute.empty())
     blocked_plugin->BlockForPowerSaverPoster();
@@ -230,21 +226,19 @@
 }
 
 void ChromePluginPlaceholder::PluginListChanged() {
-  if (!GetFrame() || !plugin())
+  if (!render_frame() || !plugin())
     return;
 
-  // Checking with GetFrame() is equivalent to checking render_frame().
-  DCHECK(render_frame());
-
   ChromeViewHostMsg_GetPluginInfo_Output output;
   std::string mime_type(GetPluginParams().mime_type.Utf8());
   render_frame()->Send(new ChromeViewHostMsg_GetPluginInfo(
       routing_id(), GURL(GetPluginParams().url),
-      GetFrame()->Top()->GetSecurityOrigin(), mime_type, &output));
+      render_frame()->GetWebFrame()->Top()->GetSecurityOrigin(), mime_type,
+      &output));
   if (output.status == status_)
     return;
   blink::WebPlugin* new_plugin = ChromeContentRendererClient::CreatePlugin(
-      render_frame(), GetFrame(), GetPluginParams(), output);
+      render_frame(), GetPluginParams(), output);
   ReplacePlugin(new_plugin);
   if (!new_plugin) {
     PluginUMAReporter::GetInstance()->ReportPluginMissing(
@@ -330,8 +324,8 @@
   content::MenuItem hide_item;
   hide_item.action = chrome::MENU_COMMAND_PLUGIN_HIDE;
   bool is_main_frame_plugin_document =
-      GetFrame()->View()->MainFrame()->IsWebLocalFrame() &&
-      GetFrame()->View()->MainFrame()->GetDocument().IsPluginDocument();
+      render_frame()->IsMainFrame() &&
+      render_frame()->GetWebFrame()->GetDocument().IsPluginDocument();
   hide_item.enabled = !is_main_frame_plugin_document;
   hide_item.label = l10n_util::GetStringUTF16(IDS_CONTENT_CONTEXT_PLUGIN_HIDE);
   params.custom_items.push_back(hide_item);
@@ -356,13 +350,13 @@
         heuristic_run_before_ ? content::RenderFrame::DONT_RECORD_DECISION
                               : content::RenderFrame::RECORD_DECISION);
     // PluginPreroller manages its own lifetime.
-    new PluginPreroller(render_frame(), GetFrame(), GetPluginParams(),
-                        GetPluginInfo(), GetIdentifier(), title_,
+    new PluginPreroller(render_frame(), GetPluginParams(), GetPluginInfo(),
+                        GetIdentifier(), title_,
                         l10n_util::GetStringFUTF16(IDS_PLUGIN_BLOCKED, title_),
                         throttler.get());
   }
-  return render_frame()->CreatePlugin(GetFrame(), GetPluginInfo(),
-                                      GetPluginParams(), std::move(throttler));
+  return render_frame()->CreatePlugin(GetPluginInfo(), GetPluginParams(),
+                                      std::move(throttler));
 }
 
 void ChromePluginPlaceholder::OnBlockedTinyContent() {
diff --git a/chrome/renderer/plugins/chrome_plugin_placeholder.h b/chrome/renderer/plugins/chrome_plugin_placeholder.h
index 5028d1d..3d62c5ce 100644
--- a/chrome/renderer/plugins/chrome_plugin_placeholder.h
+++ b/chrome/renderer/plugins/chrome_plugin_placeholder.h
@@ -28,7 +28,6 @@
 
   static ChromePluginPlaceholder* CreateBlockedPlugin(
       content::RenderFrame* render_frame,
-      blink::WebLocalFrame* frame,
       const blink::WebPluginParams& params,
       const content::WebPluginInfo& info,
       const std::string& identifier,
@@ -40,7 +39,6 @@
   // Creates a new WebViewPlugin with a MissingPlugin as a delegate.
   static ChromePluginPlaceholder* CreateLoadableMissingPlugin(
       content::RenderFrame* render_frame,
-      blink::WebLocalFrame* frame,
       const blink::WebPluginParams& params);
 
   void SetStatus(ChromeViewHostMsg_GetPluginInfo_Status status);
@@ -49,7 +47,6 @@
 
  private:
   ChromePluginPlaceholder(content::RenderFrame* render_frame,
-                          blink::WebLocalFrame* frame,
                           const blink::WebPluginParams& params,
                           const std::string& html_data,
                           const base::string16& title);
diff --git a/chrome/renderer/plugins/non_loadable_plugin_placeholder.cc b/chrome/renderer/plugins/non_loadable_plugin_placeholder.cc
index 38e4349..b6e550a 100644
--- a/chrome/renderer/plugins/non_loadable_plugin_placeholder.cc
+++ b/chrome/renderer/plugins/non_loadable_plugin_placeholder.cc
@@ -21,7 +21,6 @@
 plugins::PluginPlaceholder*
 NonLoadablePluginPlaceholder::CreateNotSupportedPlugin(
     content::RenderFrame* render_frame,
-    blink::WebLocalFrame* frame,
     const blink::WebPluginParams& params) {
   const base::StringPiece template_html(
       ResourceBundle::GetSharedInstance().GetRawDataResource(
@@ -34,7 +33,7 @@
   std::string html_data = webui::GetI18nTemplateHtml(template_html, &values);
 
   // PluginPlaceholder will destroy itself when its WebViewPlugin is going away.
-  return new plugins::PluginPlaceholder(render_frame, frame, params, html_data);
+  return new plugins::PluginPlaceholder(render_frame, params, html_data);
 }
 
 // static
@@ -53,7 +52,7 @@
   blink::WebPluginParams params;
   // PluginPlaceholder will destroy itself when its WebViewPlugin is going away.
   plugins::PluginPlaceholder* plugin =
-      new plugins::PluginPlaceholder(render_frame, nullptr, params, html_data);
+      new plugins::PluginPlaceholder(render_frame, params, html_data);
 
   content::RenderThread::Get()->Send(new ChromeViewHostMsg_CouldNotLoadPlugin(
       plugin->routing_id(), file_path));
diff --git a/chrome/renderer/plugins/non_loadable_plugin_placeholder.h b/chrome/renderer/plugins/non_loadable_plugin_placeholder.h
index e872e5e4..1b2c6d13 100644
--- a/chrome/renderer/plugins/non_loadable_plugin_placeholder.h
+++ b/chrome/renderer/plugins/non_loadable_plugin_placeholder.h
@@ -12,7 +12,6 @@
 }
 
 namespace blink {
-class WebLocalFrame;
 struct WebPluginParams;
 }
 
@@ -29,7 +28,6 @@
   // Creates a non-loadable plugin placeholder for platforms without plugins.
   static plugins::PluginPlaceholder* CreateNotSupportedPlugin(
       content::RenderFrame* render_frame,
-      blink::WebLocalFrame* frame,
       const blink::WebPluginParams& params);
 
   static plugins::PluginPlaceholder* CreateErrorPlugin(
diff --git a/chrome/renderer/plugins/plugin_preroller.cc b/chrome/renderer/plugins/plugin_preroller.cc
index 18f0269..c801a3b8 100644
--- a/chrome/renderer/plugins/plugin_preroller.cc
+++ b/chrome/renderer/plugins/plugin_preroller.cc
@@ -15,7 +15,6 @@
 #include "ui/gfx/codec/png_codec.h"
 
 PluginPreroller::PluginPreroller(content::RenderFrame* render_frame,
-                                 blink::WebLocalFrame* frame,
                                  const blink::WebPluginParams& params,
                                  const content::WebPluginInfo& info,
                                  const std::string& identifier,
@@ -23,7 +22,6 @@
                                  const base::string16& message,
                                  content::PluginInstanceThrottler* throttler)
     : RenderFrameObserver(render_frame),
-      frame_(frame),
       params_(params),
       info_(info),
       identifier_(identifier),
@@ -66,7 +64,7 @@
 
   ChromePluginPlaceholder* placeholder =
       ChromePluginPlaceholder::CreateBlockedPlugin(
-          render_frame(), frame_, params_, info_, identifier_, name_,
+          render_frame(), params_, info_, identifier_, name_,
           IDR_PLUGIN_POSTER_HTML, message_, power_saver_info);
   placeholder->SetPremadePlugin(throttler_);
   placeholder->AllowLoading();
diff --git a/chrome/renderer/plugins/plugin_preroller.h b/chrome/renderer/plugins/plugin_preroller.h
index 802e9c4..c6e8bfb 100644
--- a/chrome/renderer/plugins/plugin_preroller.h
+++ b/chrome/renderer/plugins/plugin_preroller.h
@@ -12,10 +12,6 @@
 #include "third_party/WebKit/public/web/WebPluginParams.h"
 #include "url/gurl.h"
 
-namespace blink {
-class WebLocalFrame;
-}
-
 class SkBitmap;
 
 // This class manages a plugin briefly for the purposes of keyframe extraction.
@@ -25,9 +21,8 @@
 class PluginPreroller : public content::PluginInstanceThrottler::Observer,
                         public content::RenderFrameObserver {
  public:
-  // Does not take ownership of either |plugin| or |throttler|.
+  // Does not take ownership of |render_frame|, |plugin|, or |throttler|.
   PluginPreroller(content::RenderFrame* render_frame,
-                  blink::WebLocalFrame* frame,
                   const blink::WebPluginParams& params,
                   const content::WebPluginInfo& info,
                   const std::string& identifier,
@@ -46,7 +41,6 @@
   // content::RenderFrameObserver implementation.
   void OnDestruct() override;
 
-  blink::WebLocalFrame* frame_;
   blink::WebPluginParams params_;
   content::WebPluginInfo info_;
   std::string identifier_;
diff --git a/chrome/test/data/webui/print_preview.js b/chrome/test/data/webui/print_preview.js
index 7d64f2e..81e518968 100644
--- a/chrome/test/data/webui/print_preview.js
+++ b/chrome/test/data/webui/print_preview.js
@@ -84,17 +84,22 @@
       function NativeLayerStub() {
         cr.EventTarget.call(this);
         this.printStarted_ = false;
+        this.generateDraft_ = false;
       }
       NativeLayerStub.prototype = {
         __proto__: cr.EventTarget.prototype,
         isPrintStarted: function() { return this.printStarted_; },
+        generateDraft: function() { return this.generateDraft_; },
         previewReadyForTest: function() {},
         startGetInitialSettings: function() {},
         startGetLocalDestinations: function() {},
         startGetPrivetDestinations: function() {},
         startGetExtensionDestinations: function() {},
         startGetLocalDestinationCapabilities: function(destinationId) {},
-        startGetPreview: function() {},
+        startGetPreview: function(destination, printTicketStore, documentInfo,
+                                  generateDraft, requestId) {
+          this.generateDraft_ = generateDraft;
+        },
         startHideDialog: function () {},
         startPrint: function () { this.printStarted_ = true; }
       };
@@ -177,14 +182,23 @@
   },
 
   /**
-   * @return {boolean} Whether the UI has/has not "printed" (called startPrint
-   *     on the native layer).
+   * @return {boolean} Whether the UI has "printed" or not. (called startPrint
+   *     on the native layer)
    */
   hasPrinted: function() {
     return this.nativeLayer_.isPrintStarted();
   },
 
   /**
+   * @return {boolean} Whether the UI is "generating draft" in the most recent
+   *     preview. (checking the result of the startGetPreview call in the native
+   *     layer)
+   */
+  generateDraft: function() {
+    return this.nativeLayer_.generateDraft();
+  },
+
+  /**
    * Even though animation duration and delay is set to zero, it is necessary to
    * wait until the animation has finished.
    */
@@ -1449,3 +1463,32 @@
   expectTrue(this.hasPrinted());
   testDone();
 });
+
+// Test the preview generator to make sure the generate draft parameter is set
+// correctly. It should be false if the only change is the page range.
+TEST_F('PrintPreviewWebUITest', 'TestGenerateDraft', function() {
+  // Use a real preview generator.
+  printPreview.previewArea_.previewGenerator_ =
+      new print_preview.PreviewGenerator(printPreview.destinationStore_,
+        printPreview.printTicketStore_, this.nativeLayer_,
+        printPreview.documentInfo_);
+
+  this.setInitialSettings();
+  this.setLocalDestinations();
+  this.setCapabilities(getCddTemplate("FooDevice"));
+
+  // The first request should generate draft because there was no previous print
+  // preview draft.
+  expectTrue(this.generateDraft());
+
+  // Change the page range - no new draft needed.
+  printPreview.printTicketStore_.pageRange.updateValue("2");
+  expectFalse(this.generateDraft());
+
+  // Change the margin type - need to regenerate again.
+  printPreview.printTicketStore_.marginsType.updateValue(
+      print_preview.ticket_items.MarginsTypeValue.NO_MARGINS);
+  expectTrue(this.generateDraft());
+
+  testDone();
+});
diff --git a/chrome/test/media_router/telemetry/benchmarks/pagesets/media_router_page.py b/chrome/test/media_router/telemetry/benchmarks/pagesets/media_router_page.py
index 23381ce6..a6c2cc7 100644
--- a/chrome/test/media_router/telemetry/benchmarks/pagesets/media_router_page.py
+++ b/chrome/test/media_router/telemetry/benchmarks/pagesets/media_router_page.py
@@ -37,11 +37,15 @@
           'window.document.getElementById("media-router-container").' +
           'shadowRoot.getElementById("container-header").shadowRoot.' +
           'getElementById("close-button").click();')
-    except exceptions.DevtoolsTargetCrashException:
+    except (exceptions.DevtoolsTargetCrashException,
+            exceptions.EvaluateException):
       # Ignore the crash exception, this exception is caused by the js
       # code which closes the dialog, it is expected.
+      # Ignore the evaluate exception, this exception maybe caused by the dialog
+      # is closed/closing when the JS is executing.
       pass
 
+
   def CloseExistingRoute(self, action_runner, sink_name):
     """Closes the existing route if it exists, otherwise does nothing."""
 
diff --git a/components/autofill/content/common/autofill_types_struct_traits_unittest.cc b/components/autofill/content/common/autofill_types_struct_traits_unittest.cc
index b60bff0..dba2fa6 100644
--- a/components/autofill/content/common/autofill_types_struct_traits_unittest.cc
+++ b/components/autofill/content/common/autofill_types_struct_traits_unittest.cc
@@ -93,7 +93,7 @@
   form->icon_url = GURL("https://foo.com/icon.png");
   form->federation_origin =
       url::Origin::UnsafelyCreateOriginWithoutNormalization(
-          "http", "www.google.com", 80);
+          "http", "www.google.com", 80, "");
   form->skip_zero_click = false;
   form->layout = PasswordForm::Layout::LAYOUT_LOGIN_AND_SIGNUP;
   form->was_parsed_using_autofill_predictions = false;
diff --git a/components/cronet/android/cronet_library_loader.cc b/components/cronet/android/cronet_library_loader.cc
index d31d6d2..4ac9eab 100644
--- a/components/cronet/android/cronet_library_loader.cc
+++ b/components/cronet/android/cronet_library_loader.cc
@@ -107,7 +107,9 @@
   base::CommandLine::Init(0, nullptr);
   DCHECK(!base::MessageLoop::current());
   DCHECK(!g_init_message_loop);
-  g_init_message_loop = new base::MessageLoop();
+  g_init_message_loop =
+      new base::MessageLoop(base::MessageLoop::Type::TYPE_JAVA);
+  static_cast<base::MessageLoopForUI*>(g_init_message_loop)->Start();
   DCHECK(!g_network_change_notifier);
   net::NetworkChangeNotifier::SetFactory(
       new net::NetworkChangeNotifierFactoryAndroid());
diff --git a/components/cronet/android/java/src/org/chromium/net/impl/CronetLibraryLoader.java b/components/cronet/android/java/src/org/chromium/net/impl/CronetLibraryLoader.java
index ecd1ef1..935240d9 100644
--- a/components/cronet/android/java/src/org/chromium/net/impl/CronetLibraryLoader.java
+++ b/components/cronet/android/java/src/org/chromium/net/impl/CronetLibraryLoader.java
@@ -110,7 +110,7 @@
     /**
      * Run {@code r} on the initialization thread.
      */
-    static void postToInitThread(Runnable r) {
+    public static void postToInitThread(Runnable r) {
         if (onInitThread()) {
             r.run();
         } else {
diff --git a/components/cronet/android/test/javatests/src/org/chromium/net/NetworkChangeNotifierTest.java b/components/cronet/android/test/javatests/src/org/chromium/net/NetworkChangeNotifierTest.java
index 6b3662e..b7113b5 100644
--- a/components/cronet/android/test/javatests/src/org/chromium/net/NetworkChangeNotifierTest.java
+++ b/components/cronet/android/test/javatests/src/org/chromium/net/NetworkChangeNotifierTest.java
@@ -12,8 +12,8 @@
 import android.support.test.filters.SmallTest;
 import android.system.Os;
 
-import org.chromium.base.ThreadUtils;
 import org.chromium.base.test.util.Feature;
+import org.chromium.net.impl.CronetLibraryLoader;
 
 import java.io.FileDescriptor;
 import java.net.InetAddress;
@@ -77,7 +77,7 @@
         }
 
         // Simulate network change which should abort connect jobs
-        ThreadUtils.postOnUiThread(new Runnable() {
+        CronetLibraryLoader.postToInitThread(new Runnable() {
             @Override
             public void run() {
                 NetworkChangeNotifier.getInstance().notifyObserversOfConnectionTypeChange(
diff --git a/components/plugins/renderer/loadable_plugin_placeholder.cc b/components/plugins/renderer/loadable_plugin_placeholder.cc
index caf085df..57678645 100644
--- a/components/plugins/renderer/loadable_plugin_placeholder.cc
+++ b/components/plugins/renderer/loadable_plugin_placeholder.cc
@@ -59,10 +59,9 @@
 
 LoadablePluginPlaceholder::LoadablePluginPlaceholder(
     RenderFrame* render_frame,
-    blink::WebLocalFrame* frame,
     const blink::WebPluginParams& params,
     const std::string& html_data)
-    : PluginPlaceholderBase(render_frame, frame, params, html_data),
+    : PluginPlaceholderBase(render_frame, params, html_data),
       heuristic_run_before_(false),
       is_blocked_for_tinyness_(false),
       is_blocked_for_background_tab_(false),
diff --git a/components/plugins/renderer/loadable_plugin_placeholder.h b/components/plugins/renderer/loadable_plugin_placeholder.h
index 2c74086..f4a0599 100644
--- a/components/plugins/renderer/loadable_plugin_placeholder.h
+++ b/components/plugins/renderer/loadable_plugin_placeholder.h
@@ -49,10 +49,8 @@
 
  protected:
   LoadablePluginPlaceholder(content::RenderFrame* render_frame,
-                            blink::WebLocalFrame* frame,
                             const blink::WebPluginParams& params,
                             const std::string& html_data);
-
   ~LoadablePluginPlaceholder() override;
 
   void MarkPluginEssential(
diff --git a/components/plugins/renderer/plugin_placeholder.cc b/components/plugins/renderer/plugin_placeholder.cc
index 8d11e55..9502575 100644
--- a/components/plugins/renderer/plugin_placeholder.cc
+++ b/components/plugins/renderer/plugin_placeholder.cc
@@ -21,11 +21,9 @@
 
 PluginPlaceholderBase::PluginPlaceholderBase(
     content::RenderFrame* render_frame,
-    blink::WebLocalFrame* frame,
     const blink::WebPluginParams& params,
     const std::string& html_data)
     : content::RenderFrameObserver(render_frame),
-      frame_(frame),
       plugin_params_(params),
       plugin_(WebViewPlugin::Create(render_frame->GetRenderView(),
                                     this,
@@ -113,23 +111,15 @@
   HidePlugin();
 }
 
-void PluginPlaceholderBase::OnDestruct() {
-  frame_ = NULL;
-}
-
-blink::WebLocalFrame* PluginPlaceholderBase::GetFrame() {
-  return frame_;
-}
+void PluginPlaceholderBase::OnDestruct() {}
 
 // static
 gin::WrapperInfo PluginPlaceholder::kWrapperInfo = {gin::kEmbedderNativeGin};
 
 PluginPlaceholder::PluginPlaceholder(content::RenderFrame* render_frame,
-                                     blink::WebLocalFrame* frame,
                                      const blink::WebPluginParams& params,
                                      const std::string& html_data)
-    : PluginPlaceholderBase(render_frame, frame, params, html_data) {
-}
+    : PluginPlaceholderBase(render_frame, params, html_data) {}
 
 PluginPlaceholder::~PluginPlaceholder() {
 }
diff --git a/components/plugins/renderer/plugin_placeholder.h b/components/plugins/renderer/plugin_placeholder.h
index 9f4f141e..86ca3528 100644
--- a/components/plugins/renderer/plugin_placeholder.h
+++ b/components/plugins/renderer/plugin_placeholder.h
@@ -20,19 +20,16 @@
 class PluginPlaceholderBase : public content::RenderFrameObserver,
                               public WebViewPlugin::Delegate {
  public:
-  // |render_frame| and |frame| are weak pointers. If either one is going away,
-  // our |plugin_| will be destroyed as well and will notify us.
+  // |render_frame| is a weak pointer. If it is going away, our |plugin_| will
+  // be destroyed as well and will notify us.
   PluginPlaceholderBase(content::RenderFrame* render_frame,
-                        blink::WebLocalFrame* frame,
                         const blink::WebPluginParams& params,
                         const std::string& html_data);
-
   ~PluginPlaceholderBase() override;
 
   WebViewPlugin* plugin() { return plugin_; }
 
  protected:
-  blink::WebLocalFrame* GetFrame();
   const blink::WebPluginParams& GetPluginParams() const;
 
   // WebViewPlugin::Delegate methods:
@@ -45,7 +42,7 @@
  protected:
   // Hide this placeholder.
   void HidePlugin();
-  bool hidden() { return hidden_; }
+  bool hidden() const { return hidden_; }
 
   // JavaScript callbacks:
   void HideCallback();
@@ -54,7 +51,6 @@
   // RenderFrameObserver methods:
   void OnDestruct() override;
 
-  blink::WebLocalFrame* frame_;
   blink::WebPluginParams plugin_params_;
   WebViewPlugin* plugin_;
 
@@ -70,7 +66,6 @@
   static gin::WrapperInfo kWrapperInfo;
 
   PluginPlaceholder(content::RenderFrame* render_frame,
-                    blink::WebLocalFrame* frame,
                     const blink::WebPluginParams& params,
                     const std::string& html_data);
   ~PluginPlaceholder() override;
diff --git a/content/browser/accessibility/dump_accessibility_tree_browsertest.cc b/content/browser/accessibility/dump_accessibility_tree_browsertest.cc
index 452093397..3e73d711 100644
--- a/content/browser/accessibility/dump_accessibility_tree_browsertest.cc
+++ b/content/browser/accessibility/dump_accessibility_tree_browsertest.cc
@@ -12,7 +12,6 @@
 #include "base/strings/string_split.h"
 #include "base/strings/string_util.h"
 #include "base/strings/utf_string_conversions.h"
-#include "base/test/scoped_feature_list.h"
 #include "base/threading/thread_restrictions.h"
 #include "build/build_config.h"
 #include "content/browser/accessibility/accessibility_tree_formatter.h"
@@ -72,6 +71,9 @@
     // Enable <dialog>, which is used in some tests.
     base::CommandLine::ForCurrentProcess()->AppendSwitch(
         switches::kEnableExperimentalWebPlatformFeatures);
+    // Enable accessibility object model, used in other tests.
+    base::CommandLine::ForCurrentProcess()->AppendSwitchASCII(
+        switches::kEnableBlinkFeatures, "AccessibilityObjectModel");
   }
 
   void RunAriaTest(const base::FilePath::CharType* file_path) {
@@ -86,9 +88,6 @@
   }
 
   void RunAomTest(const base::FilePath::CharType* file_path) {
-    scoped_feature_list_.InitAndEnableFeature(
-        features::kAccessibilityObjectModel);
-
     base::FilePath test_path = GetTestFilePath("accessibility", "aom");
     {
       base::ThreadRestrictions::ScopedAllowIO allow_io_for_test_setup;
@@ -136,9 +135,6 @@
         actual_contents, "\n",
         base::KEEP_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
   }
-
- protected:
-  base::test::ScopedFeatureList scoped_feature_list_;
 };
 
 IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest, AccessibilityCSSColor) {
diff --git a/content/browser/frame_host/render_frame_host_impl.cc b/content/browser/frame_host/render_frame_host_impl.cc
index 15283cb..c6eba025 100644
--- a/content/browser/frame_host/render_frame_host_impl.cc
+++ b/content/browser/frame_host/render_frame_host_impl.cc
@@ -2792,12 +2792,12 @@
     return true;
 
   // Standard URLs must match the reported origin.
-  if (url.IsStandard() && !origin.IsSameOriginWith(url::Origin(url)))
+  if (url.IsStandard() && !origin.IsSamePhysicalOriginWith(url::Origin(url)))
     return false;
 
   // A non-unique origin must be a valid URL, which allows us to safely do a
   // conversion to GURL.
-  GURL origin_url(origin.Serialize());
+  GURL origin_url = origin.GetPhysicalOrigin().GetURL();
 
   // Verify that the origin is allowed to commit in this process.
   // Note: This also handles non-standard cases for |url|, such as
diff --git a/content/browser/indexed_db/OWNERS b/content/browser/indexed_db/OWNERS
index beee668..11e923c 100644
--- a/content/browser/indexed_db/OWNERS
+++ b/content/browser/indexed_db/OWNERS
@@ -1,8 +1,8 @@
 jsbell@chromium.org
 cmumford@chromium.org
+dmurph@chromium.org
 
 # Secondary: only if other OWNERS are unavailable and something is urgent.
-dgrogan@chromium.org
 michaeln@chromium.org
 
 # TEAM: storage-dev@chromium.org
diff --git a/content/child/blink_platform_impl_unittest.cc b/content/child/blink_platform_impl_unittest.cc
index 47b3659..ca84ee2f 100644
--- a/content/child/blink_platform_impl_unittest.cc
+++ b/content/child/blink_platform_impl_unittest.cc
@@ -20,7 +20,7 @@
   url::Origin checked_origin =
       url::Origin::UnsafelyCreateOriginWithoutNormalization(
           origin.Protocol().Utf8(), origin.Host().Utf8(),
-          origin.EffectivePort());
+          origin.EffectivePort(), origin.Suborigin().Utf8());
   url::Origin non_checked_origin =
       url::Origin::CreateFromNormalizedTupleWithSuborigin(
           origin.Protocol().Utf8(), origin.Host().Utf8(),
diff --git a/content/child/runtime_features.cc b/content/child/runtime_features.cc
index a3f3e31..10adbf55 100644
--- a/content/child/runtime_features.cc
+++ b/content/child/runtime_features.cc
@@ -322,9 +322,6 @@
           features::kSendBeaconThrowForBlobWithNonSimpleType))
     WebRuntimeFeatures::EnableSendBeaconThrowForBlobWithNonSimpleType(true);
 
-  WebRuntimeFeatures::EnableAccessibilityObjectModel(
-      base::FeatureList::IsEnabled(features::kAccessibilityObjectModel));
-
 #if defined(OS_ANDROID)
   if (command_line.HasSwitch(switches::kDisableMediaSessionAPI))
     WebRuntimeFeatures::EnableMediaSession(false);
diff --git a/content/public/common/common_param_traits.cc b/content/public/common/common_param_traits.cc
index 3fcfdda..286e6621 100644
--- a/content/public/common/common_param_traits.cc
+++ b/content/public/common/common_param_traits.cc
@@ -21,6 +21,7 @@
   GetParamSize(s, p.scheme());
   GetParamSize(s, p.host());
   GetParamSize(s, p.port());
+  GetParamSize(s, p.suborigin());
 }
 
 void ParamTraits<url::Origin>::Write(base::Pickle* m, const url::Origin& p) {
@@ -28,6 +29,7 @@
   WriteParam(m, p.scheme());
   WriteParam(m, p.host());
   WriteParam(m, p.port());
+  WriteParam(m, p.suborigin());
 }
 
 bool ParamTraits<url::Origin>::Read(const base::Pickle* m,
@@ -37,15 +39,17 @@
   std::string scheme;
   std::string host;
   uint16_t port;
+  std::string suborigin;
   if (!ReadParam(m, iter, &unique) || !ReadParam(m, iter, &scheme) ||
-      !ReadParam(m, iter, &host) || !ReadParam(m, iter, &port)) {
+      !ReadParam(m, iter, &host) || !ReadParam(m, iter, &port) ||
+      !ReadParam(m, iter, &suborigin)) {
     *p = url::Origin();
     return false;
   }
 
   *p = unique ? url::Origin()
               : url::Origin::UnsafelyCreateOriginWithoutNormalization(
-                    scheme, host, port);
+                    scheme, host, port, suborigin);
 
   // If a unique origin was created, but the unique flag wasn't set, then
   // the values provided to 'UnsafelyCreateOriginWithoutNormalization' were
diff --git a/content/public/common/content_features.cc b/content/public/common/content_features.cc
index 6cf6701..f13ec8b3 100644
--- a/content/public/common/content_features.cc
+++ b/content/public/common/content_features.cc
@@ -9,13 +9,6 @@
 
 // All features in alphabetical order.
 
-// Enables the Accessibility Object Model.
-// Explainer: https://github.com/WICG/aom/blob/master/explainer.md
-// Spec: https://wicg.github.io/aom/spec/
-const base::Feature kAccessibilityObjectModel(
-    "AccessibilityObjectModel",
-    base::FEATURE_DISABLED_BY_DEFAULT);
-
 // Enables content-initiated, main frame navigations to data URLs.
 // TODO(meacer): Remove when the deprecation is complete.
 //               https://www.chromestatus.com/feature/5669602927312896
diff --git a/content/public/common/content_features.h b/content/public/common/content_features.h
index c83b97d..697c35e4 100644
--- a/content/public/common/content_features.h
+++ b/content/public/common/content_features.h
@@ -16,7 +16,6 @@
 
 // All features in alphabetical order. The features should be documented
 // alongside the definition of their values in the .cc file.
-CONTENT_EXPORT extern const base::Feature kAccessibilityObjectModel;
 CONTENT_EXPORT extern const base::Feature
     kAllowContentInitiatedDataUrlNavigations;
 CONTENT_EXPORT extern const base::Feature kAsmJsToWebAssembly;
diff --git a/content/public/renderer/content_renderer_client.cc b/content/public/renderer/content_renderer_client.cc
index 3beeb1cf..f2d852a 100644
--- a/content/public/renderer/content_renderer_client.cc
+++ b/content/public/renderer/content_renderer_client.cc
@@ -26,7 +26,6 @@
 
 bool ContentRendererClient::OverrideCreatePlugin(
     RenderFrame* render_frame,
-    blink::WebLocalFrame* frame,
     const blink::WebPluginParams& params,
     blink::WebPlugin** plugin) {
   return false;
diff --git a/content/public/renderer/content_renderer_client.h b/content/public/renderer/content_renderer_client.h
index 64127229..7147c0a 100644
--- a/content/public/renderer/content_renderer_client.h
+++ b/content/public/renderer/content_renderer_client.h
@@ -55,7 +55,7 @@
 class WebWorkerContentSettingsClientProxy;
 struct WebPluginParams;
 struct WebURLError;
-}
+}  // namespace blink
 
 namespace gfx {
 class ICCProfile;
@@ -97,7 +97,6 @@
   // returns false, the content layer will create the plugin.
   virtual bool OverrideCreatePlugin(
       RenderFrame* render_frame,
-      blink::WebLocalFrame* frame,
       const blink::WebPluginParams& params,
       blink::WebPlugin** plugin);
 
diff --git a/content/public/renderer/render_frame.h b/content/public/renderer/render_frame.h
index 6fb7ecf..a2f781ed 100644
--- a/content/public/renderer/render_frame.h
+++ b/content/public/renderer/render_frame.h
@@ -138,7 +138,6 @@
   // Create a new NPAPI/Pepper plugin depending on |info|. Returns NULL if no
   // plugin was found. |throttler| may be empty.
   virtual blink::WebPlugin* CreatePlugin(
-      blink::WebFrame* frame,
       const WebPluginInfo& info,
       const blink::WebPluginParams& params,
       std::unique_ptr<PluginInstanceThrottler> throttler) = 0;
diff --git a/content/renderer/pepper/pepper_webplugin_impl_browsertest.cc b/content/renderer/pepper/pepper_webplugin_impl_browsertest.cc
index 05b84d7..71c7f58 100644
--- a/content/renderer/pepper/pepper_webplugin_impl_browsertest.cc
+++ b/content/renderer/pepper/pepper_webplugin_impl_browsertest.cc
@@ -166,14 +166,13 @@
   class MockContentRendererClient : public ContentRendererClient {
    public:
     bool OverrideCreatePlugin(RenderFrame* render_frame,
-                              blink::WebLocalFrame* frame,
                               const blink::WebPluginParams& params,
                               blink::WebPlugin** plugin) override {
       current_test_->throttler_ =
           new PluginInstanceThrottlerImpl(RenderFrame::DONT_RECORD_DECISION);
       current_test_->throttler_->AddObserver(current_test_);
       *plugin = render_frame->CreatePlugin(
-          frame, GetPluginInfo().ToWebPluginInfo(), params,
+          GetPluginInfo().ToWebPluginInfo(), params,
           base::WrapUnique(current_test_->throttler_));
       return *plugin;
     }
diff --git a/content/renderer/render_frame_impl.cc b/content/renderer/render_frame_impl.cc
index 970d570f..92121a3 100644
--- a/content/renderer/render_frame_impl.cc
+++ b/content/renderer/render_frame_impl.cc
@@ -2512,19 +2512,17 @@
 }
 
 blink::WebPlugin* RenderFrameImpl::CreatePlugin(
-    blink::WebFrame* frame,
     const WebPluginInfo& info,
     const blink::WebPluginParams& params,
     std::unique_ptr<content::PluginInstanceThrottler> throttler) {
-  DCHECK_EQ(frame_, frame);
 #if BUILDFLAG(ENABLE_PLUGINS)
   if (info.type == WebPluginInfo::PLUGIN_TYPE_BROWSER_PLUGIN) {
+    // |delegate| deletes itself.
+    BrowserPluginDelegate* delegate =
+        GetContentClient()->renderer()->CreateBrowserPluginDelegate(
+            this, params.mime_type.Utf8(), GURL(params.url));
     return BrowserPluginManager::Get()->CreateBrowserPlugin(
-        this, GetContentClient()
-                  ->renderer()
-                  ->CreateBrowserPluginDelegate(this, params.mime_type.Utf8(),
-                                                GURL(params.url))
-                  ->GetWeakPtr());
+        this, delegate->GetWeakPtr());
   }
 
   bool pepper_plugin_was_registered = false;
@@ -2541,8 +2539,8 @@
 #if defined(OS_CHROMEOS)
   LOG(WARNING) << "Pepper module/plugin creation failed.";
 #endif
-#endif
-  return NULL;
+#endif  // BUILDFLAG(ENABLE_PLUGINS)
+  return nullptr;
 }
 
 void RenderFrameImpl::LoadURLExternally(const blink::WebURLRequest& request,
@@ -2787,23 +2785,21 @@
 // blink::WebFrameClient implementation ----------------------------------------
 
 blink::WebPlugin* RenderFrameImpl::CreatePlugin(
-    blink::WebLocalFrame* frame,
     const blink::WebPluginParams& params) {
-  DCHECK_EQ(frame_, frame);
-  blink::WebPlugin* plugin = NULL;
-  if (GetContentClient()->renderer()->OverrideCreatePlugin(
-          this, frame, params, &plugin)) {
+  blink::WebPlugin* plugin = nullptr;
+  if (GetContentClient()->renderer()->OverrideCreatePlugin(this, params,
+                                                           &plugin)) {
     return plugin;
   }
 
   if (params.mime_type.ContainsOnlyASCII() &&
       params.mime_type.Ascii() == kBrowserPluginMimeType) {
+    // |delegate| deletes itself.
+    BrowserPluginDelegate* delegate =
+        GetContentClient()->renderer()->CreateBrowserPluginDelegate(
+            this, kBrowserPluginMimeType, GURL(params.url));
     return BrowserPluginManager::Get()->CreateBrowserPlugin(
-        this, GetContentClient()
-                  ->renderer()
-                  ->CreateBrowserPluginDelegate(this, kBrowserPluginMimeType,
-                                                GURL(params.url))
-                  ->GetWeakPtr());
+        this, delegate->GetWeakPtr());
   }
 
 #if BUILDFLAG(ENABLE_PLUGINS)
@@ -2811,16 +2807,16 @@
   std::string mime_type;
   bool found = false;
   Send(new FrameHostMsg_GetPluginInfo(
-      routing_id_, params.url, frame->Top()->GetSecurityOrigin(),
+      routing_id_, params.url, frame_->Top()->GetSecurityOrigin(),
       params.mime_type.Utf8(), &found, &info, &mime_type));
   if (!found)
-    return NULL;
+    return nullptr;
 
   WebPluginParams params_to_use = params;
   params_to_use.mime_type = WebString::FromUTF8(mime_type);
-  return CreatePlugin(frame, info, params_to_use, nullptr /* throttler */);
+  return CreatePlugin(info, params_to_use, nullptr /* throttler */);
 #else
-  return NULL;
+  return nullptr;
 #endif  // BUILDFLAG(ENABLE_PLUGINS)
 }
 
diff --git a/content/renderer/render_frame_impl.h b/content/renderer/render_frame_impl.h
index 084bcd9..1ad07b6 100644
--- a/content/renderer/render_frame_impl.h
+++ b/content/renderer/render_frame_impl.h
@@ -438,7 +438,6 @@
                       const ContextMenuParams& params) override;
   void CancelContextMenu(int request_id) override;
   blink::WebPlugin* CreatePlugin(
-      blink::WebFrame* frame,
       const WebPluginInfo& info,
       const blink::WebPluginParams& params,
       std::unique_ptr<PluginInstanceThrottler> throttler) override;
@@ -499,8 +498,7 @@
   void SetHostZoomLevel(const GURL& url, double zoom_level) override;
 
   // blink::WebFrameClient implementation:
-  blink::WebPlugin* CreatePlugin(blink::WebLocalFrame* frame,
-                                 const blink::WebPluginParams& params) override;
+  blink::WebPlugin* CreatePlugin(const blink::WebPluginParams& params) override;
   blink::WebMediaPlayer* CreateMediaPlayer(
       const blink::WebMediaPlayerSource& source,
       blink::WebMediaPlayerClient* client,
diff --git a/content/renderer/render_thread_impl.cc b/content/renderer/render_thread_impl.cc
index efeb21a..9dad020 100644
--- a/content/renderer/render_thread_impl.cc
+++ b/content/renderer/render_thread_impl.cc
@@ -1410,6 +1410,12 @@
       !cmd_line->HasSwitch(switches::kDisableGpuMemoryBufferVideoFrames) &&
       !cmd_line->HasSwitch(switches::kDisableGpuCompositing) &&
       !gpu_channel_host->gpu_info().software_rendering;
+#elif defined(OS_WIN)
+      !cmd_line->HasSwitch(switches::kDisableGpuMemoryBufferVideoFrames) &&
+      !cmd_line->HasSwitch(switches::kDisableGpuCompositing) &&
+      !gpu_channel_host->gpu_info().software_rendering &&
+      (cmd_line->HasSwitch(switches::kEnableGpuMemoryBufferVideoFrames) ||
+       gpu_channel_host->gpu_info().supports_overlays);
 #else
       cmd_line->HasSwitch(switches::kEnableGpuMemoryBufferVideoFrames);
 #endif
diff --git a/content/shell/renderer/layout_test/blink_test_runner.cc b/content/shell/renderer/layout_test/blink_test_runner.cc
index 2ebaac0..b0628dc8 100644
--- a/content/shell/renderer/layout_test/blink_test_runner.cc
+++ b/content/shell/renderer/layout_test/blink_test_runner.cc
@@ -745,13 +745,12 @@
 }
 
 blink::WebPlugin* BlinkTestRunner::CreatePluginPlaceholder(
-    blink::WebLocalFrame* frame, const blink::WebPluginParams& params) {
+    const blink::WebPluginParams& params) {
   if (params.mime_type != "application/x-plugin-placeholder-test")
     return nullptr;
 
-  plugins::PluginPlaceholder* placeholder =
-      new plugins::PluginPlaceholder(render_view()->GetMainRenderFrame(), frame,
-                                     params, "<div>Test content</div>");
+  plugins::PluginPlaceholder* placeholder = new plugins::PluginPlaceholder(
+      render_view()->GetMainRenderFrame(), params, "<div>Test content</div>");
   return placeholder->plugin();
 }
 
diff --git a/content/shell/renderer/layout_test/blink_test_runner.h b/content/shell/renderer/layout_test/blink_test_runner.h
index 12507bf..3f1e18c 100644
--- a/content/shell/renderer/layout_test/blink_test_runner.h
+++ b/content/shell/renderer/layout_test/blink_test_runner.h
@@ -157,7 +157,6 @@
   void ResolveBeforeInstallPromptPromise(
       const std::string& platform) override;
   blink::WebPlugin* CreatePluginPlaceholder(
-    blink::WebLocalFrame* frame,
     const blink::WebPluginParams& params) override;
   float GetDeviceScaleFactor() const override;
   void RunIdleTasks(const base::Closure& callback) override;
diff --git a/content/shell/test_runner/test_plugin.cc b/content/shell/test_runner/test_plugin.cc
index c2b5fe6..959439f 100644
--- a/content/shell/test_runner/test_plugin.cc
+++ b/content/shell/test_runner/test_plugin.cc
@@ -30,7 +30,6 @@
 #include "third_party/WebKit/public/platform/WebTouchPoint.h"
 #include "third_party/WebKit/public/platform/WebTraceLocation.h"
 #include "third_party/WebKit/public/platform/WebURL.h"
-#include "third_party/WebKit/public/web/WebFrame.h"
 #include "third_party/WebKit/public/web/WebKit.h"
 #include "third_party/WebKit/public/web/WebPluginParams.h"
 #include "third_party/WebKit/public/web/WebUserGestureIndicator.h"
@@ -110,11 +109,9 @@
 
 }  // namespace
 
-TestPlugin::TestPlugin(blink::WebFrame* frame,
-                       const blink::WebPluginParams& params,
+TestPlugin::TestPlugin(const blink::WebPluginParams& params,
                        WebTestDelegate* delegate)
-    : frame_(frame),
-      delegate_(delegate),
+    : delegate_(delegate),
       container_(nullptr),
       gl_(nullptr),
       color_texture_(0),
@@ -212,7 +209,6 @@
   context_provider_.reset();
 
   container_ = nullptr;
-  frame_ = nullptr;
 
   blink::Platform::Current()
       ->MainThread()
@@ -587,10 +583,9 @@
   return false;
 }
 
-TestPlugin* TestPlugin::create(blink::WebFrame* frame,
-                               const blink::WebPluginParams& params,
+TestPlugin* TestPlugin::Create(const blink::WebPluginParams& params,
                                WebTestDelegate* delegate) {
-  return new TestPlugin(frame, params, delegate);
+  return new TestPlugin(params, delegate);
 }
 
 const blink::WebString& TestPlugin::MimeType() {
diff --git a/content/shell/test_runner/test_plugin.h b/content/shell/test_runner/test_plugin.h
index 7f7e706..07a7a32 100644
--- a/content/shell/test_runner/test_plugin.h
+++ b/content/shell/test_runner/test_plugin.h
@@ -19,7 +19,6 @@
 #include "third_party/khronos/GLES2/gl2.h"
 
 namespace blink {
-class WebFrame;
 class WebGraphicsContext3DProvider;
 class WebLayer;
 struct WebPluginParams;
@@ -53,8 +52,7 @@
 // 'accepts-touch' plugin parameter (defaults to false).
 class TestPlugin : public blink::WebPlugin, public cc::TextureLayerClient {
  public:
-  static TestPlugin* create(blink::WebFrame* frame,
-                            const blink::WebPluginParams& params,
+  static TestPlugin* Create(const blink::WebPluginParams& params,
                             WebTestDelegate* delegate);
   ~TestPlugin() override;
 
@@ -98,9 +96,7 @@
       std::unique_ptr<cc::SingleReleaseCallback>* release_callback) override;
 
  private:
-  TestPlugin(blink::WebFrame* frame,
-             const blink::WebPluginParams& params,
-             WebTestDelegate* delegate);
+  TestPlugin(const blink::WebPluginParams& params, WebTestDelegate* delegate);
 
   enum Primitive { PrimitiveNone, PrimitiveTriangle };
 
@@ -148,7 +144,6 @@
   // Functions for drawing scene in Software.
   void DrawSceneSoftware(void* memory);
 
-  blink::WebFrame* frame_;
   WebTestDelegate* delegate_;
   blink::WebPluginContainer* container_;
 
diff --git a/content/shell/test_runner/web_frame_test_client.cc b/content/shell/test_runner/web_frame_test_client.cc
index 2a52fad..d465938 100644
--- a/content/shell/test_runner/web_frame_test_client.cc
+++ b/content/shell/test_runner/web_frame_test_client.cc
@@ -370,11 +370,10 @@
 }
 
 blink::WebPlugin* WebFrameTestClient::CreatePlugin(
-    blink::WebLocalFrame* frame,
     const blink::WebPluginParams& params) {
   if (TestPlugin::IsSupportedMimeType(params.mime_type))
-    return TestPlugin::create(frame, params, delegate_);
-  return delegate_->CreatePluginPlaceholder(frame, params);
+    return TestPlugin::Create(params, delegate_);
+  return delegate_->CreatePluginPlaceholder(params);
 }
 
 void WebFrameTestClient::ShowContextMenu(
diff --git a/content/shell/test_runner/web_frame_test_client.h b/content/shell/test_runner/web_frame_test_client.h
index 5d162ea..1d74ee18 100644
--- a/content/shell/test_runner/web_frame_test_client.h
+++ b/content/shell/test_runner/web_frame_test_client.h
@@ -47,8 +47,7 @@
   void PostAccessibilityEvent(const blink::WebAXObject& object,
                               blink::WebAXEvent event) override;
   void DidChangeSelection(bool is_selection_empty) override;
-  blink::WebPlugin* CreatePlugin(blink::WebLocalFrame* frame,
-                                 const blink::WebPluginParams& params) override;
+  blink::WebPlugin* CreatePlugin(const blink::WebPluginParams& params) override;
   void ShowContextMenu(
       const blink::WebContextMenuData& context_menu_data) override;
   blink::WebUserMediaClient* UserMediaClient() override;
diff --git a/content/shell/test_runner/web_frame_test_proxy.h b/content/shell/test_runner/web_frame_test_proxy.h
index cd1e517f..a52adee 100644
--- a/content/shell/test_runner/web_frame_test_proxy.h
+++ b/content/shell/test_runner/web_frame_test_proxy.h
@@ -58,12 +58,11 @@
 
   // WebFrameClient implementation.
   blink::WebPlugin* CreatePlugin(
-      blink::WebLocalFrame* frame,
       const blink::WebPluginParams& params) override {
-    blink::WebPlugin* plugin = test_client()->CreatePlugin(frame, params);
+    blink::WebPlugin* plugin = test_client()->CreatePlugin(params);
     if (plugin)
       return plugin;
-    return Base::CreatePlugin(frame, params);
+    return Base::CreatePlugin(params);
   }
 
   blink::WebScreenOrientationClient* GetWebScreenOrientationClient() override {
diff --git a/content/shell/test_runner/web_test_delegate.h b/content/shell/test_runner/web_test_delegate.h
index f9be6b8..845889c 100644
--- a/content/shell/test_runner/web_test_delegate.h
+++ b/content/shell/test_runner/web_test_delegate.h
@@ -285,7 +285,6 @@
       const std::string& platform) = 0;
 
   virtual blink::WebPlugin* CreatePluginPlaceholder(
-      blink::WebLocalFrame* frame,
       const blink::WebPluginParams& params) = 0;
 
   virtual float GetDeviceScaleFactor() const = 0;
@@ -301,6 +300,9 @@
   // Indicates if the navigation was initiated by the browser or renderer.
   virtual bool IsNavigationInitiatedByRenderer(
       const blink::WebURLRequest& request) = 0;
+
+ protected:
+  virtual ~WebTestDelegate() {}
 };
 
 }  // namespace test_runner
diff --git a/extensions/shell/renderer/shell_content_renderer_client.cc b/extensions/shell/renderer/shell_content_renderer_client.cc
index dcb905cc..8f505bf 100644
--- a/extensions/shell/renderer/shell_content_renderer_client.cc
+++ b/extensions/shell/renderer/shell_content_renderer_client.cc
@@ -80,7 +80,6 @@
 
 bool ShellContentRendererClient::OverrideCreatePlugin(
     content::RenderFrame* render_frame,
-    blink::WebLocalFrame* frame,
     const blink::WebPluginParams& params,
     blink::WebPlugin** plugin) {
   // Allow the content module to create the plugin.
diff --git a/extensions/shell/renderer/shell_content_renderer_client.h b/extensions/shell/renderer/shell_content_renderer_client.h
index 03e2e74..5aad8dbb 100644
--- a/extensions/shell/renderer/shell_content_renderer_client.h
+++ b/extensions/shell/renderer/shell_content_renderer_client.h
@@ -34,7 +34,6 @@
   void RenderFrameCreated(content::RenderFrame* render_frame) override;
   void RenderViewCreated(content::RenderView* render_view) override;
   bool OverrideCreatePlugin(content::RenderFrame* render_frame,
-                            blink::WebLocalFrame* frame,
                             const blink::WebPluginParams& params,
                             blink::WebPlugin** plugin) override;
   blink::WebPlugin* CreatePluginReplacement(
diff --git a/ios/build/bots/chromium.mac/ios-simulator.json b/ios/build/bots/chromium.mac/ios-simulator.json
index 95357ffd..79d61520 100644
--- a/ios/build/bots/chromium.mac/ios-simulator.json
+++ b/ios/build/bots/chromium.mac/ios-simulator.json
@@ -51,18 +51,6 @@
     },
     {
       "include": "common_tests.json",
-      "device type": "iPhone 6s Plus",
-      "os": "9.0",
-      "xcode version": "8.0"
-    },
-    {
-      "include": "common_tests.json",
-      "device type": "iPhone 6s",
-      "os": "9.0",
-      "xcode version": "8.0"
-    },
-    {
-      "include": "common_tests.json",
       "device type": "iPhone 5",
       "os": "9.0",
       "xcode version": "8.0"
@@ -72,12 +60,6 @@
       "device type": "iPad Air 2",
       "os": "9.0",
       "xcode version": "8.0"
-    },
-    {
-      "include": "common_tests.json",
-      "device type": "iPad Retina",
-      "os": "9.0",
-      "xcode version": "8.0"
     }
   ]
 }
diff --git a/ios/chrome/browser/ui/authentication/signin_interaction_controller_egtest.mm b/ios/chrome/browser/ui/authentication/signin_interaction_controller_egtest.mm
index 606c067..cf90395e 100644
--- a/ios/chrome/browser/ui/authentication/signin_interaction_controller_egtest.mm
+++ b/ios/chrome/browser/ui/authentication/signin_interaction_controller_egtest.mm
@@ -86,16 +86,7 @@
 // Opens the signin screen from the settings page. Must be called from the NTP.
 // User must not be signed in.
 void OpenSignInFromSettings() {
-  const CGFloat scroll_displacement = 50.0;
-
-  [ChromeEarlGreyUI openToolsMenu];
-  [[[EarlGrey
-      selectElementWithMatcher:grey_accessibilityID(kToolsMenuSettingsId)]
-         usingSearchAction:grey_scrollInDirection(kGREYDirectionDown,
-                                                  scroll_displacement)
-      onElementWithMatcher:grey_accessibilityID(kToolsMenuTableViewId)]
-      performAction:grey_tap()];
-
+  [ChromeEarlGreyUI openSettingsMenu];
   TapViewWithAccessibilityId(kSettingsSignInCellId);
 }
 
diff --git a/ios/chrome/browser/ui/dialogs/javascript_dialog_egtest.mm b/ios/chrome/browser/ui/dialogs/javascript_dialog_egtest.mm
index 3e58bb7d..a70248d 100644
--- a/ios/chrome/browser/ui/dialogs/javascript_dialog_egtest.mm
+++ b/ios/chrome/browser/ui/dialogs/javascript_dialog_egtest.mm
@@ -492,10 +492,7 @@
   [self loadBlankTestPage];
 
   // Show settings.
-  [ChromeEarlGreyUI openToolsMenu];
-  [[EarlGrey
-      selectElementWithMatcher:grey_accessibilityID(kToolsMenuSettingsId)]
-      performAction:grey_tap()];
+  [ChromeEarlGreyUI openSettingsMenu];
   [[EarlGrey selectElementWithMatcher:chrome_test_util::
                                           StaticTextWithAccessibilityLabelId(
                                               IDS_IOS_SETTINGS_TITLE)]
diff --git a/ios/chrome/browser/ui/history/history_ui_egtest.mm b/ios/chrome/browser/ui/history/history_ui_egtest.mm
index 3f20831..e86fa3d 100644
--- a/ios/chrome/browser/ui/history/history_ui_egtest.mm
+++ b/ios/chrome/browser/ui/history/history_ui_egtest.mm
@@ -145,13 +145,7 @@
   ios::FakeChromeIdentityService::GetInstanceFromChromeProvider()->AddIdentity(
       identity);
 
-  [ChromeEarlGreyUI openToolsMenu];
-  [[[EarlGrey
-      selectElementWithMatcher:grey_accessibilityID(kToolsMenuSettingsId)]
-         usingSearchAction:grey_scrollToContentEdge(kGREYContentEdgeBottom)
-      onElementWithMatcher:grey_accessibilityID(kToolsMenuTableViewId)]
-      performAction:grey_tap()];
-
+  [ChromeEarlGreyUI openSettingsMenu];
   [[EarlGrey
       selectElementWithMatcher:grey_accessibilityID(kSettingsSignInCellId)]
       performAction:grey_tap()];
diff --git a/ios/chrome/browser/ui/keyboard_commands_egtest.mm b/ios/chrome/browser/ui/keyboard_commands_egtest.mm
index af09efd7..971ce430 100644
--- a/ios/chrome/browser/ui/keyboard_commands_egtest.mm
+++ b/ios/chrome/browser/ui/keyboard_commands_egtest.mm
@@ -133,10 +133,7 @@
 
 // Tests that keyboard commands are not registered when Settings are shown.
 - (void)testKeyboardCommandsNotRegistered_SettingsPresented {
-  // Open Settings
-  id<GREYMatcher> toolsMenuSettings =
-      grey_accessibilityID(kToolsMenuSettingsId);
-  [self selectToolsMenuItem:toolsMenuSettings];
+  [ChromeEarlGreyUI openSettingsMenu];
 
   [self verifyNoKeyboardCommandsAreRegistered];
 
diff --git a/ios/chrome/browser/ui/payments/payment_request_coordinator.mm b/ios/chrome/browser/ui/payments/payment_request_coordinator.mm
index 991e2ee7..c43545a 100644
--- a/ios/chrome/browser/ui/payments/payment_request_coordinator.mm
+++ b/ios/chrome/browser/ui/payments/payment_request_coordinator.mm
@@ -54,6 +54,9 @@
   void Show() override {
     view_controller_.reset(
         [[CardUnmaskPromptViewController alloc] initWithBridge:this]);
+    [view_controller_ setModalPresentationStyle:UIModalPresentationFormSheet];
+    [view_controller_
+        setModalTransitionStyle:UIModalTransitionStyleCoverVertical];
     [base_view_controller_ presentViewController:view_controller_
                                         animated:YES
                                       completion:nil];
diff --git a/ios/chrome/browser/ui/settings/accounts_collection_egtest.mm b/ios/chrome/browser/ui/settings/accounts_collection_egtest.mm
index 0d9aec4..73adca9e 100644
--- a/ios/chrome/browser/ui/settings/accounts_collection_egtest.mm
+++ b/ios/chrome/browser/ui/settings/accounts_collection_egtest.mm
@@ -68,16 +68,7 @@
 // Opens the signin screen from the settings page. Assumes the tools menu is
 // accessible. User must not be signed in.
 void OpenSignInFromSettings() {
-  const CGFloat scroll_displacement = 50.0;
-
-  [ChromeEarlGreyUI openToolsMenu];
-  [[[EarlGrey
-      selectElementWithMatcher:grey_accessibilityID(kToolsMenuSettingsId)]
-         usingSearchAction:grey_scrollInDirection(kGREYDirectionDown,
-                                                  scroll_displacement)
-      onElementWithMatcher:grey_accessibilityID(kToolsMenuTableViewId)]
-      performAction:grey_tap()];
-
+  [ChromeEarlGreyUI openSettingsMenu];
   TapViewWithAccessibilityId(kSettingsSignInCellId);
 }
 
diff --git a/ios/chrome/browser/ui/settings/autofill_settings_egtest.mm b/ios/chrome/browser/ui/settings/autofill_settings_egtest.mm
index 484672d5..ce16d3f 100644
--- a/ios/chrome/browser/ui/settings/autofill_settings_egtest.mm
+++ b/ios/chrome/browser/ui/settings/autofill_settings_egtest.mm
@@ -133,16 +133,11 @@
 
 // Helper to open the settings page for the record with |address|.
 - (void)openEditAddress:(NSString*)address {
-  // Open settings and verify data in the view controller.
-  [ChromeEarlGreyUI openToolsMenu];
-  [[EarlGrey
-      selectElementWithMatcher:grey_accessibilityID(kToolsMenuSettingsId)]
-      performAction:grey_tap()];
+  [ChromeEarlGreyUI openSettingsMenu];
   NSString* label = l10n_util::GetNSString(IDS_IOS_AUTOFILL);
   [[EarlGrey selectElementWithMatcher:ButtonWithAccessibilityLabel(label)]
       performAction:grey_tap()];
 
-  // Tap on the 'George Washington' result.
   NSString* cellLabel = address;
   [[EarlGrey selectElementWithMatcher:grey_accessibilityLabel(cellLabel)]
       performAction:grey_tap()];
diff --git a/ios/chrome/browser/ui/settings/block_popups_egtest.mm b/ios/chrome/browser/ui/settings/block_popups_egtest.mm
index 57a113b..968d670 100644
--- a/ios/chrome/browser/ui/settings/block_popups_egtest.mm
+++ b/ios/chrome/browser/ui/settings/block_popups_egtest.mm
@@ -56,10 +56,6 @@
 // Opens the block popups settings page.  Must be called from the NTP.
 void OpenBlockPopupsSettings() {
   const CGFloat scroll_displacement = 50.0;
-  id<GREYMatcher> tools_menu_table_view_matcher =
-      grey_accessibilityID(kToolsMenuTableViewId);
-  id<GREYMatcher> settings_button_matcher =
-      grey_accessibilityID(kToolsMenuSettingsId);
   id<GREYMatcher> content_settings_button_matcher =
       chrome_test_util::ButtonWithAccessibilityLabelId(
           IDS_IOS_CONTENT_SETTINGS_TITLE);
@@ -68,12 +64,7 @@
   id<GREYMatcher> block_popups_button_matcher =
       chrome_test_util::ButtonWithAccessibilityLabelId(IDS_IOS_BLOCK_POPUPS);
 
-  [ChromeEarlGreyUI openToolsMenu];
-  [[[EarlGrey selectElementWithMatcher:settings_button_matcher]
-         usingSearchAction:grey_scrollInDirection(kGREYDirectionDown,
-                                                  scroll_displacement)
-      onElementWithMatcher:tools_menu_table_view_matcher]
-      performAction:grey_tap()];
+  [ChromeEarlGreyUI openSettingsMenu];
 
   [[[EarlGrey selectElementWithMatcher:content_settings_button_matcher]
          usingSearchAction:grey_scrollInDirection(kGREYDirectionDown,
diff --git a/ios/chrome/browser/ui/settings/clear_browsing_data_egtest.mm b/ios/chrome/browser/ui/settings/clear_browsing_data_egtest.mm
index 76a70dd4..38307d3 100644
--- a/ios/chrome/browser/ui/settings/clear_browsing_data_egtest.mm
+++ b/ios/chrome/browser/ui/settings/clear_browsing_data_egtest.mm
@@ -27,10 +27,7 @@
 @implementation ClearBrowsingDataSettingsTestCase
 
 - (void)openClearBrowsingDataDialog {
-  [ChromeEarlGreyUI openToolsMenu];
-  [[EarlGrey
-      selectElementWithMatcher:grey_accessibilityID(kToolsMenuSettingsId)]
-      performAction:grey_tap()];
+  [ChromeEarlGreyUI openSettingsMenu];
   NSString* settingsLabel =
       l10n_util::GetNSString(IDS_OPTIONS_ADVANCED_SECTION_TITLE_PRIVACY);
   [[EarlGrey
diff --git a/ios/chrome/browser/ui/settings/settings_egtest.mm b/ios/chrome/browser/ui/settings/settings_egtest.mm
index 377918f..3ffc458f 100644
--- a/ios/chrome/browser/ui/settings/settings_egtest.mm
+++ b/ios/chrome/browser/ui/settings/settings_egtest.mm
@@ -103,10 +103,6 @@
 id<GREYMatcher> ConfirmClearBrowsingDataButton() {
   return ButtonWithAccessibilityLabelId(IDS_IOS_CONFIRM_CLEAR_BUTTON);
 }
-// Matcher for the Settings button in the tools menu.
-id<GREYMatcher> SettingsButton() {
-  return grey_accessibilityID(kToolsMenuSettingsId);
-}
 // Matcher for the Save Passwords cell on the main Settings screen.
 id<GREYMatcher> PasswordsButton() {
   return ButtonWithAccessibilityLabelId(IDS_IOS_SAVE_PASSWORDS);
@@ -253,18 +249,10 @@
 // until the bottom.
 - (void)openSubSettingMenu:(id<GREYMatcher>)settingToTap {
   const CGFloat kScrollDisplacement = 150.0;
-  id<GREYMatcher> toolsMenuTableViewMatcher =
-      grey_accessibilityID(kToolsMenuTableViewId);
-  id<GREYMatcher> settingsButtonMatcher =
-      grey_accessibilityID(kToolsMenuSettingsId);
   id<GREYMatcher> settingsCollectionViewMatcher =
       grey_accessibilityID(kSettingsCollectionViewId);
 
-  [ChromeEarlGreyUI openToolsMenu];
-  [[[EarlGrey selectElementWithMatcher:settingsButtonMatcher]
-         usingSearchAction:grey_scrollInDirection(kGREYDirectionDown,
-                                                  kScrollDisplacement)
-      onElementWithMatcher:toolsMenuTableViewMatcher] performAction:grey_tap()];
+  [ChromeEarlGreyUI openSettingsMenu];
   [[[EarlGrey selectElementWithMatcher:settingToTap]
          usingSearchAction:grey_scrollInDirection(kGREYDirectionDown,
                                                   kScrollDisplacement)
@@ -306,9 +294,7 @@
 
 // From the NTP, clears the cookies and site data via the UI.
 - (void)clearCookiesAndSiteData {
-  [ChromeEarlGreyUI openToolsMenu];
-  [[EarlGrey selectElementWithMatcher:SettingsButton()]
-      performAction:grey_tap()];
+  [ChromeEarlGreyUI openSettingsMenu];
   [[EarlGrey selectElementWithMatcher:PrivacyButton()]
       performAction:grey_tap()];
   [[EarlGrey selectElementWithMatcher:ClearBrowsingDataCell()]
@@ -329,9 +315,7 @@
 
 // From the NTP, clears the saved passwords via the UI.
 - (void)clearPasswords {
-  [ChromeEarlGreyUI openToolsMenu];
-  [[EarlGrey selectElementWithMatcher:SettingsButton()]
-      performAction:grey_tap()];
+  [ChromeEarlGreyUI openSettingsMenu];
   [[EarlGrey selectElementWithMatcher:PrivacyButton()]
       performAction:grey_tap()];
   [[EarlGrey selectElementWithMatcher:ClearBrowsingDataCell()]
@@ -409,9 +393,7 @@
 // If |saved| is YES, it checks that there is a Saved Passwords section.
 // If |saved| is NO, it checks that there is no Saved Passwords section.
 - (void)checkIfPasswordsSaved:(BOOL)saved {
-  [ChromeEarlGreyUI openToolsMenu];
-  [[EarlGrey selectElementWithMatcher:SettingsButton()]
-      performAction:grey_tap()];
+  [ChromeEarlGreyUI openSettingsMenu];
   [[EarlGrey selectElementWithMatcher:PasswordsButton()]
       performAction:grey_tap()];
 
@@ -455,10 +437,7 @@
 // Opens the passwords page from the NTP. It requires no menus to be open.
 - (void)openPasswordSettings {
   // Open settings and verify data in the view controller.
-  [ChromeEarlGreyUI openToolsMenu];
-  [[EarlGrey
-      selectElementWithMatcher:grey_accessibilityID(kToolsMenuSettingsId)]
-      performAction:grey_tap()];
+  [ChromeEarlGreyUI openSettingsMenu];
   [[EarlGrey selectElementWithMatcher:PasswordsButton()]
       performAction:grey_tap()];
 }
@@ -776,9 +755,7 @@
 - (void)testSettingsSignedOutIncognito {
   chrome_test_util::OpenNewIncognitoTab();
 
-  [ChromeEarlGreyUI openToolsMenu];
-  [[EarlGrey selectElementWithMatcher:SettingsButton()]
-      performAction:grey_tap()];
+  [ChromeEarlGreyUI openSettingsMenu];
   [[EarlGrey
       selectElementWithMatcher:grey_accessibilityID(kSettingsCollectionViewId)]
       assertWithMatcher:grey_notNil()];
@@ -790,9 +767,7 @@
 
 // Verifies the UI elements are accessible on the Settings page.
 - (void)testAccessibilityOnSettingsPage {
-  [ChromeEarlGreyUI openToolsMenu];
-  [[EarlGrey selectElementWithMatcher:SettingsButton()]
-      performAction:grey_tap()];
+  [ChromeEarlGreyUI openSettingsMenu];
   chrome_test_util::VerifyAccessibilityForCurrentScreen();
   [[EarlGrey selectElementWithMatcher:NavigationBarDoneButton()]
       performAction:grey_tap()];
@@ -880,9 +855,7 @@
 
 // Verifies the UI elements are accessible on the Save Passwords page.
 - (void)testAccessibilityOnSavePasswords {
-  [ChromeEarlGreyUI openToolsMenu];
-  [[EarlGrey selectElementWithMatcher:SettingsButton()]
-      performAction:grey_tap()];
+  [ChromeEarlGreyUI openSettingsMenu];
   [[EarlGrey selectElementWithMatcher:PasswordsButton()]
       performAction:grey_tap()];
   chrome_test_util::VerifyAccessibilityForCurrentScreen();
@@ -891,9 +864,7 @@
 
 // Verifies the UI elements are accessible on the Search engine page.
 - (void)testAccessibilityOnSearchEngine {
-  [ChromeEarlGreyUI openToolsMenu];
-  [[EarlGrey selectElementWithMatcher:SettingsButton()]
-      performAction:grey_tap()];
+  [ChromeEarlGreyUI openSettingsMenu];
   [[EarlGrey selectElementWithMatcher:SearchEngineButton()]
       performAction:grey_tap()];
   chrome_test_util::VerifyAccessibilityForCurrentScreen();
@@ -902,9 +873,7 @@
 
 // Verifies the UI elements are accessible on the Autofill Forms page.
 - (void)testAccessibilityOnAutofillForms {
-  [ChromeEarlGreyUI openToolsMenu];
-  [[EarlGrey selectElementWithMatcher:SettingsButton()]
-      performAction:grey_tap()];
+  [ChromeEarlGreyUI openSettingsMenu];
   [[EarlGrey selectElementWithMatcher:AutofillButton()]
       performAction:grey_tap()];
   chrome_test_util::VerifyAccessibilityForCurrentScreen();
@@ -916,11 +885,7 @@
   // TODO(crbug/711511): Remove when Native App Launcher is full deprecated.
   if (!experimental_flags::IsNativeAppLauncherEnabled())
     return;
-  [ChromeEarlGreyUI openToolsMenu];
-  [[EarlGrey selectElementWithMatcher:SettingsButton()]
-      performAction:grey_tap()];
-  [[EarlGrey selectElementWithMatcher:GoogleAppsButton()]
-      performAction:grey_tap()];
+  [self openSubSettingMenu:GoogleAppsButton()];
   chrome_test_util::VerifyAccessibilityForCurrentScreen();
   [self closeSubSettingsMenu];
 }
@@ -987,10 +952,7 @@
 // Verifies that the Settings UI registers keyboard commands when presented, but
 // not when it itslef presents something.
 - (void)testSettingsKeyboardCommands {
-  // Open Settings.
-  [ChromeEarlGreyUI openToolsMenu];
-  [[EarlGrey selectElementWithMatcher:SettingsButton()]
-      performAction:grey_tap()];
+  [ChromeEarlGreyUI openSettingsMenu];
   [[EarlGrey
       selectElementWithMatcher:grey_accessibilityID(kSettingsCollectionViewId)]
       assertWithMatcher:grey_notNil()];
@@ -1030,9 +992,7 @@
 
 // Verifies the UI elements are accessible on the Send Usage Data page.
 - (void)testAccessibilityOnSendUsageData {
-  [ChromeEarlGreyUI openToolsMenu];
-  [[EarlGrey selectElementWithMatcher:SettingsButton()]
-      performAction:grey_tap()];
+  [ChromeEarlGreyUI openSettingsMenu];
   [[EarlGrey selectElementWithMatcher:PrivacyButton()]
       performAction:grey_tap()];
   [[EarlGrey selectElementWithMatcher:SendUsageDataButton()]
diff --git a/ios/chrome/browser/ui/settings/translate_ui_egtest.mm b/ios/chrome/browser/ui/settings/translate_ui_egtest.mm
index dd169c5..132e507 100644
--- a/ios/chrome/browser/ui/settings/translate_ui_egtest.mm
+++ b/ios/chrome/browser/ui/settings/translate_ui_egtest.mm
@@ -35,13 +35,7 @@
   // Open translate settings.
   // TODO(crbug.com/606815): This and close settings is mostly shared with block
   // popups settings tests, and others. See if this can move to shared code.
-  [ChromeEarlGreyUI openToolsMenu];
-  [[[EarlGrey
-      selectElementWithMatcher:grey_accessibilityID(kToolsMenuSettingsId)]
-         usingSearchAction:grey_scrollInDirection(kGREYDirectionDown,
-                                                  kScrollDisplacement)
-      onElementWithMatcher:grey_accessibilityID(kToolsMenuTableViewId)]
-      performAction:grey_tap()];
+  [ChromeEarlGreyUI openSettingsMenu];
   [[[EarlGrey
       selectElementWithMatcher:chrome_test_util::ButtonWithAccessibilityLabelId(
                                    IDS_IOS_CONTENT_SETTINGS_TITLE)]
diff --git a/ios/chrome/browser/ui/sync/sync_fake_server_egtest.mm b/ios/chrome/browser/ui/sync/sync_fake_server_egtest.mm
index 1b33147..71cd0e37 100644
--- a/ios/chrome/browser/ui/sync/sync_fake_server_egtest.mm
+++ b/ios/chrome/browser/ui/sync/sync_fake_server_egtest.mm
@@ -54,16 +54,7 @@
 // User must not be signed in.
 // TODO(crbug.com/638674): Evaluate if this can move to shared code.
 void OpenSignInFromSettings() {
-  const CGFloat scroll_displacement = 50.0;
-
-  [ChromeEarlGreyUI openToolsMenu];
-  [[[EarlGrey
-      selectElementWithMatcher:grey_accessibilityID(kToolsMenuSettingsId)]
-         usingSearchAction:grey_scrollInDirection(kGREYDirectionDown,
-                                                  scroll_displacement)
-      onElementWithMatcher:grey_accessibilityID(kToolsMenuTableViewId)]
-      performAction:grey_tap()];
-
+  [ChromeEarlGreyUI openSettingsMenu];
   id<GREYMatcher> matcher =
       grey_allOf(grey_accessibilityID(kSettingsSignInCellId),
                  grey_sufficientlyVisible(), nil);
diff --git a/ios/chrome/test/earl_grey/chrome_earl_grey_ui.h b/ios/chrome/test/earl_grey/chrome_earl_grey_ui.h
index 8c37e0d6..ea7dd86a 100644
--- a/ios/chrome/test/earl_grey/chrome_earl_grey_ui.h
+++ b/ios/chrome/test/earl_grey/chrome_earl_grey_ui.h
@@ -16,6 +16,11 @@
 // calling this method.
 + (void)openToolsMenu;
 
+// Opens the settings menu by opening the tools menu, and then tapping the
+// Settings button. There will be a GREYAssert if the tools menu is open when
+// calling this method.
++ (void)openSettingsMenu;
+
 // Open a new tab via the tools menu.
 + (void)openNewTab;
 
diff --git a/ios/chrome/test/earl_grey/chrome_earl_grey_ui.mm b/ios/chrome/test/earl_grey/chrome_earl_grey_ui.mm
index a04aec38..ef0722b 100644
--- a/ios/chrome/test/earl_grey/chrome_earl_grey_ui.mm
+++ b/ios/chrome/test/earl_grey/chrome_earl_grey_ui.mm
@@ -4,7 +4,8 @@
 
 #import "ios/chrome/test/earl_grey/chrome_earl_grey_ui.h"
 
-#include "ios/chrome/browser/ui/tools_menu/tools_menu_constants.h"
+#import "ios/chrome/browser/ui/tools_menu/tools_menu_constants.h"
+#import "ios/chrome/browser/ui/tools_menu/tools_popup_controller.h"
 #import "ios/chrome/browser/ui/uikit_ui_util.h"
 #import "ios/chrome/test/app/chrome_test_util.h"
 #include "ios/chrome/test/app/navigation_test_util.h"
@@ -44,6 +45,16 @@
   // to always find it.
 }
 
++ (void)openSettingsMenu {
+  [ChromeEarlGreyUI openToolsMenu];
+  id<GREYMatcher> toolsMenuTableViewMatcher =
+      grey_accessibilityID(kToolsMenuTableViewId);
+  [[[EarlGrey
+      selectElementWithMatcher:grey_accessibilityID(kToolsMenuSettingsId)]
+         usingSearchAction:grey_scrollToContentEdge(kGREYContentEdgeBottom)
+      onElementWithMatcher:toolsMenuTableViewMatcher] performAction:grey_tap()];
+}
+
 + (void)openNewTab {
   [ChromeEarlGreyUI openToolsMenu];
   id<GREYMatcher> newTabButtonMatcher =
diff --git a/media/base/BUILD.gn b/media/base/BUILD.gn
index b473136..f5ef5c4a 100644
--- a/media/base/BUILD.gn
+++ b/media/base/BUILD.gn
@@ -42,6 +42,9 @@
     "//media/ffmpeg",
   ]
   sources = [
+    "android_overlay_config.cc",
+    "android_overlay_config.h",
+    "android_overlay_mojo_factory_cb.h",
     "audio_block_fifo.cc",
     "audio_block_fifo.h",
     "audio_buffer.cc",
diff --git a/media/base/android/BUILD.gn b/media/base/android/BUILD.gn
index 4fbf153..de899bee 100644
--- a/media/base/android/BUILD.gn
+++ b/media/base/android/BUILD.gn
@@ -20,7 +20,6 @@
       "android_cdm_factory.h",
       "android_overlay.cc",
       "android_overlay.h",
-      "android_overlay_factory.h",
       "android_util.cc",
       "android_util.h",
       "media_codec_bridge.h",
diff --git a/media/base/android/android_overlay.cc b/media/base/android/android_overlay.cc
index 3f705ee5..53fe296 100644
--- a/media/base/android/android_overlay.cc
+++ b/media/base/android/android_overlay.cc
@@ -6,8 +6,4 @@
 
 namespace media {
 
-AndroidOverlay::Config::Config() = default;
-AndroidOverlay::Config::Config(const AndroidOverlay::Config&) = default;
-AndroidOverlay::Config::~Config() = default;
-
 }  // namespace media
diff --git a/media/base/android/android_overlay.h b/media/base/android/android_overlay.h
index 4574191..8b500a85 100644
--- a/media/base/android/android_overlay.h
+++ b/media/base/android/android_overlay.h
@@ -8,6 +8,7 @@
 #include "base/android/scoped_java_ref.h"
 #include "base/callback.h"
 #include "base/macros.h"
+#include "media/base/android_overlay_config.h"
 #include "media/base/media_export.h"
 #include "ui/gfx/geometry/rect.h"
 #include "ui/gl/android/scoped_java_surface.h"
@@ -36,34 +37,6 @@
 // AndroidOverlay isn't technically supposed to do that.
 class MEDIA_EXPORT AndroidOverlay {
  public:
-  // Called when the overlay is ready for use, via |GetJavaSurface()|.
-  using ReadyCB = base::Callback<void(AndroidOverlay*)>;
-
-  // Called when overlay has failed before |ReadyCB| is called.  Will not be
-  // called after ReadyCB.  It will be the last callback for the overlay.
-  using FailedCB = base::Callback<void(AndroidOverlay*)>;
-
-  // Called when the overlay has been destroyed.  This will not be called unless
-  // ReadyCB has been called.  It will be the last callback for the overlay.
-  using DestroyedCB = base::Callback<void(AndroidOverlay*)>;
-
-  // Configuration used to create an overlay.
-  struct Config {
-   public:
-    Config();
-    Config(const Config&);
-    ~Config();
-
-    gfx::Rect rect;
-
-    // Require a secure overlay?
-    bool secure = false;
-
-    ReadyCB ready_cb;
-    FailedCB failed_cb;
-    DestroyedCB destroyed_cb;
-  };
-
   virtual ~AndroidOverlay() {}
 
   // Schedules a relayout of this overlay.  If called before the client is
diff --git a/media/base/android/android_overlay_factory.h b/media/base/android/android_overlay_factory.h
deleted file mode 100644
index a023357..0000000
--- a/media/base/android/android_overlay_factory.h
+++ /dev/null
@@ -1,28 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef MEDIA_BASE_ANDROID_ANDROID_OVERLAY_FACTORY_H_
-#define MEDIA_BASE_ANDROID_ANDROID_OVERLAY_FACTORY_H_
-
-#include "media/base/android/android_overlay.h"
-
-namespace media {
-
-// Base class for overlay factories.
-class AndroidOverlayFactory {
- public:
-  virtual ~AndroidOverlayFactory() {}
-
-  virtual std::unique_ptr<AndroidOverlay> CreateOverlay(
-      const AndroidOverlay::Config& config) = 0;
-
- protected:
-  AndroidOverlayFactory() {}
-
-  DISALLOW_COPY_AND_ASSIGN(AndroidOverlayFactory);
-};
-
-}  // namespace media
-
-#endif  // MEDIA_BASE_ANDROID_ANDROID_OVERLAY_FACTORY_H_
diff --git a/media/base/android/mock_android_overlay.cc b/media/base/android/mock_android_overlay.cc
index 240497ea..98afca5 100644
--- a/media/base/android/mock_android_overlay.cc
+++ b/media/base/android/mock_android_overlay.cc
@@ -5,6 +5,7 @@
 #include "media/base/android/mock_android_overlay.h"
 
 #include "base/bind.h"
+#include "base/memory/ptr_util.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace media {
@@ -17,8 +18,8 @@
 
 MockAndroidOverlay::~MockAndroidOverlay() {}
 
-void MockAndroidOverlay::SetConfig(const Config& config) {
-  config_ = config;
+void MockAndroidOverlay::SetConfig(AndroidOverlayConfig config) {
+  config_ = base::MakeUnique<AndroidOverlayConfig>(std::move(config));
 }
 
 MockAndroidOverlay::Callbacks MockAndroidOverlay::GetCallbacks() {
@@ -34,15 +35,15 @@
 }
 
 void MockAndroidOverlay::OnOverlayReady() {
-  config_.ready_cb.Run(this);
+  config_->is_ready(this);
 }
 
 void MockAndroidOverlay::OnOverlayFailed() {
-  config_.failed_cb.Run(this);
+  config_->is_failed(this);
 }
 
 void MockAndroidOverlay::OnSurfaceDestroyed() {
-  config_.destroyed_cb.Run(this);
+  config_->is_destroyed(this);
 }
 
 }  // namespace media
diff --git a/media/base/android/mock_android_overlay.h b/media/base/android/mock_android_overlay.h
index e6d9728..a841e9f 100644
--- a/media/base/android/mock_android_overlay.h
+++ b/media/base/android/mock_android_overlay.h
@@ -27,7 +27,7 @@
   // Set |config_|.  Sometimes, it's convenient to do this after construction,
   // especially if one must create the overlay before the factory provides it
   // via CreateOverlay.  That's helpful to set test expectations.
-  void SetConfig(const Config& config);
+  void SetConfig(AndroidOverlayConfig config);
 
   // Set of callbacks that we provide to control the overlay once you've handed
   // off ownership of it.  Will return false if the overlay has been destroyed.
@@ -52,7 +52,7 @@
   void OnSurfaceDestroyed();
 
   // Initial configuration, mostly for callbacks.
-  Config config_;
+  std::unique_ptr<AndroidOverlayConfig> config_;
 
   base::WeakPtrFactory<MockAndroidOverlay> weak_factory_;
 
diff --git a/media/base/android_overlay_config.cc b/media/base/android_overlay_config.cc
new file mode 100644
index 0000000..41e4aa09
--- /dev/null
+++ b/media/base/android_overlay_config.cc
@@ -0,0 +1,13 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "media/base/android_overlay_config.h"
+
+namespace media {
+
+AndroidOverlayConfig::AndroidOverlayConfig() = default;
+AndroidOverlayConfig::AndroidOverlayConfig(AndroidOverlayConfig&&) = default;
+AndroidOverlayConfig::~AndroidOverlayConfig() = default;
+
+}  // namespace media
diff --git a/media/base/android_overlay_config.h b/media/base/android_overlay_config.h
new file mode 100644
index 0000000..de23d7ad
--- /dev/null
+++ b/media/base/android_overlay_config.h
@@ -0,0 +1,63 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MEDIA_BASE_ANDROID_OVERLAY_CONFIG_H_
+#define MEDIA_BASE_ANDROID_OVERLAY_CONFIG_H_
+
+#include "base/callback.h"
+#include "base/macros.h"
+#include "media/base/media_export.h"
+#include "ui/gfx/geometry/rect.h"
+
+namespace media {
+
+class AndroidOverlay;
+
+// Configuration used to create an overlay.
+struct MEDIA_EXPORT AndroidOverlayConfig {
+ public:
+  // Called when the overlay is ready for use, via |GetJavaSurface()|.
+  using ReadyCB = base::OnceCallback<void(AndroidOverlay*)>;
+
+  // Called when overlay has failed before |ReadyCB| is called.  Will not be
+  // called after ReadyCB.  It will be the last callback for the overlay.
+  using FailedCB = base::OnceCallback<void(AndroidOverlay*)>;
+
+  // Called when the overlay has been destroyed.  This will not be called unless
+  // ReadyCB has been called.  It will be the last callback for the overlay.
+  using DestroyedCB = base::OnceCallback<void(AndroidOverlay*)>;
+
+  // Configuration used to create an overlay.
+  AndroidOverlayConfig();
+  AndroidOverlayConfig(AndroidOverlayConfig&&);
+  ~AndroidOverlayConfig();
+
+  // Initial rectangle for the overlay.  May be changed via ScheduleLayout().
+  gfx::Rect rect;
+
+  // Require a secure overlay?
+  bool secure = false;
+
+  // Convenient helpers since the syntax is weird.
+  void is_ready(AndroidOverlay* overlay) { std::move(ready_cb).Run(overlay); }
+  void is_failed(AndroidOverlay* overlay) { std::move(failed_cb).Run(overlay); }
+  void is_destroyed(AndroidOverlay* overlay) {
+    std::move(destroyed_cb).Run(overlay);
+  }
+
+  ReadyCB ready_cb;
+  FailedCB failed_cb;
+  DestroyedCB destroyed_cb;
+
+  DISALLOW_COPY(AndroidOverlayConfig);
+};
+
+// Common factory type.
+using AndroidOverlayFactoryCB =
+    base::RepeatingCallback<std::unique_ptr<AndroidOverlay>(
+        AndroidOverlayConfig)>;
+
+}  // namespace media
+
+#endif  // MEDIA_BASE_ANDROID_OVERLAY_CONFIG_H_
diff --git a/media/base/android_overlay_mojo_factory.h b/media/base/android_overlay_mojo_factory.h
new file mode 100644
index 0000000..04458bd
--- /dev/null
+++ b/media/base/android_overlay_mojo_factory.h
@@ -0,0 +1,20 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MEDIA_BASE_ANDROID_OVERLAY_MOJO_FACTORY_CB_H_
+#define MEDIA_BASE_ANDROID_OVERLAY_MOJO_FACTORY_CB_H_
+
+#include "base/callback.h"
+#include "base/macros.h"
+#include "media/base/android_overlay_config.h"
+
+namespace media {
+
+// Note that this compiles on non-android too.
+using AndroidOverlayMojoFactoryCB = base::RepeatingCallback<std::unique_ptr<
+    AndroidOverlay>(base::UnguessableToken&, const AndroidOverlayConfig&)>
+
+}  // namespace media
+
+#endif  // MEDIA_BASE_ANDROID_OVERLAY_MOJO_FACTORY_CB_H_
diff --git a/media/capture/video/linux/video_capture_device_factory_linux.cc b/media/capture/video/linux/video_capture_device_factory_linux.cc
index 45d8729..8cc3425d 100644
--- a/media/capture/video/linux/video_capture_device_factory_linux.cc
+++ b/media/capture/video/linux/video_capture_device_factory_linux.cc
@@ -2,6 +2,11 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+// Reduce number of log messages by logging each NOTIMPLEMENTED() only once.
+// // This has to be before any other includes, else default is picked up.
+// // See base/logging.h for details on this.
+// #define NOTIMPLEMENTED_POLICY 5
+
 #include "media/capture/video/linux/video_capture_device_factory_linux.h"
 
 #include <errno.h>
@@ -12,6 +17,7 @@
 #include "base/files/file_enumerator.h"
 #include "base/files/scoped_file.h"
 #include "base/posix/eintr_wrapper.h"
+#include "base/strings/string_util.h"
 #include "base/strings/stringprintf.h"
 #include "build/build_config.h"
 
@@ -29,17 +35,17 @@
 
 namespace media {
 
+namespace {
+
 // USB VID and PID are both 4 bytes long.
-static const size_t kVidPidSize = 4;
+const size_t kVidPidSize = 4;
 
 // /sys/class/video4linux/video{N}/device is a symlink to the corresponding
 // USB device info directory.
-static const char kVidPathTemplate[] =
-    "/sys/class/video4linux/%s/device/../idVendor";
-static const char kPidPathTemplate[] =
-    "/sys/class/video4linux/%s/device/../idProduct";
+const char kVidPathTemplate[] = "/sys/class/video4linux/%s/device/../idVendor";
+const char kPidPathTemplate[] = "/sys/class/video4linux/%s/device/../idProduct";
 
-static bool ReadIdFile(const std::string& path, std::string* id) {
+bool ReadIdFile(const std::string& path, std::string* id) {
   char id_buf[kVidPidSize];
   FILE* file = fopen(path.c_str(), "rb");
   if (!file)
@@ -52,7 +58,7 @@
   return true;
 }
 
-static bool HasUsableFormats(int fd, uint32_t capabilities) {
+bool HasUsableFormats(int fd, uint32_t capabilities) {
   if (!(capabilities & V4L2_CAP_VIDEO_CAPTURE))
     return false;
 
@@ -72,10 +78,10 @@
   return false;
 }
 
-static std::list<float> GetFrameRateList(int fd,
-                                         uint32_t fourcc,
-                                         uint32_t width,
-                                         uint32_t height) {
+std::list<float> GetFrameRateList(int fd,
+                                  uint32_t fourcc,
+                                  uint32_t width,
+                                  uint32_t height) {
   std::list<float> frame_rates;
 
   v4l2_frmivalenum frame_interval = {};
@@ -105,9 +111,9 @@
   return frame_rates;
 }
 
-static void GetSupportedFormatsForV4L2BufferType(
+void GetSupportedFormatsForV4L2BufferType(
     int fd,
-    media::VideoCaptureFormats* supported_formats) {
+    VideoCaptureFormats* supported_formats) {
   v4l2_fmtdesc v4l2_format = {};
   v4l2_format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
   for (; HANDLE_EINTR(ioctl(fd, VIDIOC_ENUM_FMT, &v4l2_format)) == 0;
@@ -145,6 +151,8 @@
   }
 }
 
+}  // namespace
+
 VideoCaptureDeviceFactoryLinux::VideoCaptureDeviceFactoryLinux(
     scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner)
     : ui_task_runner_(ui_task_runner) {
@@ -244,22 +252,22 @@
 std::string VideoCaptureDeviceFactoryLinux::GetDeviceModelId(
     const std::string& device_id) {
   // |unique_id| is of the form "/dev/video2".  |file_name| is "video2".
-  const std::string dev_dir = "/dev/";
-  DCHECK_EQ(0, device_id.compare(0, dev_dir.length(), dev_dir));
+  const char kDevDir[] = "/dev/";
+  DCHECK(base::StartsWith(device_id, kDevDir, base::CompareCase::SENSITIVE));
   const std::string file_name =
-      device_id.substr(dev_dir.length(), device_id.length());
-
-  const std::string vidPath =
-      base::StringPrintf(kVidPathTemplate, file_name.c_str());
-  const std::string pidPath =
-      base::StringPrintf(kPidPathTemplate, file_name.c_str());
+      device_id.substr(strlen(kDevDir), device_id.length());
 
   std::string usb_id;
-  if (!ReadIdFile(vidPath, &usb_id))
-    return "";
+  const std::string vid_path =
+      base::StringPrintf(kVidPathTemplate, file_name.c_str());
+  if (!ReadIdFile(vid_path, &usb_id))
+    return usb_id;
+
   usb_id.append(":");
-  if (!ReadIdFile(pidPath, &usb_id))
-    return "";
+  const std::string pid_path =
+      base::StringPrintf(kPidPathTemplate, file_name.c_str());
+  if (!ReadIdFile(pid_path, &usb_id))
+    usb_id.clear();
 
   return usb_id;
 }
diff --git a/media/gpu/BUILD.gn b/media/gpu/BUILD.gn
index dc9c400..7947c37 100644
--- a/media/gpu/BUILD.gn
+++ b/media/gpu/BUILD.gn
@@ -198,8 +198,6 @@
       "content_video_view_overlay.h",
       "content_video_view_overlay_allocator.cc",
       "content_video_view_overlay_allocator.h",
-      "content_video_view_overlay_factory.cc",
-      "content_video_view_overlay_factory.h",
       "surface_texture_gl_owner.cc",
       "surface_texture_gl_owner.h",
     ]
diff --git a/media/gpu/android_video_decode_accelerator.cc b/media/gpu/android_video_decode_accelerator.cc
index b6f8c70..cd76298c 100644
--- a/media/gpu/android_video_decode_accelerator.cc
+++ b/media/gpu/android_video_decode_accelerator.cc
@@ -38,7 +38,6 @@
 #include "media/gpu/android_video_surface_chooser_impl.h"
 #include "media/gpu/avda_picture_buffer_manager.h"
 #include "media/gpu/content_video_view_overlay.h"
-#include "media/gpu/content_video_view_overlay_factory.h"
 #include "media/gpu/shared_memory_region.h"
 #include "media/video/picture.h"
 #include "ui/gl/android/scoped_java_surface.h"
@@ -119,6 +118,13 @@
           platform_config.sdk_int <= 18);
 }
 
+std::unique_ptr<AndroidOverlay> CreateContentVideoViewOverlay(
+    int32_t surface_id,
+    AndroidOverlayConfig config) {
+  return base::MakeUnique<ContentVideoViewOverlay>(surface_id,
+                                                   std::move(config));
+}
+
 }  // namespace
 
 // AVDAManager manages a RepeatingTimer so that AVDAs can get a regular callback
@@ -399,11 +405,9 @@
   }
 
   // If we have a surface, then notify |surface_chooser_| about it.
-  std::unique_ptr<AndroidOverlayFactory> factory;
-  if (config_.surface_id != SurfaceManager::kNoSurfaceID) {
-    factory =
-        base::MakeUnique<ContentVideoViewOverlayFactory>(config_.surface_id);
-  }
+  AndroidOverlayFactoryCB factory;
+  if (config_.surface_id != SurfaceManager::kNoSurfaceID)
+    factory = base::Bind(&CreateContentVideoViewOverlay, config_.surface_id);
 
   // Notify |surface_chooser_| that we've started.  This guarantees that we'll
   // get a callback.  It might not be a synchronous callback, but we're not in
@@ -1271,9 +1275,9 @@
     return;
   }
 
-  std::unique_ptr<AndroidOverlayFactory> factory;
+  AndroidOverlayFactoryCB factory;
   if (surface_id != SurfaceManager::kNoSurfaceID)
-    factory = base::MakeUnique<ContentVideoViewOverlayFactory>(surface_id);
+    factory = base::Bind(&CreateContentVideoViewOverlay, surface_id);
 
   surface_chooser_->ReplaceOverlayFactory(std::move(factory));
 }
diff --git a/media/gpu/android_video_decode_accelerator_unittest.cc b/media/gpu/android_video_decode_accelerator_unittest.cc
index 73740636..54592fd8 100644
--- a/media/gpu/android_video_decode_accelerator_unittest.cc
+++ b/media/gpu/android_video_decode_accelerator_unittest.cc
@@ -234,9 +234,9 @@
 // Surface chooser that calls mocked functions to allow counting calls, but
 // also records arguments.  Note that gmock can't mock out unique_ptrs
 // anyway, so we can't mock AndroidVideoSurfaceChooser directly.
-class FakeOverlayHelper : public NiceMock<AndroidVideoSurfaceChooser> {
+class FakeOverlayChooser : public NiceMock<AndroidVideoSurfaceChooser> {
  public:
-  FakeOverlayHelper() : weak_factory_(this) {}
+  FakeOverlayChooser() : weak_factory_(this) {}
 
   // These are called by the real functions.  You may set expectations on
   // them if you like.
@@ -245,15 +245,14 @@
 
   // We guarantee that we'll only clear this during destruction, so that you
   // may treat it as "pointer that lasts as long as |this| does".
-  base::WeakPtr<FakeOverlayHelper> GetWeakPtrForTesting() {
+  base::WeakPtr<FakeOverlayChooser> GetWeakPtrForTesting() {
     return weak_factory_.GetWeakPtr();
   }
 
-  void Initialize(
-      UseOverlayCB use_overlay_cb,
-      UseSurfaceTextureCB use_surface_texture_cb,
-      StopUsingOverlayImmediatelyCB stop_immediately_cb,
-      std::unique_ptr<AndroidOverlayFactory> initial_factory) override {
+  void Initialize(UseOverlayCB use_overlay_cb,
+                  UseSurfaceTextureCB use_surface_texture_cb,
+                  StopUsingOverlayImmediatelyCB stop_immediately_cb,
+                  AndroidOverlayFactoryCB initial_factory) override {
     MockInitialize();
 
     factory_ = std::move(initial_factory);
@@ -262,8 +261,7 @@
     stop_immediately_cb_ = std::move(stop_immediately_cb);
   }
 
-  void ReplaceOverlayFactory(
-      std::unique_ptr<AndroidOverlayFactory> factory) override {
+  void ReplaceOverlayFactory(AndroidOverlayFactoryCB factory) override {
     MockReplaceOverlayFactory();
     factory_ = std::move(factory);
   }
@@ -288,12 +286,12 @@
   UseSurfaceTextureCB use_surface_texture_cb_;
   StopUsingOverlayImmediatelyCB stop_immediately_cb_;
 
-  std::unique_ptr<AndroidOverlayFactory> factory_;
+  AndroidOverlayFactoryCB factory_;
 
-  base::WeakPtrFactory<FakeOverlayHelper> weak_factory_;
+  base::WeakPtrFactory<FakeOverlayChooser> weak_factory_;
 
  private:
-  DISALLOW_COPY_AND_ASSIGN(FakeOverlayHelper);
+  DISALLOW_COPY_AND_ASSIGN(FakeOverlayChooser);
 };
 
 }  // namespace
@@ -318,7 +316,7 @@
                                          gl::GLContextAttribs());
     context_->MakeCurrent(surface_.get());
 
-    chooser_that_is_usually_null_ = base::MakeUnique<FakeOverlayHelper>();
+    chooser_that_is_usually_null_ = base::MakeUnique<FakeOverlayChooser>();
     chooser_ = chooser_that_is_usually_null_->GetWeakPtrForTesting();
 
     platform_config_.sdk_int = base::android::SDK_VERSION_MARSHMALLOW;
@@ -350,7 +348,7 @@
     config_.surface_id = 123;
     ASSERT_TRUE(InitializeAVDA());
     base::RunLoop().RunUntilIdle();
-    ASSERT_TRUE(chooser_->factory_ != nullptr);
+    ASSERT_TRUE(chooser_->factory_);
 
     // Have the factory provide an overlay, and verify that codec creation is
     // provided with that overlay.
@@ -371,7 +369,7 @@
     ASSERT_TRUE(InitializeAVDA());
     base::RunLoop().RunUntilIdle();
     // We do not expect a factory, since we are using SurfaceTexture.
-    ASSERT_TRUE(chooser_->factory_ == nullptr);
+    ASSERT_FALSE(chooser_->factory_);
 
     // Set the expectations first, since ProvideOverlay might cause callbacks.
     EXPECT_CALL(codec_allocator_,
@@ -413,7 +411,7 @@
   AndroidVideoDecodeAccelerator::PlatformConfig platform_config_;
 
   // We maintain a weak ref to this since AVDA owns it.
-  base::WeakPtr<FakeOverlayHelper> chooser_;
+  base::WeakPtr<FakeOverlayChooser> chooser_;
 
   // This must be a unique pointer to a VDA, not an AVDA, to ensure the
   // the default_delete specialization that calls Destroy() will be used.
@@ -426,7 +424,7 @@
  private:
   // This is the object that |chooser_| points to, or nullptr once we assign
   // ownership to AVDA.
-  std::unique_ptr<FakeOverlayHelper> chooser_that_is_usually_null_;
+  std::unique_ptr<FakeOverlayChooser> chooser_that_is_usually_null_;
 };
 
 TEST_F(AndroidVideoDecodeAcceleratorTest, ConfigureUnsupportedCodec) {
diff --git a/media/gpu/android_video_surface_chooser.h b/media/gpu/android_video_surface_chooser.h
index ce669a4..b8d4a05 100644
--- a/media/gpu/android_video_surface_chooser.h
+++ b/media/gpu/android_video_surface_chooser.h
@@ -9,7 +9,6 @@
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
 #include "media/base/android/android_overlay.h"
-#include "media/base/android/android_overlay_factory.h"
 #include "media/gpu/media_gpu_export.h"
 
 namespace media {
@@ -20,33 +19,33 @@
   // Notify the client that |overlay| is ready for use.  The client may get
   // the surface immediately.
   using UseOverlayCB =
-      base::Callback<void(std::unique_ptr<AndroidOverlay> overlay)>;
+      base::RepeatingCallback<void(std::unique_ptr<AndroidOverlay> overlay)>;
 
   // Notify the client that the most recently provided overlay should be
   // discarded.  The overlay is still valid, but we recommend against
   // using it soon, in favor of a SurfaceTexture.
-  using UseSurfaceTextureCB = base::Callback<void(void)>;
+  using UseSurfaceTextureCB = base::RepeatingCallback<void(void)>;
 
   // Callback that mirrors AndroidOverlay::DestroyedCB .  The surface
   // that was provided with |overlay| is being destroyed.
-  using StopUsingOverlayImmediatelyCB = base::Callback<void(AndroidOverlay*)>;
+  using StopUsingOverlayImmediatelyCB =
+      base::RepeatingCallback<void(AndroidOverlay*)>;
 
   AndroidVideoSurfaceChooser() {}
   virtual ~AndroidVideoSurfaceChooser() {}
 
   // Notify us that our client is ready for overlays.  We will send it a
   // callback telling it whether to start with a SurfaceTexture or overlay,
-  // either synchronously or post one very soon.
-  virtual void Initialize(
-      UseOverlayCB use_overlay_cb,
-      UseSurfaceTextureCB use_surface_texture_cb,
-      StopUsingOverlayImmediatelyCB stop_immediately_cb,
-      std::unique_ptr<AndroidOverlayFactory> initial_factory) = 0;
+  // either synchronously or post one very soon.  |initial_factory| can be
+  // an empty callback to indicate "no factory".
+  virtual void Initialize(UseOverlayCB use_overlay_cb,
+                          UseSurfaceTextureCB use_surface_texture_cb,
+                          StopUsingOverlayImmediatelyCB stop_immediately_cb,
+                          AndroidOverlayFactoryCB initial_factory) = 0;
 
-  // Notify us that a new factory has arrived.  May be null to indicate that
-  // the most recent factory has been revoked.
-  virtual void ReplaceOverlayFactory(
-      std::unique_ptr<AndroidOverlayFactory> factory) = 0;
+  // Notify us that a new factory has arrived.  May be is_null() to indicate
+  // that the most recent factory has been revoked.
+  virtual void ReplaceOverlayFactory(AndroidOverlayFactoryCB factory) = 0;
 
  private:
   DISALLOW_COPY_AND_ASSIGN(AndroidVideoSurfaceChooser);
diff --git a/media/gpu/android_video_surface_chooser_impl.cc b/media/gpu/android_video_surface_chooser_impl.cc
index d1df413..486a274b 100644
--- a/media/gpu/android_video_surface_chooser_impl.cc
+++ b/media/gpu/android_video_surface_chooser_impl.cc
@@ -15,7 +15,7 @@
     UseOverlayCB use_overlay_cb,
     UseSurfaceTextureCB use_surface_texture_cb,
     StopUsingOverlayImmediatelyCB stop_immediately_cb,
-    std::unique_ptr<AndroidOverlayFactory> initial_factory) {
+    AndroidOverlayFactoryCB initial_factory) {
   use_overlay_cb_ = std::move(use_overlay_cb);
   use_surface_texture_cb_ = std::move(use_surface_texture_cb);
   stop_immediately_cb_ = std::move(stop_immediately_cb);
@@ -39,7 +39,7 @@
 }
 
 void AndroidVideoSurfaceChooserImpl::ReplaceOverlayFactory(
-    std::unique_ptr<AndroidOverlayFactory> factory) {
+    AndroidOverlayFactoryCB factory) {
   // If we have an overlay, then we should transition away from it.  It
   // doesn't matter if we have a new factory or no factory; the old overlay goes
   // with the old factory.
@@ -69,7 +69,7 @@
   // a SurfaceTexture then be unable to switch.  Perhaps there should be a
   // pre-M implementation of this class that guarantees that it won't change
   // between the two.
-  AndroidOverlay::Config config;
+  AndroidOverlayConfig config;
   // We bind all of our callbacks with weak ptrs, since we don't know how long
   // the client will hold on to overlays.  They could, in principle, show up
   // long after the client is destroyed too, if codec destruction hangs.
@@ -85,7 +85,7 @@
   // set via the natural size, and this is ignored anyway.  The client should
   // provide this.
   config.rect = gfx::Rect(0, 0, 1, 1);
-  overlay_ = overlay_factory_->CreateOverlay(config);
+  overlay_ = overlay_factory_.Run(std::move(config));
 }
 
 void AndroidVideoSurfaceChooserImpl::OnOverlayReady(AndroidOverlay* overlay) {
diff --git a/media/gpu/android_video_surface_chooser_impl.h b/media/gpu/android_video_surface_chooser_impl.h
index 9b9d4927..d8c72bd 100644
--- a/media/gpu/android_video_surface_chooser_impl.h
+++ b/media/gpu/android_video_surface_chooser_impl.h
@@ -9,7 +9,6 @@
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
 #include "media/base/android/android_overlay.h"
-#include "media/base/android/android_overlay_factory.h"
 #include "media/gpu/android_video_surface_chooser.h"
 #include "media/gpu/media_gpu_export.h"
 
@@ -23,13 +22,11 @@
   ~AndroidVideoSurfaceChooserImpl() override;
 
   // AndroidVideoSurfaceChooser
-  void Initialize(
-      UseOverlayCB use_overlay_cb,
-      UseSurfaceTextureCB use_surface_texture_cb,
-      StopUsingOverlayImmediatelyCB stop_immediately_cb,
-      std::unique_ptr<AndroidOverlayFactory> initial_factory) override;
-  void ReplaceOverlayFactory(
-      std::unique_ptr<AndroidOverlayFactory> factory) override;
+  void Initialize(UseOverlayCB use_overlay_cb,
+                  UseSurfaceTextureCB use_surface_texture_cb,
+                  StopUsingOverlayImmediatelyCB stop_immediately_cb,
+                  AndroidOverlayFactoryCB initial_factory) override;
+  void ReplaceOverlayFactory(AndroidOverlayFactoryCB factory) override;
 
  private:
   // AndroidOverlay callbacks.
@@ -51,7 +48,7 @@
   // or a surface texture.
   bool client_notification_pending_ = false;
 
-  std::unique_ptr<AndroidOverlayFactory> overlay_factory_;
+  AndroidOverlayFactoryCB overlay_factory_;
 
   base::WeakPtrFactory<AndroidVideoSurfaceChooserImpl> weak_factory_;
 
diff --git a/media/gpu/android_video_surface_chooser_impl_unittest.cc b/media/gpu/android_video_surface_chooser_impl_unittest.cc
index b30cfbf..58023076b 100644
--- a/media/gpu/android_video_surface_chooser_impl_unittest.cc
+++ b/media/gpu/android_video_surface_chooser_impl_unittest.cc
@@ -11,7 +11,6 @@
 #include "base/bind.h"
 #include "base/logging.h"
 #include "base/memory/ptr_util.h"
-#include "media/base/android/android_overlay_factory.h"
 #include "media/base/android/mock_android_overlay.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -25,7 +24,6 @@
 
 namespace {
 using ::media::AndroidOverlay;
-using ::media::AndroidOverlayFactory;
 using ::media::MockAndroidOverlay;
 
 class MockClient {
@@ -47,47 +45,6 @@
   std::unique_ptr<AndroidOverlay> overlay_;
 };
 
-// Mock factory which will return one overlay.
-class MockAndroidOverlayFactory : public AndroidOverlayFactory {
- public:
-  MockAndroidOverlayFactory()
-      : overlay_(base::MakeUnique<MockAndroidOverlay>()),
-        weak_this_factory_(this) {}
-
-  // Called by CreateOverlay.
-  MOCK_METHOD0(MockCreateOverlay, void());
-
-  std::unique_ptr<AndroidOverlay> CreateOverlay(
-      const AndroidOverlay::Config& config) override {
-    MockCreateOverlay();
-
-    // Notify the overlay  about the config that was used to create it.  We
-    // can't do this during overlay construction since we might want the
-    // overlay to set test expectations.
-    if (overlay_)
-      overlay_->SetConfig(config);
-
-    return std::move(overlay_);
-  }
-
-  // Return the overlay, if we still own it.  One the client creates an
-  // overlay, we'll re-assign ownership.
-  MockAndroidOverlay* overlay() { return overlay_.get(); }
-
-  // Set the overlay that we'll provide next, or nullptr.
-  void SetOverlay(std::unique_ptr<MockAndroidOverlay> overlay) {
-    overlay_ = std::move(overlay);
-  }
-
-  base::WeakPtr<MockAndroidOverlayFactory> GetWeakPtr() {
-    return weak_this_factory_.GetWeakPtr();
-  }
-
- private:
-  std::unique_ptr<MockAndroidOverlay> overlay_;
-
-  base::WeakPtrFactory<MockAndroidOverlayFactory> weak_this_factory_;
-};
 }  // namespace
 
 namespace media {
@@ -99,15 +56,14 @@
 
   void SetUp() override {
     chooser_ = base::MakeUnique<AndroidVideoSurfaceChooserImpl>();
-    factory_ = base::MakeUnique<NiceMock<MockAndroidOverlayFactory>>();
-    factory_weak_ = factory_->GetWeakPtr();
+    overlay_ = base::MakeUnique<MockAndroidOverlay>();
 
     // We create a destruction observer.  By default, the overlay must not be
     // destroyed until the test completes.  Of course, the test may ask the
     // observer to expect something else.
-    destruction_observer_ = factory_->overlay()->CreateDestructionObserver();
+    destruction_observer_ = overlay_->CreateDestructionObserver();
     destruction_observer_->DoNotAllowDestruction();
-    overlay_callbacks_ = factory_->overlay()->GetCallbacks();
+    overlay_callbacks_ = overlay_->GetCallbacks();
   }
 
   void TearDown() override {
@@ -119,7 +75,8 @@
     destruction_observer_ = nullptr;
   }
 
-  void StartHelper(std::unique_ptr<MockAndroidOverlayFactory> factory) {
+  // Start the chooser, providing |factory| as the initial factory.
+  void StartChooser(AndroidOverlayFactoryCB factory) {
     chooser_->Initialize(
         base::Bind(&MockClient::UseOverlayImpl, base::Unretained(&client_)),
         base::Bind(&MockClient::UseSurfaceTexture, base::Unretained(&client_)),
@@ -128,10 +85,48 @@
         std::move(factory));
   }
 
+  // AndroidOverlayFactoryCB is a RepeatingCallback, so we can't just bind
+  // something that uses unique_ptr.  RepeatingCallback needs to copy it.
+  class Factory {
+   public:
+    Factory(std::unique_ptr<MockAndroidOverlay> overlay,
+            base::RepeatingCallback<void()> create_overlay_cb)
+        : overlay_(std::move(overlay)),
+          create_overlay_cb_(std::move(create_overlay_cb)) {}
+
+    // Return whatever overlay we're given.  This is used to construct factory
+    // callbacks for the chooser.
+    std::unique_ptr<AndroidOverlay> ReturnOverlay(AndroidOverlayConfig config) {
+      // Notify the mock.
+      create_overlay_cb_.Run();
+      if (overlay_)
+        overlay_->SetConfig(std::move(config));
+      return std::move(overlay_);
+    }
+
+   private:
+    std::unique_ptr<MockAndroidOverlay> overlay_;
+    base::RepeatingCallback<void()> create_overlay_cb_;
+  };
+
+  // Create a factory that will return |overlay| when run.
+  AndroidOverlayFactoryCB FactoryFor(
+      std::unique_ptr<MockAndroidOverlay> overlay) {
+    Factory* factory = new Factory(
+        std::move(overlay),
+        base::Bind(&AndroidVideoSurfaceChooserImplTest::MockOnOverlayCreated,
+                   base::Unretained(this)));
+
+    // Leaky!
+    return base::Bind(&Factory::ReturnOverlay, base::Unretained(factory));
+  }
+
+  // Called by the factory when it's run.
+  MOCK_METHOD0(MockOnOverlayCreated, void());
+
   std::unique_ptr<AndroidVideoSurfaceChooserImpl> chooser_;
   StrictMock<MockClient> client_;
-  std::unique_ptr<NiceMock<MockAndroidOverlayFactory>> factory_;
-  base::WeakPtr<MockAndroidOverlayFactory> factory_weak_;
+  std::unique_ptr<MockAndroidOverlay> overlay_;
 
   // Callbacks to control the overlay that will be vended by |factory_|
   MockAndroidOverlay::Callbacks overlay_callbacks_;
@@ -145,7 +140,7 @@
   // Calling Initialize() with no factory should result in a callback to use
   // surface texture.
   EXPECT_CALL(client_, UseSurfaceTexture());
-  StartHelper(nullptr);
+  StartChooser(AndroidOverlayFactoryCB());
 }
 
 TEST_F(AndroidVideoSurfaceChooserImplTest, ProvideInitialOverlaySuccessfully) {
@@ -155,7 +150,7 @@
   // just don't differentiate those cases yet in the impl.
 
   EXPECT_CALL(client_, UseSurfaceTexture()).Times(0);
-  StartHelper(std::move(factory_));
+  StartChooser(FactoryFor(std::move(overlay_)));
 
   // Notify the chooser that the overlay is ready.  Expect that |client_| will
   // be told to use it.
@@ -173,8 +168,8 @@
   // Initially, there should be no callback into |client_|, since we haven't
   // told |chooser_| that the overlay is ready.  It should, however, request the
   // overlay from |factory_|.
-  EXPECT_CALL(*factory_, MockCreateOverlay());
-  StartHelper(std::move(factory_));
+  EXPECT_CALL(*this, MockOnOverlayCreated());
+  StartChooser(FactoryFor(std::move(overlay_)));
 }
 
 TEST_F(AndroidVideoSurfaceChooserImplTest,
@@ -182,23 +177,20 @@
   // If we provide a factory, but it fails to create an overlay, then |client_|
   // should be notified to use a surface texture.
 
-  EXPECT_CALL(*factory_, MockCreateOverlay());
+  EXPECT_CALL(*this, MockOnOverlayCreated());
   EXPECT_CALL(client_, UseSurfaceTexture());
-  // Replacing the overlay with null will destroy it.
-  destruction_observer_->ExpectDestruction();
-  factory_->SetOverlay(nullptr);
-  StartHelper(std::move(factory_));
+  StartChooser(FactoryFor(nullptr));
 }
 
 TEST_F(AndroidVideoSurfaceChooserImplTest,
        FailedInitialOverlayUsesSurfaceTexture) {
   // If we provide a factory, but the overlay that it provides returns 'failed',
   // then |client_| should use surface texture.
-  EXPECT_CALL(*factory_, MockCreateOverlay());
-  StartHelper(std::move(factory_));
+  EXPECT_CALL(*this, MockOnOverlayCreated());
+  StartChooser(FactoryFor(std::move(overlay_)));
 
   testing::Mock::VerifyAndClearExpectations(&client_);
-  testing::Mock::VerifyAndClearExpectations(factory_weak_.get());
+  testing::Mock::VerifyAndClearExpectations(this);
 
   // The overlay may be destroyed at any time after we send OverlayFailed.  It
   // doesn't have to be destroyed.  We just care that it hasn't been destroyed
@@ -213,17 +205,17 @@
   // If |chooser_| is notified about OnSurfaceDestroyed, then |client_| should
   // also be notified.
 
-  EXPECT_CALL(*factory_, MockCreateOverlay());
-  StartHelper(std::move(factory_));
+  EXPECT_CALL(*this, MockOnOverlayCreated());
+  StartChooser(FactoryFor(std::move(overlay_)));
   EXPECT_CALL(client_, UseOverlay(NotNull()));
   overlay_callbacks_.OverlayReady.Run();
 
   testing::Mock::VerifyAndClearExpectations(&client_);
-  testing::Mock::VerifyAndClearExpectations(factory_weak_.get());
+  testing::Mock::VerifyAndClearExpectations(this);
 
   // Switch to a surface texture.  OnSurfaceDestroyed should still be sent.
   EXPECT_CALL(client_, UseSurfaceTexture());
-  chooser_->ReplaceOverlayFactory(nullptr);
+  chooser_->ReplaceOverlayFactory(AndroidOverlayFactoryCB());
   testing::Mock::VerifyAndClearExpectations(&client_);
 
   EXPECT_CALL(client_, StopUsingOverlayImmediately(NotNull()));
@@ -247,17 +239,17 @@
   // We test these together, since switching the factory is the only way we have
   // to make |chooser_| transition to SurfaceTexture without sending destroyed.
 
-  EXPECT_CALL(*factory_, MockCreateOverlay());
-  StartHelper(std::move(factory_));
+  EXPECT_CALL(*this, MockOnOverlayCreated());
+  StartChooser(FactoryFor(std::move(overlay_)));
   EXPECT_CALL(client_, UseOverlay(NotNull()));
   overlay_callbacks_.OverlayReady.Run();
 
   testing::Mock::VerifyAndClearExpectations(&client_);
-  testing::Mock::VerifyAndClearExpectations(factory_weak_.get());
+  testing::Mock::VerifyAndClearExpectations(this);
 
   // Switch factories, to notify the client back to switch to SurfaceTexture.
   EXPECT_CALL(client_, UseSurfaceTexture());
-  chooser_->ReplaceOverlayFactory(nullptr);
+  chooser_->ReplaceOverlayFactory(AndroidOverlayFactoryCB());
   testing::Mock::VerifyAndClearExpectations(&client_);
 
   // Destroy the original surface.
@@ -272,14 +264,13 @@
 
   // Start with SurfaceTexture.
   EXPECT_CALL(client_, UseSurfaceTexture());
-  StartHelper(nullptr);
+  StartChooser(AndroidOverlayFactoryCB());
   testing::Mock::VerifyAndClearExpectations(&client_);
 
   // Provide a factory that will return a null overlay.
-  EXPECT_CALL(*factory_, MockCreateOverlay());
+  EXPECT_CALL(*this, MockOnOverlayCreated());
   EXPECT_CALL(client_, UseSurfaceTexture()).Times(AnyNumber());
-  chooser_->ReplaceOverlayFactory(std::move(factory_));
-  factory_weak_->SetOverlay(nullptr);
+  chooser_->ReplaceOverlayFactory(FactoryFor(nullptr));
 }
 
 TEST_F(AndroidVideoSurfaceChooserImplTest, FailedLaterOverlayDoesNothing) {
@@ -289,13 +280,13 @@
 
   // Start with SurfaceTexture.
   EXPECT_CALL(client_, UseSurfaceTexture());
-  StartHelper(nullptr);
+  StartChooser(AndroidOverlayFactoryCB());
   testing::Mock::VerifyAndClearExpectations(&client_);
 
   // Provide a factory.
-  EXPECT_CALL(*factory_, MockCreateOverlay());
+  EXPECT_CALL(*this, MockOnOverlayCreated());
   EXPECT_CALL(client_, UseSurfaceTexture()).Times(AnyNumber());
-  chooser_->ReplaceOverlayFactory(std::move(factory_));
+  chooser_->ReplaceOverlayFactory(FactoryFor(std::move(overlay_)));
   testing::Mock::VerifyAndClearExpectations(&client_);
 
   // Fail the overlay.  We don't care if it's destroyed after that, as long as
@@ -310,17 +301,17 @@
 
   // Start with SurfaceTexture.
   EXPECT_CALL(client_, UseSurfaceTexture());
-  StartHelper(nullptr);
+  StartChooser(AndroidOverlayFactoryCB());
   testing::Mock::VerifyAndClearExpectations(&client_);
 
   // Provide a factory.  |chooser_| should try to create an overlay.  We don't
   // care if a call to UseSurfaceTexture is elided or not.  Note that AVDA will
   // ignore duplicate calls anyway (MultipleSurfaceTextureCallbacksAreIgnored).
-  EXPECT_CALL(*factory_, MockCreateOverlay());
+  EXPECT_CALL(*this, MockOnOverlayCreated());
   EXPECT_CALL(client_, UseSurfaceTexture()).Times(AnyNumber());
-  chooser_->ReplaceOverlayFactory(std::move(factory_));
+  chooser_->ReplaceOverlayFactory(FactoryFor(std::move(overlay_)));
   testing::Mock::VerifyAndClearExpectations(&client_);
-  testing::Mock::VerifyAndClearExpectations(factory_weak_.get());
+  testing::Mock::VerifyAndClearExpectations(this);
 
   // Notify |chooser_| that the overlay is ready.
   EXPECT_CALL(client_, UseOverlay(NotNull()));
diff --git a/media/gpu/content_video_view_overlay.cc b/media/gpu/content_video_view_overlay.cc
index 31da81e..8fc56f7 100644
--- a/media/gpu/content_video_view_overlay.cc
+++ b/media/gpu/content_video_view_overlay.cc
@@ -11,10 +11,9 @@
 
 namespace media {
 
-ContentVideoViewOverlay::ContentVideoViewOverlay(
-    int surface_id,
-    const AndroidOverlay::Config& config)
-    : surface_id_(surface_id), config_(config), weak_factory_(this) {
+ContentVideoViewOverlay::ContentVideoViewOverlay(int surface_id,
+                                                 AndroidOverlayConfig config)
+    : surface_id_(surface_id), config_(std::move(config)), weak_factory_(this) {
   if (ContentVideoViewOverlayAllocator::GetInstance()->AllocateSurface(this)) {
     // We have the surface -- post a callback to our OnSurfaceAvailable.
     base::ThreadTaskRunnerHandle::Get()->PostTask(
@@ -41,7 +40,7 @@
 void ContentVideoViewOverlay::OnSurfaceAvailable(bool success) {
   if (!success) {
     // Notify that the surface won't be available.
-    config_.failed_cb.Run(this);
+    config_.is_failed(this);
     // |this| may be deleted.
     return;
   }
@@ -52,16 +51,16 @@
 
   // If no surface was returned, then fail instead.
   if (surface_.IsEmpty()) {
-    config_.failed_cb.Run(this);
+    config_.is_failed(this);
     // |this| may be deleted.
     return;
   }
 
-  config_.ready_cb.Run(this);
+  config_.is_ready(this);
 }
 
 void ContentVideoViewOverlay::OnSurfaceDestroyed() {
-  config_.destroyed_cb.Run(this);
+  config_.is_destroyed(this);
   // |this| may be deleted, or deletion might be posted elsewhere.
 }
 
diff --git a/media/gpu/content_video_view_overlay.h b/media/gpu/content_video_view_overlay.h
index ef6a6c4c..9657be9 100644
--- a/media/gpu/content_video_view_overlay.h
+++ b/media/gpu/content_video_view_overlay.h
@@ -17,7 +17,7 @@
  public:
   // |config| is ignored except for callbacks.  Callbacks will not be called
   // before this returns.
-  ContentVideoViewOverlay(int surface_id, const AndroidOverlay::Config& config);
+  ContentVideoViewOverlay(int surface_id, AndroidOverlayConfig config);
   ~ContentVideoViewOverlay() override;
 
   // AndroidOverlay (via ContentVideoViewOverlayAllocator::Client)
@@ -36,7 +36,7 @@
 
  private:
   int surface_id_;
-  AndroidOverlay::Config config_;
+  AndroidOverlayConfig config_;
   gl::ScopedJavaSurface surface_;
 
   base::WeakPtrFactory<ContentVideoViewOverlay> weak_factory_;
diff --git a/media/gpu/content_video_view_overlay_factory.cc b/media/gpu/content_video_view_overlay_factory.cc
deleted file mode 100644
index a284d8a7..0000000
--- a/media/gpu/content_video_view_overlay_factory.cc
+++ /dev/null
@@ -1,26 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "media/gpu/content_video_view_overlay_factory.h"
-
-#include "base/memory/ptr_util.h"
-#include "media/base/surface_manager.h"
-#include "media/gpu/content_video_view_overlay.h"
-
-namespace media {
-
-ContentVideoViewOverlayFactory::ContentVideoViewOverlayFactory(
-    int32_t surface_id)
-    : surface_id_(surface_id) {
-  DCHECK_NE(surface_id_, SurfaceManager::kNoSurfaceID);
-}
-
-ContentVideoViewOverlayFactory::~ContentVideoViewOverlayFactory() {}
-
-std::unique_ptr<AndroidOverlay> ContentVideoViewOverlayFactory::CreateOverlay(
-    const AndroidOverlay::Config& config) {
-  return base::MakeUnique<ContentVideoViewOverlay>(surface_id_, config);
-}
-
-}  // namespace media
diff --git a/media/gpu/content_video_view_overlay_factory.h b/media/gpu/content_video_view_overlay_factory.h
deleted file mode 100644
index a8db495..0000000
--- a/media/gpu/content_video_view_overlay_factory.h
+++ /dev/null
@@ -1,31 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef MEDIA_GPU_CONTENT_VIDEO_VIEW_OVERLAY_FACTORY_H_
-#define MEDIA_GPU_CONTENT_VIDEO_VIEW_OVERLAY_FACTORY_H_
-
-#include "media/base/android/android_overlay_factory.h"
-
-namespace media {
-
-// Factory for CVV-based overlays.  One needs this only to hide the factory
-// impl from AVDA.
-class ContentVideoViewOverlayFactory : public AndroidOverlayFactory {
- public:
-  // |surface_id| must not be kNoSurfaceID.
-  ContentVideoViewOverlayFactory(int32_t surface_id);
-  ~ContentVideoViewOverlayFactory() override;
-
-  std::unique_ptr<AndroidOverlay> CreateOverlay(
-      const AndroidOverlay::Config& config) override;
-
- private:
-  int32_t surface_id_;
-
-  DISALLOW_COPY_AND_ASSIGN(ContentVideoViewOverlayFactory);
-};
-
-}  // namespace media
-
-#endif  // MEDIA_GPU_ANDROID_CONTENT_VIDEO_VIEW_OVERLAY_FACTORY_H_
diff --git a/media/mojo/clients/BUILD.gn b/media/mojo/clients/BUILD.gn
index c77785e..505bdedc 100644
--- a/media/mojo/clients/BUILD.gn
+++ b/media/mojo/clients/BUILD.gn
@@ -45,8 +45,6 @@
     sources += [
       "mojo_android_overlay.cc",
       "mojo_android_overlay.h",
-      "mojo_android_overlay_factory.cc",
-      "mojo_android_overlay_factory.h",
     ]
   }
 
diff --git a/media/mojo/clients/mojo_android_overlay.cc b/media/mojo/clients/mojo_android_overlay.cc
index 025f430..44934cd 100644
--- a/media/mojo/clients/mojo_android_overlay.cc
+++ b/media/mojo/clients/mojo_android_overlay.cc
@@ -11,9 +11,9 @@
 
 MojoAndroidOverlay::MojoAndroidOverlay(
     service_manager::mojom::InterfaceProvider* interface_provider,
-    const AndroidOverlay::Config& config,
+    AndroidOverlayConfig config,
     const base::UnguessableToken& routing_token)
-    : interface_provider_(interface_provider), config_(config) {
+    : interface_provider_(interface_provider), config_(std::move(config)) {
   // Connect to the provider service.
   mojom::AndroidOverlayProviderPtr provider_ptr;
   service_manager::GetInterface<mojom::AndroidOverlayProvider>(
@@ -57,7 +57,7 @@
 void MojoAndroidOverlay::OnSurfaceReady(uint64_t surface_key) {
   // TODO(liberato): ask binder for the surface here, and fill in |surface_|.
   received_surface_ = true;
-  config_.ready_cb.Run(this);
+  config_.is_ready(this);
 }
 
 void MojoAndroidOverlay::OnDestroyed() {
@@ -65,9 +65,9 @@
   // gotten a surface.  Regardless, the overlay cannot be used.
 
   if (!received_surface_)
-    config_.failed_cb.Run(this);
+    config_.is_failed(this);
   else
-    config_.destroyed_cb.Run(this);
+    config_.is_destroyed(this);
 
   // Note: we do not delete |overlay_ptr_| here.  Our client must delete us to
   // signal that we should do that, since it still might be in use.
diff --git a/media/mojo/clients/mojo_android_overlay.h b/media/mojo/clients/mojo_android_overlay.h
index c370577..af52d8f 100644
--- a/media/mojo/clients/mojo_android_overlay.h
+++ b/media/mojo/clients/mojo_android_overlay.h
@@ -25,7 +25,7 @@
  public:
   MojoAndroidOverlay(
       service_manager::mojom::InterfaceProvider* interface_provider,
-      const AndroidOverlay::Config& config,
+      AndroidOverlayConfig config,
       const base::UnguessableToken& routing_token);
 
   ~MojoAndroidOverlay() override;
@@ -40,7 +40,7 @@
 
  private:
   service_manager::mojom::InterfaceProvider* interface_provider_;
-  AndroidOverlay::Config config_;
+  AndroidOverlayConfig config_;
   mojom::AndroidOverlayProviderPtr provider_ptr_;
   mojom::AndroidOverlayPtr overlay_ptr_;
   std::unique_ptr<mojo::Binding<mojom::AndroidOverlayClient>> binding_;
diff --git a/media/mojo/clients/mojo_android_overlay_factory.cc b/media/mojo/clients/mojo_android_overlay_factory.cc
deleted file mode 100644
index aee55949..0000000
--- a/media/mojo/clients/mojo_android_overlay_factory.cc
+++ /dev/null
@@ -1,24 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "media/mojo/clients/mojo_android_overlay_factory.h"
-
-#include "media/mojo/clients/mojo_android_overlay.h"
-
-namespace media {
-
-MojoAndroidOverlayFactory::MojoAndroidOverlayFactory(
-    const base::UnguessableToken& routing_token,
-    service_manager::mojom::InterfaceProvider* interface_provider)
-    : interface_provider_(interface_provider), routing_token_(routing_token) {}
-
-MojoAndroidOverlayFactory::~MojoAndroidOverlayFactory() {}
-
-std::unique_ptr<AndroidOverlay> MojoAndroidOverlayFactory::CreateOverlay(
-    const AndroidOverlay::Config& config) {
-  return base::MakeUnique<MojoAndroidOverlay>(interface_provider_, config,
-                                              routing_token_);
-}
-
-}  // namespace media
diff --git a/media/mojo/clients/mojo_android_overlay_factory.h b/media/mojo/clients/mojo_android_overlay_factory.h
deleted file mode 100644
index dd45b62..0000000
--- a/media/mojo/clients/mojo_android_overlay_factory.h
+++ /dev/null
@@ -1,41 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef MEDIA_BASE_MOJO_ANDROID_OVERLAY_FACTORY_H_
-#define MEDIA_BASE_MOJO_ANDROID_OVERLAY_FACTORY_H_
-
-#include "base/macros.h"
-#include "base/unguessable_token.h"
-#include "media/base/android/android_overlay_factory.h"
-#include "media/mojo/interfaces/android_overlay.mojom.h"
-
-namespace service_manager {
-namespace mojom {
-class InterfaceProvider;
-}
-}
-
-namespace media {
-
-// AndroidOverlayFactory implementation for mojo-based overlays.
-class MojoAndroidOverlayFactory : public AndroidOverlayFactory {
- public:
-  MojoAndroidOverlayFactory(
-      const base::UnguessableToken& routing_token,
-      service_manager::mojom::InterfaceProvider* interface_provider);
-  ~MojoAndroidOverlayFactory() override;
-
-  std::unique_ptr<AndroidOverlay> CreateOverlay(
-      const AndroidOverlay::Config& config) override;
-
- private:
-  service_manager::mojom::InterfaceProvider* const interface_provider_;
-  base::UnguessableToken routing_token_;
-
-  DISALLOW_COPY_AND_ASSIGN(MojoAndroidOverlayFactory);
-};
-
-}  // namespace media
-
-#endif  // MEDIA_BASE_MOJO_ANDROID_OVERLAY_H_
diff --git a/media/mojo/clients/mojo_android_overlay_unittest.cc b/media/mojo/clients/mojo_android_overlay_unittest.cc
index 3e44f1de..c0536f44 100644
--- a/media/mojo/clients/mojo_android_overlay_unittest.cc
+++ b/media/mojo/clients/mojo_android_overlay_unittest.cc
@@ -120,8 +120,8 @@
     EXPECT_CALL(mock_provider_, OverlayCreated());
 
     base::UnguessableToken routing_token = base::UnguessableToken::Create();
-    overlay_client_.reset(
-        new MojoAndroidOverlay(&interface_provider_, config_, routing_token));
+    overlay_client_.reset(new MojoAndroidOverlay(
+        &interface_provider_, std::move(config_), routing_token));
     base::RunLoop().RunUntilIdle();
   }
 
@@ -168,7 +168,7 @@
 
   // Inital config for |CreateOverlay|.
   // Set to sane values, but feel free to modify before CreateOverlay().
-  AndroidOverlay::Config config_;
+  AndroidOverlayConfig config_;
   MockClientCallbacks callbacks_;
 };
 
diff --git a/net/quic/chromium/bidirectional_stream_quic_impl.cc b/net/quic/chromium/bidirectional_stream_quic_impl.cc
index 1d507ae..ca231ba 100644
--- a/net/quic/chromium/bidirectional_stream_quic_impl.cc
+++ b/net/quic/chromium/bidirectional_stream_quic_impl.cc
@@ -21,9 +21,8 @@
 namespace net {
 
 BidirectionalStreamQuicImpl::BidirectionalStreamQuicImpl(
-    const base::WeakPtr<QuicChromiumClientSession>& session)
-    : session_(session),
-      was_handshake_confirmed_(session->IsCryptoHandshakeConfirmed()),
+    std::unique_ptr<QuicChromiumClientSession::Handle> session)
+    : session_(std::move(session)),
       stream_(nullptr),
       request_info_(nullptr),
       delegate_(nullptr),
@@ -38,19 +37,13 @@
       has_sent_headers_(false),
       has_received_headers_(false),
       send_request_headers_automatically_(true),
-      weak_factory_(this) {
-  DCHECK(session_);
-  session_->AddObserver(this);
-}
+      weak_factory_(this) {}
 
 BidirectionalStreamQuicImpl::~BidirectionalStreamQuicImpl() {
   if (stream_) {
     delegate_ = nullptr;
     stream_->Reset(QUIC_STREAM_CANCELLED);
   }
-
-  if (session_)
-    session_->RemoveObserver(this);
 }
 
 void BidirectionalStreamQuicImpl::Start(
@@ -63,25 +56,26 @@
   CHECK(delegate);
 
   send_request_headers_automatically_ = send_request_headers_automatically;
-  if (!session_) {
-    NotifyError(was_handshake_confirmed_ ? ERR_QUIC_PROTOCOL_ERROR
-                                         : ERR_QUIC_HANDSHAKE_FAILED);
+  if (!session_->IsConnected()) {
+    NotifyError(session_->IsCryptoHandshakeConfirmed()
+                    ? ERR_QUIC_PROTOCOL_ERROR
+                    : ERR_QUIC_HANDSHAKE_FAILED);
     return;
   }
 
   delegate_ = delegate;
   request_info_ = request_info;
 
-  stream_request_ =
-      session_->CreateStreamRequest(request_info_->method == "POST");
-  int rv = stream_request_->StartRequest(base::Bind(
-      &BidirectionalStreamQuicImpl::OnStreamReady, weak_factory_.GetWeakPtr()));
+  int rv = session_->RequestStream(
+      request_info_->method == "POST",
+      base::Bind(&BidirectionalStreamQuicImpl::OnStreamReady,
+                 weak_factory_.GetWeakPtr()));
   if (rv == ERR_IO_PENDING)
     return;
 
   if (rv == OK) {
     OnStreamReady(rv);
-  } else if (!was_handshake_confirmed_) {
+  } else if (!session_->IsCryptoHandshakeConfirmed()) {
     NotifyError(ERR_QUIC_HANDSHAKE_FAILED);
   }
 }
@@ -152,8 +146,8 @@
     DCHECK(!send_request_headers_automatically_);
     // Creates a bundler only if there are headers to be sent along with the
     // single data buffer.
-    bundler.reset(new QuicConnection::ScopedPacketBundler(
-        session_->connection(), QuicConnection::SEND_ACK_IF_PENDING));
+    bundler =
+        session_->CreatePacketBundler(QuicConnection::SEND_ACK_IF_PENDING);
     SendRequestHeaders();
   }
 
@@ -184,8 +178,8 @@
     return;
   }
 
-  QuicConnection::ScopedPacketBundler bundler(
-      session_->connection(), QuicConnection::SEND_ACK_IF_PENDING);
+  std::unique_ptr<QuicConnection::ScopedPacketBundler> bundler(
+      session_->CreatePacketBundler(QuicConnection::SEND_ACK_IF_PENDING));
   if (!has_sent_headers_) {
     DCHECK(!send_request_headers_automatically_);
     SendRequestHeaders();
@@ -268,13 +262,13 @@
 }
 
 void BidirectionalStreamQuicImpl::OnClose() {
-  DCHECK(session_);
   DCHECK(stream_);
 
   if (stream_->connection_error() != QUIC_NO_ERROR ||
       stream_->stream_error() != QUIC_STREAM_NO_ERROR) {
-    NotifyError(was_handshake_confirmed_ ? ERR_QUIC_PROTOCOL_ERROR
-                                         : ERR_QUIC_HANDSHAKE_FAILED);
+    NotifyError(session_->IsCryptoHandshakeConfirmed()
+                    ? ERR_QUIC_PROTOCOL_ERROR
+                    : ERR_QUIC_HANDSHAKE_FAILED);
     return;
   }
 
@@ -294,27 +288,11 @@
   NotifyError(error);
 }
 
-void BidirectionalStreamQuicImpl::OnCryptoHandshakeConfirmed() {
-  was_handshake_confirmed_ = true;
-}
-
-void BidirectionalStreamQuicImpl::OnSuccessfulVersionNegotiation(
-    const QuicVersion& version) {}
-
-void BidirectionalStreamQuicImpl::OnSessionClosed(
-    int error,
-    bool /*port_migration_detected*/) {
-  DCHECK_NE(OK, error);
-  session_.reset();
-  NotifyError(error);
-}
-
 void BidirectionalStreamQuicImpl::OnStreamReady(int rv) {
   DCHECK_NE(ERR_IO_PENDING, rv);
   DCHECK(rv == OK || !stream_);
   if (rv == OK) {
-    stream_ = stream_request_->ReleaseStream();
-    stream_request_.reset();
+    stream_ = session_->ReleaseStream();
     stream_->SetDelegate(this);
     NotifyStreamReady();
   } else {
@@ -357,11 +335,6 @@
 }
 
 void BidirectionalStreamQuicImpl::ResetStream() {
-  if (session_) {
-    session_->RemoveObserver(this);
-    session_ = nullptr;
-  }
-
   if (!stream_)
     return;
   closed_stream_received_bytes_ = stream_->stream_bytes_read();
diff --git a/net/quic/chromium/bidirectional_stream_quic_impl.h b/net/quic/chromium/bidirectional_stream_quic_impl.h
index 1256581..4729d7f 100644
--- a/net/quic/chromium/bidirectional_stream_quic_impl.h
+++ b/net/quic/chromium/bidirectional_stream_quic_impl.h
@@ -30,11 +30,10 @@
 
 class NET_EXPORT_PRIVATE BidirectionalStreamQuicImpl
     : public BidirectionalStreamImpl,
-      public QuicChromiumClientStream::Delegate,
-      public QuicChromiumClientSession::Observer {
+      public QuicChromiumClientStream::Delegate {
  public:
   explicit BidirectionalStreamQuicImpl(
-      const base::WeakPtr<QuicChromiumClientSession>& session);
+      std::unique_ptr<QuicChromiumClientSession::Handle> session);
 
   ~BidirectionalStreamQuicImpl() override;
 
@@ -65,11 +64,6 @@
   void OnClose() override;
   void OnError(int error) override;
 
-  // QuicChromiumClientSession::Observer implementation:
-  void OnCryptoHandshakeConfirmed() override;
-  void OnSuccessfulVersionNegotiation(const QuicVersion& version) override;
-  void OnSessionClosed(int error, bool port_migration_detected) override;
-
   void OnStreamReady(int rv);
   void OnSendDataComplete(int rv);
   void OnReadDataComplete(int rv);
@@ -81,9 +75,7 @@
   // Resets the stream and ensures that |delegate_| won't be called back.
   void ResetStream();
 
-  base::WeakPtr<QuicChromiumClientSession> session_;
-  bool was_handshake_confirmed_;  // True if the crypto handshake succeeded.
-  std::unique_ptr<QuicChromiumClientSession::StreamRequest> stream_request_;
+  const std::unique_ptr<QuicChromiumClientSession::Handle> session_;
   QuicChromiumClientStream* stream_;  // Non-owning.
 
   const BidirectionalStreamRequestInfo* request_info_;
diff --git a/net/quic/chromium/bidirectional_stream_quic_impl_unittest.cc b/net/quic/chromium/bidirectional_stream_quic_impl_unittest.cc
index 4aacba8..deb08051 100644
--- a/net/quic/chromium/bidirectional_stream_quic_impl_unittest.cc
+++ b/net/quic/chromium/bidirectional_stream_quic_impl_unittest.cc
@@ -153,8 +153,8 @@
 
   void Start(const BidirectionalStreamRequestInfo* request_info,
              const NetLogWithSource& net_log,
-             const base::WeakPtr<QuicChromiumClientSession> session) {
-    stream_.reset(new BidirectionalStreamQuicImpl(session));
+             std::unique_ptr<QuicChromiumClientSession::Handle> session) {
+    stream_ = base::MakeUnique<BidirectionalStreamQuicImpl>(std::move(session));
     stream_->Start(request_info, net_log, send_request_headers_automatically_,
                    this, nullptr);
   }
@@ -739,7 +739,7 @@
   std::unique_ptr<TestDelegateBase> delegate(
       new TestDelegateBase(read_buffer.get(), kReadBufferSize));
   delegate->set_trailers_expected(true);
-  delegate->Start(&request, net_log().bound(), session()->GetWeakPtr());
+  delegate->Start(&request, net_log().bound(), session()->CreateHandle());
   delegate->WaitUntilNextCallback();  // OnStreamReady
   ConfirmHandshake();
 
@@ -840,13 +840,13 @@
   scoped_refptr<IOBuffer> read_buffer(new IOBuffer(kReadBufferSize));
   std::unique_ptr<TestDelegateBase> delegate(
       new TestDelegateBase(read_buffer.get(), kReadBufferSize));
-  delegate->Start(&request, net_log().bound(), session()->GetWeakPtr());
+  delegate->Start(&request, net_log().bound(), session()->CreateHandle());
 
   // Start second request.
   scoped_refptr<IOBuffer> read_buffer2(new IOBuffer(kReadBufferSize));
   std::unique_ptr<TestDelegateBase> delegate2(
       new TestDelegateBase(read_buffer2.get(), kReadBufferSize));
-  delegate2->Start(&request, net_log().bound(), session()->GetWeakPtr());
+  delegate2->Start(&request, net_log().bound(), session()->CreateHandle());
 
   delegate->WaitUntilNextCallback();   // OnStreamReady
   delegate2->WaitUntilNextCallback();  // OnStreamReady
@@ -918,7 +918,7 @@
   std::unique_ptr<TestDelegateBase> delegate(
       new TestDelegateBase(read_buffer.get(), kReadBufferSize));
   delegate->DoNotSendRequestHeadersAutomatically();
-  delegate->Start(&request, net_log().bound(), session()->GetWeakPtr());
+  delegate->Start(&request, net_log().bound(), session()->CreateHandle());
   EXPECT_FALSE(delegate->is_ready());
   ConfirmHandshake();
   delegate->WaitUntilNextCallback();  // OnStreamReady
@@ -1025,7 +1025,7 @@
   std::unique_ptr<TestDelegateBase> delegate(
       new TestDelegateBase(read_buffer.get(), kReadBufferSize));
   delegate->DoNotSendRequestHeadersAutomatically();
-  delegate->Start(&request, net_log().bound(), session()->GetWeakPtr());
+  delegate->Start(&request, net_log().bound(), session()->CreateHandle());
   ConfirmHandshake();
   delegate->WaitUntilNextCallback();  // OnStreamReady
 
@@ -1124,7 +1124,7 @@
   std::unique_ptr<TestDelegateBase> delegate(
       new TestDelegateBase(read_buffer.get(), kReadBufferSize));
   delegate->DoNotSendRequestHeadersAutomatically();
-  delegate->Start(&request, net_log().bound(), session()->GetWeakPtr());
+  delegate->Start(&request, net_log().bound(), session()->CreateHandle());
   ConfirmHandshake();
   delegate->WaitUntilNextCallback();  // OnStreamReady
 
@@ -1218,7 +1218,7 @@
   scoped_refptr<IOBuffer> read_buffer(new IOBuffer(kReadBufferSize));
   std::unique_ptr<TestDelegateBase> delegate(
       new TestDelegateBase(read_buffer.get(), kReadBufferSize));
-  delegate->Start(&request, net_log().bound(), session()->GetWeakPtr());
+  delegate->Start(&request, net_log().bound(), session()->CreateHandle());
   ConfirmHandshake();
   delegate->WaitUntilNextCallback();  // OnStreamReady
 
@@ -1296,7 +1296,7 @@
   scoped_refptr<IOBuffer> read_buffer(new IOBuffer(kReadBufferSize));
   std::unique_ptr<TestDelegateBase> delegate(
       new TestDelegateBase(read_buffer.get(), kReadBufferSize));
-  delegate->Start(&request, net_log().bound(), session()->GetWeakPtr());
+  delegate->Start(&request, net_log().bound(), session()->CreateHandle());
   delegate->WaitUntilNextCallback();  // OnStreamReady
 
   // Send a DATA frame.
@@ -1377,7 +1377,7 @@
   scoped_refptr<IOBuffer> read_buffer(new IOBuffer(kReadBufferSize));
   std::unique_ptr<TestDelegateBase> delegate(
       new TestDelegateBase(read_buffer.get(), kReadBufferSize));
-  delegate->Start(&request, net_log().bound(), session()->GetWeakPtr());
+  delegate->Start(&request, net_log().bound(), session()->CreateHandle());
   ConfirmHandshake();
   delegate->WaitUntilNextCallback();  // OnStreamReady
 
@@ -1460,7 +1460,7 @@
   scoped_refptr<IOBuffer> read_buffer(new IOBuffer(kReadBufferSize));
   std::unique_ptr<TestDelegateBase> delegate(
       new TestDelegateBase(read_buffer.get(), kReadBufferSize));
-  delegate->Start(&request, net_log().bound(), session()->GetWeakPtr());
+  delegate->Start(&request, net_log().bound(), session()->CreateHandle());
   delegate->WaitUntilNextCallback();  // OnStreamReady
   ConfirmHandshake();
 
@@ -1504,7 +1504,7 @@
   scoped_refptr<IOBuffer> read_buffer(new IOBuffer(kReadBufferSize));
   std::unique_ptr<TestDelegateBase> delegate(
       new TestDelegateBase(read_buffer.get(), kReadBufferSize));
-  delegate->Start(&request, net_log().bound(), session()->GetWeakPtr());
+  delegate->Start(&request, net_log().bound(), session()->CreateHandle());
   delegate->WaitUntilNextCallback();  // OnStreamReady
   ConfirmHandshake();
 
@@ -1562,7 +1562,7 @@
   scoped_refptr<IOBuffer> read_buffer(new IOBuffer(kReadBufferSize));
   std::unique_ptr<TestDelegateBase> delegate(
       new TestDelegateBase(read_buffer.get(), kReadBufferSize));
-  delegate->Start(&request, net_log().bound(), session()->GetWeakPtr());
+  delegate->Start(&request, net_log().bound(), session()->CreateHandle());
   ConfirmHandshake();
   delegate->WaitUntilNextCallback();  // OnStreamReady
 
@@ -1624,7 +1624,7 @@
   scoped_refptr<IOBuffer> read_buffer(new IOBuffer(kReadBufferSize));
   std::unique_ptr<TestDelegateBase> delegate(
       new TestDelegateBase(read_buffer.get(), kReadBufferSize));
-  delegate->Start(&request, net_log().bound(), session()->GetWeakPtr());
+  delegate->Start(&request, net_log().bound(), session()->CreateHandle());
   ConfirmHandshake();
   delegate->WaitUntilNextCallback();  // OnStreamReady
 
@@ -1679,7 +1679,7 @@
   std::unique_ptr<DeleteStreamDelegate> delegate(
       new DeleteStreamDelegate(read_buffer.get(), kReadBufferSize,
                                DeleteStreamDelegate::ON_HEADERS_RECEIVED));
-  delegate->Start(&request, net_log().bound(), session()->GetWeakPtr());
+  delegate->Start(&request, net_log().bound(), session()->CreateHandle());
   ConfirmHandshake();
   delegate->WaitUntilNextCallback();  // OnStreamReady
 
@@ -1725,7 +1725,7 @@
   scoped_refptr<IOBuffer> read_buffer(new IOBuffer(kReadBufferSize));
   std::unique_ptr<DeleteStreamDelegate> delegate(new DeleteStreamDelegate(
       read_buffer.get(), kReadBufferSize, DeleteStreamDelegate::ON_DATA_READ));
-  delegate->Start(&request, net_log().bound(), session()->GetWeakPtr());
+  delegate->Start(&request, net_log().bound(), session()->CreateHandle());
   ConfirmHandshake();
   delegate->WaitUntilNextCallback();  // OnStreamReady
 
@@ -1779,7 +1779,7 @@
   std::unique_ptr<DeleteStreamDelegate> delegate(
       new DeleteStreamDelegate(read_buffer.get(), kReadBufferSize,
                                DeleteStreamDelegate::ON_TRAILERS_RECEIVED));
-  delegate->Start(&request, net_log().bound(), session()->GetWeakPtr());
+  delegate->Start(&request, net_log().bound(), session()->CreateHandle());
   delegate->WaitUntilNextCallback();  // OnStreamReady
 
   // Server acks the request.
diff --git a/net/quic/chromium/quic_chromium_client_session.cc b/net/quic/chromium/quic_chromium_client_session.cc
index 5f44953..e080708 100644
--- a/net/quic/chromium/quic_chromium_client_session.cc
+++ b/net/quic/chromium/quic_chromium_client_session.cc
@@ -187,26 +187,185 @@
 
 }  // namespace
 
+QuicChromiumClientSession::Handle::Handle(
+    const base::WeakPtr<QuicChromiumClientSession>& session)
+    : MultiplexedSessionHandle(session),
+      session_(session),
+      net_log_(session_->net_log()),
+      was_handshake_confirmed_(session->IsCryptoHandshakeConfirmed()),
+      error_(OK),
+      port_migration_detected_(false),
+      server_id_(session_->server_id()),
+      quic_version_(session->connection()->version()) {
+  DCHECK(session_);
+  session_->AddHandle(this);
+}
+
+QuicChromiumClientSession::Handle::~Handle() {
+  if (session_)
+    session_->RemoveHandle(this);
+}
+
+void QuicChromiumClientSession::Handle::OnCryptoHandshakeConfirmed() {
+  was_handshake_confirmed_ = true;
+}
+
+void QuicChromiumClientSession::Handle::OnSessionClosed(
+    QuicVersion quic_version,
+    int error,
+    bool port_migration_detected,
+    LoadTimingInfo::ConnectTiming connect_timing) {
+  session_ = nullptr;
+  port_migration_detected_ = port_migration_detected;
+  error_ = error;
+  quic_version_ = quic_version;
+  connect_timing_ = connect_timing;
+}
+
+bool QuicChromiumClientSession::Handle::IsConnected() const {
+  return session_ != nullptr;
+}
+
+bool QuicChromiumClientSession::Handle::IsCryptoHandshakeConfirmed() const {
+  return was_handshake_confirmed_;
+}
+
+const LoadTimingInfo::ConnectTiming&
+QuicChromiumClientSession::Handle::GetConnectTiming() {
+  if (!session_)
+    return connect_timing_;
+
+  return session_->GetConnectTiming();
+}
+
+Error QuicChromiumClientSession::Handle::GetTokenBindingSignature(
+    crypto::ECPrivateKey* key,
+    TokenBindingType tb_type,
+    std::vector<uint8_t>* out) {
+  if (!session_)
+    return ERR_CONNECTION_CLOSED;
+
+  return session_->GetTokenBindingSignature(key, tb_type, out);
+}
+
+void QuicChromiumClientSession::Handle::PopulateNetErrorDetails(
+    NetErrorDetails* details) const {
+  if (session_) {
+    session_->PopulateNetErrorDetails(details);
+  } else {
+    details->quic_port_migration_detected = port_migration_detected_;
+  }
+}
+
+QuicVersion QuicChromiumClientSession::Handle::GetQuicVersion() const {
+  if (!session_)
+    return quic_version_;
+
+  return session_->connection()->version();
+}
+
+void QuicChromiumClientSession::Handle::ResetPromised(
+    QuicStreamId id,
+    QuicRstStreamErrorCode error_code) {
+  if (session_)
+    session_->ResetPromised(id, error_code);
+}
+
+std::unique_ptr<QuicConnection::ScopedPacketBundler>
+QuicChromiumClientSession::Handle::CreatePacketBundler(
+    QuicConnection::AckBundling bundling_mode) {
+  if (!session_)
+    return nullptr;
+
+  return base::MakeUnique<QuicConnection::ScopedPacketBundler>(
+      session_->connection(), bundling_mode);
+}
+
+bool QuicChromiumClientSession::Handle::SharesSameSession(
+    const Handle& other) const {
+  return session_.get() == other.session_.get();
+}
+
+int QuicChromiumClientSession::Handle::RequestStream(
+    bool requires_confirmation,
+    const CompletionCallback& callback) {
+  DCHECK(!stream_request_);
+
+  if (!session_)
+    return ERR_CONNECTION_CLOSED;
+
+  // base::MakeUnique does not work because the StreamRequest constructor
+  // is private.
+  stream_request_ = std::unique_ptr<StreamRequest>(
+      new StreamRequest(session_->CreateHandle(), requires_confirmation));
+  return stream_request_->StartRequest(callback);
+}
+
+QuicChromiumClientStream* QuicChromiumClientSession::Handle::ReleaseStream() {
+  DCHECK(stream_request_);
+
+  auto* stream = stream_request_->ReleaseStream();
+  stream_request_.reset();
+  return stream;
+}
+
+int QuicChromiumClientSession::Handle::WaitForHandshakeConfirmation(
+    const CompletionCallback& callback) {
+  if (!session_)
+    return ERR_CONNECTION_CLOSED;
+
+  return session_->WaitForHandshakeConfirmation(callback);
+}
+
+void QuicChromiumClientSession::Handle::CancelRequest(StreamRequest* request) {
+  if (session_)
+    session_->CancelRequest(request);
+}
+
+int QuicChromiumClientSession::Handle::TryCreateStream(StreamRequest* request) {
+  if (!session_)
+    return ERR_CONNECTION_CLOSED;
+
+  return session_->TryCreateStream(request);
+}
+
+QuicClientPushPromiseIndex*
+QuicChromiumClientSession::Handle::GetPushPromiseIndex() {
+  if (!session_)
+    return push_promise_index_;
+
+  return session_->push_promise_index();
+}
+
+int QuicChromiumClientSession::Handle::GetPeerAddress(
+    IPEndPoint* address) const {
+  if (!session_)
+    return ERR_CONNECTION_CLOSED;
+
+  *address = session_->peer_address().impl().socket_address();
+  return OK;
+}
+
 QuicChromiumClientSession::StreamRequest::StreamRequest(
-    const base::WeakPtr<QuicChromiumClientSession>& session,
+    std::unique_ptr<QuicChromiumClientSession::Handle> session,
     bool requires_confirmation)
-    : session_(session),
+    : session_(std::move(session)),
       requires_confirmation_(requires_confirmation),
       stream_(nullptr),
-      next_state_(STATE_NONE),
       weak_factory_(this) {}
 
 QuicChromiumClientSession::StreamRequest::~StreamRequest() {
   if (stream_)
     stream_->Reset(QUIC_STREAM_CANCELLED);
 
-  if (session_)
+  if (session_->IsConnected())
     session_->CancelRequest(this);
 }
 
 int QuicChromiumClientSession::StreamRequest::StartRequest(
     const CompletionCallback& callback) {
-  DCHECK(session_);
+  if (!session_->IsConnected())
+    return ERR_CONNECTION_CLOSED;
 
   next_state_ = STATE_WAIT_FOR_CONFIRMATION;
   int rv = DoLoop(OK);
@@ -227,7 +386,7 @@
 void QuicChromiumClientSession::StreamRequest::OnRequestCompleteSuccess(
     QuicChromiumClientStream* stream) {
   DCHECK_EQ(STATE_REQUEST_STREAM_COMPLETE, next_state_);
-  session_.reset();
+
   stream_ = stream;
   // This method is called even when the request completes synchronously.
   if (callback_)
@@ -237,7 +396,6 @@
 void QuicChromiumClientSession::StreamRequest::OnRequestCompleteFailure(
     int rv) {
   DCHECK_EQ(STATE_REQUEST_STREAM_COMPLETE, next_state_);
-  session_.reset();
   // This method is called even when the request completes synchronously.
   if (callback_)
     DoCallback(rv);
@@ -317,7 +475,6 @@
 
 int QuicChromiumClientSession::StreamRequest::DoRequestStreamComplete(int rv) {
   DCHECK(rv == OK || !stream_);
-  session_.reset();
 
   return rv;
 }
@@ -400,18 +557,18 @@
   DCHECK(waiting_for_confirmation_callbacks_.empty());
   if (!dynamic_streams().empty())
     RecordUnexpectedOpenStreams(DESTRUCTOR);
-  if (!observers_.empty())
+  if (!handles_.empty())
     RecordUnexpectedObservers(DESTRUCTOR);
   if (!going_away_)
     RecordUnexpectedNotGoingAway(DESTRUCTOR);
 
-  while (!dynamic_streams().empty() || !observers_.empty() ||
+  while (!dynamic_streams().empty() || !handles_.empty() ||
          !stream_requests_.empty()) {
     // The session must be closed before it is destroyed.
     DCHECK(dynamic_streams().empty());
     CloseAllStreams(ERR_UNEXPECTED);
-    DCHECK(observers_.empty());
-    CloseAllObservers(ERR_UNEXPECTED);
+    DCHECK(handles_.empty());
+    CloseAllHandles(ERR_UNEXPECTED);
     CancelAllRequests(ERR_UNEXPECTED);
 
     connection()->set_debug_visitor(nullptr);
@@ -534,28 +691,23 @@
   return QuicSpdySession::OnStreamFrame(frame);
 }
 
-void QuicChromiumClientSession::AddObserver(Observer* observer) {
+void QuicChromiumClientSession::AddHandle(Handle* handle) {
   if (going_away_) {
     RecordUnexpectedObservers(ADD_OBSERVER);
-    observer->OnSessionClosed(ERR_UNEXPECTED, port_migration_detected_);
+    handle->OnSessionClosed(connection()->version(), ERR_UNEXPECTED,
+                            port_migration_detected_,
+
+                            GetConnectTiming());
     return;
   }
 
-  DCHECK(!base::ContainsKey(observers_, observer));
-  observers_.insert(observer);
+  DCHECK(!base::ContainsKey(handles_, handle));
+  handles_.insert(handle);
 }
 
-void QuicChromiumClientSession::RemoveObserver(Observer* observer) {
-  DCHECK(base::ContainsKey(observers_, observer));
-  observers_.erase(observer);
-}
-
-std::unique_ptr<QuicChromiumClientSession::StreamRequest>
-QuicChromiumClientSession::CreateStreamRequest(bool requires_confirmation) {
-  // base::MakeUnique does not work because the StreamRequest constructor
-  // is private.
-  return std::unique_ptr<StreamRequest>(
-      new StreamRequest(weak_factory_.GetWeakPtr(), requires_confirmation));
+void QuicChromiumClientSession::RemoveHandle(Handle* handle) {
+  DCHECK(base::ContainsKey(handles_, handle));
+  handles_.erase(handle);
 }
 
 int QuicChromiumClientSession::WaitForHandshakeConfirmation(
@@ -966,11 +1118,11 @@
           base::TimeTicks::Now() - connect_timing_.dns_end);
     }
 
-    ObserverSet::iterator it = observers_.begin();
-    while (it != observers_.end()) {
-      Observer* observer = *it;
+    HandleSet::iterator it = handles_.begin();
+    while (it != handles_.end()) {
+      Handle* handle = *it;
       ++it;
-      observer->OnCryptoHandshakeConfirmed();
+      handle->OnCryptoHandshakeConfirmed();
     }
 
     NotifyRequestsOfConfirmation(OK);
@@ -1114,7 +1266,7 @@
   }
   DCHECK(dynamic_streams().empty());
   CloseAllStreams(ERR_UNEXPECTED);
-  CloseAllObservers(ERR_UNEXPECTED);
+  CloseAllHandles(ERR_UNEXPECTED);
   CancelAllRequests(ERR_CONNECTION_CLOSED);
   NotifyRequestsOfConfirmation(ERR_CONNECTION_CLOSED);
   NotifyFactoryOfSessionClosedLater();
@@ -1124,13 +1276,6 @@
     const QuicVersion& version) {
   logger_->OnSuccessfulVersionNegotiation(version);
   QuicSpdySession::OnSuccessfulVersionNegotiation(version);
-
-  ObserverSet::iterator it = observers_.begin();
-  while (it != observers_.end()) {
-    Observer* observer = *it;
-    ++it;
-    observer->OnSuccessfulVersionNegotiation(version);
-  }
 }
 
 int QuicChromiumClientSession::HandleWriteError(
@@ -1336,7 +1481,7 @@
     base::ResetAndReturn(&callback_).Run(net_error);
   }
   CloseAllStreams(net_error);
-  CloseAllObservers(net_error);
+  CloseAllHandles(net_error);
   net_log_.AddEvent(NetLogEventType::QUIC_SESSION_CLOSE_ON_ERROR,
                     NetLog::IntCallback("net_error", net_error));
 
@@ -1357,11 +1502,12 @@
   }
 }
 
-void QuicChromiumClientSession::CloseAllObservers(int net_error) {
-  while (!observers_.empty()) {
-    Observer* observer = *observers_.begin();
-    observers_.erase(observer);
-    observer->OnSessionClosed(net_error, port_migration_detected_);
+void QuicChromiumClientSession::CloseAllHandles(int net_error) {
+  while (!handles_.empty()) {
+    Handle* handle = *handles_.begin();
+    handles_.erase(handle);
+    handle->OnSessionClosed(connection()->version(), net_error,
+                            port_migration_detected_, GetConnectTiming());
   }
 }
 
@@ -1416,9 +1562,10 @@
   return std::move(dict);
 }
 
-base::WeakPtr<QuicChromiumClientSession>
-QuicChromiumClientSession::GetWeakPtr() {
-  return weak_factory_.GetWeakPtr();
+std::unique_ptr<QuicChromiumClientSession::Handle>
+QuicChromiumClientSession::CreateHandle() {
+  return base::MakeUnique<QuicChromiumClientSession::Handle>(
+      weak_factory_.GetWeakPtr());
 }
 
 void QuicChromiumClientSession::OnReadError(
@@ -1514,7 +1661,7 @@
 }
 
 void QuicChromiumClientSession::PopulateNetErrorDetails(
-    NetErrorDetails* details) {
+    NetErrorDetails* details) const {
   details->quic_port_migration_detected = port_migration_detected_;
 }
 
diff --git a/net/quic/chromium/quic_chromium_client_session.h b/net/quic/chromium/quic_chromium_client_session.h
index 5ea905e..2b0ffa28 100644
--- a/net/quic/chromium/quic_chromium_client_session.h
+++ b/net/quic/chromium/quic_chromium_client_session.h
@@ -66,13 +66,117 @@
       public QuicChromiumPacketReader::Visitor,
       public QuicChromiumPacketWriter::Delegate {
  public:
-  // An interface for observing events on a session.
-  class NET_EXPORT_PRIVATE Observer {
+  class StreamRequest;
+
+  // Wrapper for interacting with the session in a restricted fashion which
+  // hides the details of the underlying session's lifetime. All methods of
+  // the Handle are safe to use even after the underlying session is destroyed.
+  class NET_EXPORT_PRIVATE Handle : public MultiplexedSessionHandle {
    public:
-    virtual ~Observer() {}
-    virtual void OnCryptoHandshakeConfirmed() = 0;
-    virtual void OnSuccessfulVersionNegotiation(const QuicVersion& version) = 0;
-    virtual void OnSessionClosed(int error, bool port_migration_detected) = 0;
+    explicit Handle(const base::WeakPtr<QuicChromiumClientSession>& session);
+    Handle(const Handle& other) = delete;
+    ~Handle() override;
+
+    // Returns true if the session is still connected.
+    bool IsConnected() const;
+
+    // Returns true if the handshake has been confirmed.
+    bool IsCryptoHandshakeConfirmed() const;
+
+    // Starts a request to create a stream.  If OK is returned, then
+    // |stream_| will be updated with the newly created stream.  If
+    // ERR_IO_PENDING is returned, then when the request is eventuallly
+    // complete |callback| will be called.
+    int RequestStream(bool requires_confirmation,
+                      const CompletionCallback& callback);
+
+    // Releases |stream_| to the caller.
+    QuicChromiumClientStream* ReleaseStream();
+
+    // Sends Rst for the stream, and makes sure that future calls to
+    // IsClosedStream(id) return true, which ensures that any subsequent
+    // frames related to this stream will be ignored (modulo flow
+    // control accounting).
+    void ResetPromised(QuicStreamId id, QuicRstStreamErrorCode error_code);
+
+    // Returns a new packet bundler while will cause writes to be batched up
+    // until a packet is full, or the last bundler is destroyed.
+    std::unique_ptr<QuicConnection::ScopedPacketBundler> CreatePacketBundler(
+        QuicConnection::AckBundling bundling_mode);
+
+    // Populates network error details for this session.
+    void PopulateNetErrorDetails(NetErrorDetails* details) const;
+
+    // Returns the connection timing for the handshake of this session.
+    const LoadTimingInfo::ConnectTiming& GetConnectTiming();
+
+    // Signs the exported keying material used for Token Binding using key
+    // |*key| and puts the signature in |*out|. Returns a net error code.
+    Error GetTokenBindingSignature(crypto::ECPrivateKey* key,
+                                   TokenBindingType tb_type,
+                                   std::vector<uint8_t>* out);
+
+    // Returns true if |other| is a handle to the same session as this handle.
+    bool SharesSameSession(const Handle& other) const;
+
+    // Returns the QUIC version used by the session.
+    QuicVersion GetQuicVersion() const;
+
+    // Copies the remote udp address into |address| and returns a net error
+    // code.
+    int GetPeerAddress(IPEndPoint* address) const;
+
+    // Returns the push promise index associated with the session.
+    QuicClientPushPromiseIndex* GetPushPromiseIndex();
+
+    // Returns the session's server ID.
+    QuicServerId server_id() const { return server_id_; }
+
+    // Returns the session's net log.
+    const NetLogWithSource& net_log() const { return net_log_; }
+
+   private:
+    friend class QuicChromiumClientSession;
+    friend class QuicChromiumClientSession::StreamRequest;
+
+    // Waits for the handshake to be confirmed and invokes |callback| when
+    // that happens. If the handshake has already been confirmed, returns OK.
+    // If the connection has already been closed, returns a net error. If the
+    // connection closes before the handshake is confirmed, |callback| will
+    // be invoked with an error.
+    int WaitForHandshakeConfirmation(const CompletionCallback& callback);
+
+    // Called when the handshake is confirmed.
+    void OnCryptoHandshakeConfirmed();
+
+    // Called when the session is closed with a net error.
+    void OnSessionClosed(QuicVersion quic_version,
+                         int error,
+                         bool port_migration_detected,
+                         LoadTimingInfo::ConnectTiming connect_timing);
+
+    // Called by |request| to create a stream.
+    int TryCreateStream(StreamRequest* request);
+
+    // Called by |request| to cancel stream request.
+    void CancelRequest(StreamRequest* request);
+
+    // Underlying session which may be destroyed before this handle.
+    base::WeakPtr<QuicChromiumClientSession> session_;
+
+    // Stream request created by |RequestStream()|.
+    std::unique_ptr<StreamRequest> stream_request_;
+
+    // Information saved from the session which can be used even after the
+    // session is destroyed.
+    NetLogWithSource net_log_;
+    bool was_handshake_confirmed_;
+    int error_;
+    bool port_migration_detected_;
+    QuicServerId server_id_;
+    QuicVersion quic_version_;
+    LoadTimingInfo::ConnectTiming connect_timing_;
+    QuicClientPushPromiseIndex* push_promise_index_;
   };
 
   // A helper class used to manage a request to create a stream.
@@ -102,7 +206,7 @@
       STATE_REQUEST_STREAM_COMPLETE,
     };
 
-    StreamRequest(const base::WeakPtr<QuicChromiumClientSession>& session,
+    StreamRequest(std::unique_ptr<QuicChromiumClientSession::Handle> session,
                   bool requires_confirmation);
 
     void OnIOComplete(int rv);
@@ -123,7 +227,7 @@
     // if |session_| is destroyed while the stream request is still pending.
     void OnRequestCompleteFailure(int rv);
 
-    base::WeakPtr<QuicChromiumClientSession> session_;
+    std::unique_ptr<QuicChromiumClientSession::Handle> session_;
     const bool requires_confirmation_;
     CompletionCallback callback_;
     QuicChromiumClientStream* stream_;
@@ -168,8 +272,8 @@
 
   void Initialize() override;
 
-  void AddObserver(Observer* observer);
-  void RemoveObserver(Observer* observer);
+  void AddHandle(Handle* handle);
+  void RemoveHandle(Handle* handle);
 
   // Waits for the handshake to be confirmed and invokes |callback| when
   // that happens. If the handshake has already been confirmed, returns OK.
@@ -178,12 +282,6 @@
   // be invoked with an error.
   int WaitForHandshakeConfirmation(const CompletionCallback& callback);
 
-  // Returns a new stream request which can be used to create a new
-  // QUIC stream. If |requires_confirmation| is true, then the requested
-  // stream will not be created until the handshake as been confirmed.
-  std::unique_ptr<StreamRequest> CreateStreamRequest(
-      bool requires_confirmation);
-
   // Attempts to create a new stream.  If the stream can be
   // created immediately, returns OK.  If the open stream limit
   // has been reached, returns ERR_IO_PENDING, and |request|
@@ -266,7 +364,8 @@
 
   const NetLogWithSource& net_log() const { return net_log_; }
 
-  base::WeakPtr<QuicChromiumClientSession> GetWeakPtr();
+  // Returns a Handle to this session.
+  std::unique_ptr<QuicChromiumClientSession::Handle> CreateHandle();
 
   // Returns the number of client hello messages that have been sent on the
   // crypto stream. If the handshake has completed then this is one greater
@@ -318,7 +417,7 @@
   void OnMigrationTimeout(size_t num_sockets);
 
   // Populates network error details for this session.
-  void PopulateNetErrorDetails(NetErrorDetails* details);
+  void PopulateNetErrorDetails(NetErrorDetails* details) const;
 
   // Returns current default socket. This is the socket over which all
   // QUIC packets are sent. This default socket can change, so do not store the
@@ -362,7 +461,7 @@
  private:
   friend class test::QuicChromiumClientSessionPeer;
 
-  typedef std::set<Observer*> ObserverSet;
+  typedef std::set<Handle*> HandleSet;
   typedef std::list<StreamRequest*> StreamRequestQueue;
 
   QuicChromiumClientStream* CreateOutgoingReliableStreamImpl();
@@ -373,7 +472,7 @@
   void OnClosedStream();
 
   void CloseAllStreams(int net_error);
-  void CloseAllObservers(int net_error);
+  void CloseAllHandles(int net_error);
   void CancelAllRequests(int net_error);
   void NotifyRequestsOfConfirmation(int net_error);
 
@@ -400,7 +499,7 @@
   std::unique_ptr<ct::CTVerifyResult> ct_verify_result_;
   std::string pinning_failure_log_;
   bool pkp_bypassed_;
-  ObserverSet observers_;
+  HandleSet handles_;
   StreamRequestQueue stream_requests_;
   std::vector<CompletionCallback> waiting_for_confirmation_callbacks_;
   CompletionCallback callback_;
diff --git a/net/quic/chromium/quic_chromium_client_session_test.cc b/net/quic/chromium/quic_chromium_client_session_test.cc
index b8390fd..f1a3e30 100644
--- a/net/quic/chromium/quic_chromium_client_session_test.cc
+++ b/net/quic/chromium/quic_chromium_client_session_test.cc
@@ -87,6 +87,7 @@
             new SequencedSocketData(default_read_.get(), 1, nullptr, 0)),
         random_(0),
         helper_(&clock_, &random_),
+        server_id_(kServerHostname, kServerPort, PRIVACY_MODE_DISABLED),
         client_maker_(GetParam(),
                       0,
                       &clock_,
@@ -119,8 +120,7 @@
         connection, std::move(socket),
         /*stream_factory=*/nullptr, &crypto_client_stream_factory_, &clock_,
         &transport_security_state_,
-        base::WrapUnique(static_cast<QuicServerInfo*>(nullptr)),
-        QuicServerId(kServerHostname, kServerPort, PRIVACY_MODE_DISABLED),
+        base::WrapUnique(static_cast<QuicServerInfo*>(nullptr)), server_id_,
         /*require_confirmation=*/false, kQuicYieldAfterPacketsRead,
         QuicTime::Delta::FromMilliseconds(kQuicYieldAfterDurationMilliseconds),
         /*cert_verify_flags=*/0, DefaultQuicConfig(), &crypto_config_,
@@ -139,7 +139,8 @@
   }
 
   void TearDown() override {
-    session_->CloseSessionOnError(ERR_ABORTED, QUIC_INTERNAL_ERROR);
+    if (session_)
+      session_->CloseSessionOnError(ERR_ABORTED, QUIC_INTERNAL_ERROR);
   }
 
   void CompleteCryptoHandshake() {
@@ -176,6 +177,7 @@
   TransportSecurityState transport_security_state_;
   MockCryptoClientStreamFactory crypto_client_stream_factory_;
   QuicClientPushPromiseIndex push_promise_index_;
+  QuicServerId server_id_;
   std::unique_ptr<QuicChromiumClientSession> session_;
   TestServerPushDelegate test_push_delegate_;
   QuicConnectionVisitorInterface* visitor_;
@@ -201,6 +203,88 @@
   CompleteCryptoHandshake();
 }
 
+TEST_P(QuicChromiumClientSessionTest, Handle) {
+  MockQuicData quic_data;
+  quic_data.AddWrite(client_maker_.MakeInitialSettingsPacket(1, nullptr));
+  quic_data.AddRead(ASYNC, ERR_IO_PENDING);
+  quic_data.AddRead(ASYNC, OK);  // EOF
+  quic_data.AddSocketDataToFactory(&socket_factory_);
+
+  Initialize();
+
+  NetLogWithSource session_net_log = session_->net_log();
+  EXPECT_EQ(NetLogSourceType::QUIC_SESSION, session_net_log.source().type);
+  EXPECT_EQ(&net_log_, session_net_log.net_log());
+
+  std::unique_ptr<QuicChromiumClientSession::Handle> handle =
+      session_->CreateHandle();
+  EXPECT_TRUE(handle->IsConnected());
+  EXPECT_FALSE(handle->IsCryptoHandshakeConfirmed());
+  EXPECT_EQ(GetParam(), handle->GetQuicVersion());
+  EXPECT_EQ(server_id_, handle->server_id());
+  EXPECT_EQ(session_net_log.source().type, handle->net_log().source().type);
+  EXPECT_EQ(session_net_log.source().id, handle->net_log().source().id);
+  EXPECT_EQ(session_net_log.net_log(), handle->net_log().net_log());
+  IPEndPoint address;
+  EXPECT_EQ(OK, handle->GetPeerAddress(&address));
+  EXPECT_EQ(kIpEndPoint, address);
+  EXPECT_TRUE(handle->CreatePacketBundler(QuicConnection::NO_ACK).get() !=
+              nullptr);
+
+  CompleteCryptoHandshake();
+
+  EXPECT_TRUE(handle->IsCryptoHandshakeConfirmed());
+
+  // Request a stream and verify that a stream was created.
+  TestCompletionCallback callback;
+  ASSERT_EQ(OK, handle->RequestStream(/*requires_confirmation=*/false,
+                                      callback.callback()));
+  EXPECT_TRUE(handle->ReleaseStream() != nullptr);
+
+  quic_data.Resume();
+  EXPECT_TRUE(quic_data.AllReadDataConsumed());
+  EXPECT_TRUE(quic_data.AllWriteDataConsumed());
+
+  // Veirfy that the handle works correctly after the session is closed.
+  EXPECT_FALSE(handle->IsConnected());
+  EXPECT_TRUE(handle->IsCryptoHandshakeConfirmed());
+  EXPECT_EQ(GetParam(), handle->GetQuicVersion());
+  EXPECT_EQ(server_id_, handle->server_id());
+  EXPECT_EQ(session_net_log.source().type, handle->net_log().source().type);
+  EXPECT_EQ(session_net_log.source().id, handle->net_log().source().id);
+  EXPECT_EQ(session_net_log.net_log(), handle->net_log().net_log());
+  EXPECT_EQ(ERR_CONNECTION_CLOSED, handle->GetPeerAddress(&address));
+  EXPECT_TRUE(handle->CreatePacketBundler(QuicConnection::NO_ACK).get() ==
+              nullptr);
+  {
+    // Verify that CreateHandle() works even after the session is closed.
+    std::unique_ptr<QuicChromiumClientSession::Handle> handle2 =
+        session_->CreateHandle();
+    EXPECT_FALSE(handle2->IsConnected());
+    EXPECT_TRUE(handle2->IsCryptoHandshakeConfirmed());
+    ASSERT_EQ(ERR_CONNECTION_CLOSED,
+              handle2->RequestStream(/*requires_confirmation=*/false,
+                                     callback.callback()));
+  }
+
+  session_.reset();
+
+  // Veirfy that the handle works correctly after the session is deleted.
+  EXPECT_FALSE(handle->IsConnected());
+  EXPECT_TRUE(handle->IsCryptoHandshakeConfirmed());
+  EXPECT_EQ(GetParam(), handle->GetQuicVersion());
+  EXPECT_EQ(server_id_, handle->server_id());
+  EXPECT_EQ(session_net_log.source().type, handle->net_log().source().type);
+  EXPECT_EQ(session_net_log.source().id, handle->net_log().source().id);
+  EXPECT_EQ(session_net_log.net_log(), handle->net_log().net_log());
+  EXPECT_EQ(ERR_CONNECTION_CLOSED, handle->GetPeerAddress(&address));
+  EXPECT_TRUE(handle->CreatePacketBundler(QuicConnection::NO_ACK).get() ==
+              nullptr);
+  ASSERT_EQ(ERR_CONNECTION_CLOSED,
+            handle->RequestStream(/*requires_confirmation=*/false,
+                                  callback.callback()));
+}
+
 TEST_P(QuicChromiumClientSessionTest, StreamRequest) {
   MockQuicData quic_data;
   quic_data.AddWrite(client_maker_.MakeInitialSettingsPacket(1, nullptr));
@@ -212,11 +296,12 @@
   CompleteCryptoHandshake();
 
   // Request a stream and verify that a stream was created.
-  std::unique_ptr<QuicChromiumClientSession::StreamRequest> stream_request =
-      session_->CreateStreamRequest(/*requires_confirmation=*/false);
+  std::unique_ptr<QuicChromiumClientSession::Handle> handle =
+      session_->CreateHandle();
   TestCompletionCallback callback;
-  ASSERT_EQ(OK, stream_request->StartRequest(callback.callback()));
-  EXPECT_TRUE(stream_request->ReleaseStream() != nullptr);
+  ASSERT_EQ(OK, handle->RequestStream(/*requires_confirmation=*/false,
+                                      callback.callback()));
+  EXPECT_TRUE(handle->ReleaseStream() != nullptr);
 
   quic_data.Resume();
   EXPECT_TRUE(quic_data.AllReadDataConsumed());
@@ -234,11 +319,12 @@
   CompleteCryptoHandshake();
 
   // Request a stream and verify that a stream was created.
-  std::unique_ptr<QuicChromiumClientSession::StreamRequest> stream_request =
-      session_->CreateStreamRequest(/*requires_confirmation=*/true);
+  std::unique_ptr<QuicChromiumClientSession::Handle> handle =
+      session_->CreateHandle();
   TestCompletionCallback callback;
-  ASSERT_EQ(OK, stream_request->StartRequest(callback.callback()));
-  EXPECT_TRUE(stream_request->ReleaseStream() != nullptr);
+  ASSERT_EQ(OK, handle->RequestStream(/*requires_confirmation=*/true,
+                                      callback.callback()));
+  EXPECT_TRUE(handle->ReleaseStream() != nullptr);
 
   quic_data.Resume();
   EXPECT_TRUE(quic_data.AllReadDataConsumed());
@@ -255,16 +341,18 @@
   Initialize();
 
   // Request a stream and verify that a stream was created.
-  std::unique_ptr<QuicChromiumClientSession::StreamRequest> stream_request =
-      session_->CreateStreamRequest(/*requires_confirmation=*/true);
+  std::unique_ptr<QuicChromiumClientSession::Handle> handle =
+      session_->CreateHandle();
   TestCompletionCallback callback;
-  ASSERT_EQ(ERR_IO_PENDING, stream_request->StartRequest(callback.callback()));
+  ASSERT_EQ(ERR_IO_PENDING,
+            handle->RequestStream(/*requires_confirmation=*/true,
+                                  callback.callback()));
 
   CompleteCryptoHandshake();
 
   EXPECT_THAT(callback.WaitForResult(), IsOk());
 
-  EXPECT_TRUE(stream_request->ReleaseStream() != nullptr);
+  EXPECT_TRUE(handle->ReleaseStream() != nullptr);
 
   quic_data.Resume();
   EXPECT_TRUE(quic_data.AllReadDataConsumed());
@@ -284,11 +372,12 @@
   CompleteCryptoHandshake();
 
   // Request a stream and cancel it without releasing the stream.
-  std::unique_ptr<QuicChromiumClientSession::StreamRequest> stream_request =
-      session_->CreateStreamRequest(/*requires_confirmation=*/false);
+  std::unique_ptr<QuicChromiumClientSession::Handle> handle =
+      session_->CreateHandle();
   TestCompletionCallback callback;
-  ASSERT_EQ(OK, stream_request->StartRequest(callback.callback()));
-  stream_request.reset();
+  ASSERT_EQ(OK, handle->RequestStream(/*requires_confirmation=*/false,
+                                      callback.callback()));
+  handle.reset();
 
   quic_data.Resume();
   EXPECT_TRUE(quic_data.AllReadDataConsumed());
@@ -316,10 +405,12 @@
   EXPECT_EQ(kMaxOpenStreams, session_->GetNumOpenOutgoingStreams());
 
   // Request a stream and verify that it's pending.
-  std::unique_ptr<QuicChromiumClientSession::StreamRequest> stream_request =
-      session_->CreateStreamRequest(/*requires_confirmation=*/false);
+  std::unique_ptr<QuicChromiumClientSession::Handle> handle =
+      session_->CreateHandle();
   TestCompletionCallback callback;
-  ASSERT_EQ(ERR_IO_PENDING, stream_request->StartRequest(callback.callback()));
+  ASSERT_EQ(ERR_IO_PENDING,
+            handle->RequestStream(/*requires_confirmation=*/false,
+                                  callback.callback()));
 
   // Close a stream and ensure the stream request completes.
   QuicRstStreamFrame rst(GetNthClientInitiatedStreamId(0),
@@ -327,7 +418,7 @@
   session_->OnRstStream(rst);
   ASSERT_TRUE(callback.have_result());
   EXPECT_THAT(callback.WaitForResult(), IsOk());
-  EXPECT_TRUE(stream_request->ReleaseStream() != nullptr);
+  EXPECT_TRUE(handle->ReleaseStream() != nullptr);
 
   quic_data.Resume();
   EXPECT_TRUE(quic_data.AllReadDataConsumed());
@@ -355,13 +446,15 @@
   EXPECT_EQ(kMaxOpenStreams, session_->GetNumOpenOutgoingStreams());
 
   // Request a stream and verify that it's pending.
-  std::unique_ptr<QuicChromiumClientSession::StreamRequest> stream_request =
-      session_->CreateStreamRequest(/*requires_confirmation=*/false);
+  std::unique_ptr<QuicChromiumClientSession::Handle> handle =
+      session_->CreateHandle();
   TestCompletionCallback callback;
-  ASSERT_EQ(ERR_IO_PENDING, stream_request->StartRequest(callback.callback()));
+  ASSERT_EQ(ERR_IO_PENDING,
+            handle->RequestStream(/*requires_confirmation=*/false,
+                                  callback.callback()));
 
   // Cancel the pending stream request.
-  stream_request.reset();
+  handle.reset();
 
   // Close a stream and ensure that no new stream is created.
   QuicRstStreamFrame rst(GetNthClientInitiatedStreamId(0),
@@ -387,11 +480,12 @@
   base::RunLoop().RunUntilIdle();
 
   // Request a stream and verify that it failed.
-  std::unique_ptr<QuicChromiumClientSession::StreamRequest> stream_request =
-      session_->CreateStreamRequest(/*requires_confirmation=*/false);
+  std::unique_ptr<QuicChromiumClientSession::Handle> handle =
+      session_->CreateHandle();
   TestCompletionCallback callback;
   ASSERT_EQ(ERR_CONNECTION_CLOSED,
-            stream_request->StartRequest(callback.callback()));
+            handle->RequestStream(/*requires_confirmation=*/false,
+                                  callback.callback()));
 
   EXPECT_TRUE(quic_data.AllReadDataConsumed());
   EXPECT_TRUE(quic_data.AllWriteDataConsumed());
@@ -406,10 +500,12 @@
   Initialize();
 
   // Request a stream and verify that it's pending.
-  std::unique_ptr<QuicChromiumClientSession::StreamRequest> stream_request =
-      session_->CreateStreamRequest(/*requires_confirmation=*/true);
+  std::unique_ptr<QuicChromiumClientSession::Handle> handle =
+      session_->CreateHandle();
   TestCompletionCallback callback;
-  ASSERT_EQ(ERR_IO_PENDING, stream_request->StartRequest(callback.callback()));
+  ASSERT_EQ(ERR_IO_PENDING,
+            handle->RequestStream(/*requires_confirmation=*/true,
+                                  callback.callback()));
 
   // Close the connection and verify that the StreamRequest completes with
   // an error.
@@ -441,10 +537,12 @@
   EXPECT_EQ(kMaxOpenStreams, session_->GetNumOpenOutgoingStreams());
 
   // Request a stream and verify that it's pending.
-  std::unique_ptr<QuicChromiumClientSession::StreamRequest> stream_request =
-      session_->CreateStreamRequest(/*requires_confirmation=*/false);
+  std::unique_ptr<QuicChromiumClientSession::Handle> handle =
+      session_->CreateHandle();
   TestCompletionCallback callback;
-  ASSERT_EQ(ERR_IO_PENDING, stream_request->StartRequest(callback.callback()));
+  ASSERT_EQ(ERR_IO_PENDING,
+            handle->RequestStream(/*requires_confirmation=*/false,
+                                  callback.callback()));
 
   // Close the connection and verify that the StreamRequest completes with
   // an error.
@@ -811,10 +909,12 @@
     streams.push_back(stream);
   }
 
-  std::unique_ptr<QuicChromiumClientSession::StreamRequest> stream_request =
-      session_->CreateStreamRequest(/*requires_confirmation=*/false);
+  std::unique_ptr<QuicChromiumClientSession::Handle> handle =
+      session_->CreateHandle();
   TestCompletionCallback callback;
-  ASSERT_EQ(ERR_IO_PENDING, stream_request->StartRequest(callback.callback()));
+  ASSERT_EQ(ERR_IO_PENDING,
+            handle->RequestStream(/*requires_confirmation=*/false,
+                                  callback.callback()));
 
   // Close a stream and ensure I can now open a new one.
   QuicStreamId stream_id = streams[0]->id();
@@ -823,7 +923,7 @@
   session_->OnRstStream(rst1);
   ASSERT_TRUE(callback.have_result());
   EXPECT_THAT(callback.WaitForResult(), IsOk());
-  EXPECT_TRUE(stream_request->ReleaseStream() != nullptr);
+  EXPECT_TRUE(handle->ReleaseStream() != nullptr);
 }
 
 TEST_P(QuicChromiumClientSessionTest, GoAwayReceived) {
diff --git a/net/quic/chromium/quic_http_stream.cc b/net/quic/chromium/quic_http_stream.cc
index 62e71b4..36e9118 100644
--- a/net/quic/chromium/quic_http_stream.cc
+++ b/net/quic/chromium/quic_http_stream.cc
@@ -45,16 +45,11 @@
 }  // namespace
 
 QuicHttpStream::QuicHttpStream(
-    const base::WeakPtr<QuicChromiumClientSession>& session,
+    std::unique_ptr<QuicChromiumClientSession::Handle> session,
     HttpServerProperties* http_server_properties)
-    : MultiplexedHttpStream(MultiplexedSessionHandle(session)),
+    : MultiplexedHttpStream(std::move(session)),
       next_state_(STATE_NONE),
-      session_(session),
-      server_id_(session->server_id()),
       http_server_properties_(http_server_properties),
-      quic_version_(session->GetQuicVersion()),
-      session_error_(ERR_UNEXPECTED),
-      was_handshake_confirmed_(session->IsCryptoHandshakeConfirmed()),
       stream_(nullptr),
       request_info_(nullptr),
       request_body_stream_(nullptr),
@@ -69,22 +64,17 @@
       closed_stream_sent_bytes_(0),
       closed_is_first_stream_(false),
       user_buffer_len_(0),
+      session_error_(ERR_UNEXPECTED),
       quic_connection_error_(QUIC_NO_ERROR),
       quic_stream_error_(QUIC_STREAM_NO_ERROR),
-      port_migration_detected_(false),
       found_promise_(false),
       push_handle_(nullptr),
       in_loop_(false),
-      weak_factory_(this) {
-  DCHECK(session_);
-  session_->AddObserver(this);
-}
+      weak_factory_(this) {}
 
 QuicHttpStream::~QuicHttpStream() {
   CHECK(!in_loop_);
   Close(false);
-  if (session_)
-    session_->RemoveObserver(this);
 }
 
 bool QuicHttpStream::CheckVary(const SpdyHeaderBlock& client_request,
@@ -167,12 +157,12 @@
   // ERR_QUIC_HANDSHAKE_FAILED. It will retry any request with
   // ERR_CONNECTION_CLOSED so long as the connection has been used for other
   // streams first and headers have not yet been received.
-  if (!session_)
+  if (!quic_session()->IsConnected())
     return GetResponseStatus();
 
   stream_net_log.AddEvent(
       NetLogEventType::HTTP_STREAM_REQUEST_BOUND_TO_QUIC_SESSION,
-      session_->net_log().source().ToEventParametersCallback());
+      quic_session()->net_log().source().ToEventParametersCallback());
 
   stream_net_log_ = stream_net_log;
   request_info_ = request_info;
@@ -183,14 +173,14 @@
 
   std::string url(request_info->url.spec());
   QuicClientPromisedInfo* promised =
-      session_->push_promise_index()->GetPromised(url);
+      quic_session()->GetPushPromiseIndex()->GetPromised(url);
   if (promised) {
     found_promise_ = true;
     stream_net_log_.AddEvent(
         NetLogEventType::QUIC_HTTP_STREAM_PUSH_PROMISE_RENDEZVOUS,
         base::Bind(&NetLogQuicPushStreamCallback, promised->id(),
                    &request_info_->url));
-    session_->net_log().AddEvent(
+    quic_session()->net_log().AddEvent(
         NetLogEventType::QUIC_HTTP_STREAM_PUSH_PROMISE_RENDEZVOUS,
         base::Bind(&NetLogQuicPushStreamCallback, promised->id(),
                    &request_info_->url));
@@ -206,7 +196,7 @@
 }
 
 int QuicHttpStream::DoHandlePromise() {
-  QuicAsyncStatus push_status = session_->push_promise_index()->Try(
+  QuicAsyncStatus push_status = quic_session()->GetPushPromiseIndex()->Try(
       request_headers_, this, &this->push_handle_);
 
   switch (push_status) {
@@ -233,7 +223,7 @@
       NetLogEventType::QUIC_HTTP_STREAM_ADOPTED_PUSH_STREAM,
       base::Bind(&NetLogQuicPushStreamCallback, stream_->id(),
                  &request_info_->url));
-  session_->net_log().AddEvent(
+  quic_session()->net_log().AddEvent(
       NetLogEventType::QUIC_HTTP_STREAM_ADOPTED_PUSH_STREAM,
       base::Bind(&NetLogQuicPushStreamCallback, stream_->id(),
                  &request_info_->url));
@@ -262,7 +252,7 @@
 
   // In order to rendezvous with a push stream, the session still needs to be
   // available. Otherwise the stream needs to be available.
-  if ((!found_promise_ && !stream_) || !session_)
+  if ((!found_promise_ && !stream_) || !quic_session()->IsConnected())
     return GetResponseStatus();
 
   // Store the serialized request headers.
@@ -277,9 +267,9 @@
     if (found_promise_) {
       std::string url(request_info_->url.spec());
       QuicClientPromisedInfo* promised =
-          session_->push_promise_index()->GetPromised(url);
+          quic_session()->GetPushPromiseIndex()->GetPromised(url);
       if (promised != nullptr) {
-        session_->ResetPromised(promised->id(), QUIC_STREAM_CANCELLED);
+        quic_session()->ResetPromised(promised->id(), QUIC_STREAM_CANCELLED);
       }
     }
 
@@ -426,20 +416,17 @@
 bool QuicHttpStream::GetAlternativeService(
     AlternativeService* alternative_service) const {
   alternative_service->protocol = kProtoQUIC;
-  alternative_service->host = server_id_.host();
-  alternative_service->port = server_id_.port();
+  alternative_service->host = quic_session()->server_id().host();
+  alternative_service->port = quic_session()->server_id().port();
   return true;
 }
 
 void QuicHttpStream::PopulateNetErrorDetails(NetErrorDetails* details) {
-  details->connection_info = ConnectionInfoFromQuicVersion(quic_version_);
-  if (was_handshake_confirmed_)
+  details->connection_info =
+      ConnectionInfoFromQuicVersion(quic_session()->GetQuicVersion());
+  quic_session()->PopulateNetErrorDetails(details);
+  if (quic_session()->IsCryptoHandshakeConfirmed())
     details->quic_connection_error = quic_connection_error_;
-  if (session_) {
-    session_->PopulateNetErrorDetails(details);
-  } else {
-    details->quic_port_migration_detected = port_migration_detected_;
-  }
 }
 
 void QuicHttpStream::SetPriority(RequestPriority priority) {
@@ -514,24 +501,6 @@
     DoCallback(GetResponseStatus());
 }
 
-void QuicHttpStream::OnCryptoHandshakeConfirmed() {
-  was_handshake_confirmed_ = true;
-}
-
-void QuicHttpStream::OnSuccessfulVersionNegotiation(
-    const QuicVersion& version) {
-  quic_version_ = version;
-}
-
-void QuicHttpStream::OnSessionClosed(int error, bool port_migration_detected) {
-  session_error_ = error;
-  port_migration_detected_ = port_migration_detected;
-  SaveResponseStatus();
-
-  Close(false);
-  session_.reset();
-}
-
 void QuicHttpStream::OnIOComplete(int rv) {
   rv = DoLoop(rv);
 
@@ -612,9 +581,8 @@
 
 int QuicHttpStream::DoRequestStream() {
   next_state_ = STATE_REQUEST_STREAM_COMPLETE;
-  stream_request_ =
-      session_->CreateStreamRequest(request_info_->method == "POST");
-  return stream_request_->StartRequest(
+  return quic_session()->RequestStream(
+      request_info_->method == "POST",
       base::Bind(&QuicHttpStream::OnIOComplete, weak_factory_.GetWeakPtr()));
 }
 
@@ -625,8 +593,7 @@
     return GetResponseStatus();
   }
 
-  stream_ = stream_request_->ReleaseStream();
-  stream_request_.reset();
+  stream_ = quic_session()->ReleaseStream();
   stream_->SetDelegate(this);
   if (request_info_->load_flags & LOAD_DISABLE_CONNECTION_MIGRATION) {
     stream_->DisableConnectionMigration();
@@ -761,10 +728,14 @@
     return ERR_QUIC_PROTOCOL_ERROR;
   }
   // Put the peer's IP address and port into the response.
-  IPEndPoint address = session_->peer_address().impl().socket_address();
+  IPEndPoint address;
+  int rv = quic_session()->GetPeerAddress(&address);
+  if (rv != OK)
+    return rv;
+
   response_info_->socket_address = HostPortPair::FromIPEndPoint(address);
   response_info_->connection_info =
-      ConnectionInfoFromQuicVersion(quic_version_);
+      ConnectionInfoFromQuicVersion(quic_session()->GetQuicVersion());
   response_info_->vary_data.Init(*request_info_,
                                  *response_info_->headers.get());
   response_info_->was_alpn_negotiated = true;
@@ -776,7 +747,7 @@
 
   // Populate |connect_timing_| when response headers are received. This should
   // take care of 0-RTT where request is sent before handshake is confirmed.
-  connect_timing_ = session_->GetConnectTiming();
+  connect_timing_ = quic_session()->GetConnectTiming();
   return OK;
 }
 
@@ -839,7 +810,7 @@
 
   // If the handshake has failed this will be handled by the QuicStreamFactory
   // and HttpStreamFactory to mark QUIC as broken if TCP is actually working.
-  if (!was_handshake_confirmed_)
+  if (!quic_session()->IsCryptoHandshakeConfirmed())
     return ERR_QUIC_HANDSHAKE_FAILED;
 
   // If the session was aborted by a higher layer, simply use that error code.
diff --git a/net/quic/chromium/quic_http_stream.h b/net/quic/chromium/quic_http_stream.h
index 84bbeb9..83d07763 100644
--- a/net/quic/chromium/quic_http_stream.h
+++ b/net/quic/chromium/quic_http_stream.h
@@ -36,12 +36,11 @@
 // non-owning pointer to a QuicChromiumClientStream which it uses to
 // send and receive data.
 class NET_EXPORT_PRIVATE QuicHttpStream
-    : public QuicChromiumClientSession::Observer,
-      public QuicChromiumClientStream::Delegate,
+    : public QuicChromiumClientStream::Delegate,
       public QuicClientPushPromiseIndex::Delegate,
       public MultiplexedHttpStream {
  public:
-  QuicHttpStream(const base::WeakPtr<QuicChromiumClientSession>& session,
+  QuicHttpStream(std::unique_ptr<QuicChromiumClientSession::Handle> session,
                  HttpServerProperties* http_server_properties);
 
   ~QuicHttpStream() override;
@@ -76,11 +75,6 @@
   void OnClose() override;
   void OnError(int error) override;
 
-  // QuicChromiumClientSession::Observer implementation
-  void OnCryptoHandshakeConfirmed() override;
-  void OnSuccessfulVersionNegotiation(const QuicVersion& version) override;
-  void OnSessionClosed(int error, bool port_migration_detected) override;
-
   // QuicClientPushPromiseIndex::Delegate implementation
   bool CheckVary(const SpdyHeaderBlock& client_request,
                  const SpdyHeaderBlock& promise_request,
@@ -144,17 +138,18 @@
   // |session_error|, |connection_error| and |stream_error|.
   int ComputeResponseStatus() const;
 
-  State next_state_;
+  QuicChromiumClientSession::Handle* quic_session() {
+    return static_cast<QuicChromiumClientSession::Handle*>(session());
+  }
 
-  base::WeakPtr<QuicChromiumClientSession> session_;
-  const QuicServerId server_id_;  // The ID of the QUIC server for this stream.
+  const QuicChromiumClientSession::Handle* quic_session() const {
+    return static_cast<const QuicChromiumClientSession::Handle*>(session());
+  }
+
+  State next_state_;
 
   HttpServerProperties* http_server_properties_;  // Unowned.
 
-  QuicVersion quic_version_;
-  int session_error_;             // Error code from the connection shutdown.
-  bool was_handshake_confirmed_;  // True if the crypto handshake succeeded.
-  std::unique_ptr<QuicChromiumClientSession::StreamRequest> stream_request_;
   QuicChromiumClientStream* stream_;  // Non-owning.
 
   // The following three fields are all owned by the caller and must
@@ -213,12 +208,10 @@
 
   NetLogWithSource stream_net_log_;
 
+  int session_error_;  // Error code from the connection shutdown.
   QuicErrorCode quic_connection_error_;       // Cached connection error code.
   QuicRstStreamErrorCode quic_stream_error_;  // Cached stream error code.
 
-  // True when this stream receives a go away from server due to port migration.
-  bool port_migration_detected_;
-
   bool found_promise_;
   // |QuicClientPromisedInfo| owns this. It will be set when |Try()|
   // is asynchronous, i.e. it returned QUIC_PENDING, and remains valid
diff --git a/net/quic/chromium/quic_http_stream_test.cc b/net/quic/chromium/quic_http_stream_test.cc
index 5e19a21..8ee621c 100644
--- a/net/quic/chromium/quic_http_stream_test.cc
+++ b/net/quic/chromium/quic_http_stream_test.cc
@@ -103,9 +103,9 @@
 class AutoClosingStream : public QuicHttpStream {
  public:
   explicit AutoClosingStream(
-      const base::WeakPtr<QuicChromiumClientSession>& session,
+      std::unique_ptr<QuicChromiumClientSession::Handle> session,
       HttpServerProperties* http_server_properties)
-      : QuicHttpStream(session, http_server_properties) {}
+      : QuicHttpStream(std::move(session), http_server_properties) {}
 
   void OnHeadersAvailable(const SpdyHeaderBlock& headers,
                           size_t frame_len) override {
@@ -322,17 +322,18 @@
         /*socket_performance_watcher=*/nullptr, net_log_.bound().net_log()));
     session_->Initialize();
     TestCompletionCallback callback;
+
     session_->CryptoConnect(callback.callback());
     stream_.reset(use_closing_stream_
-                      ? new AutoClosingStream(session_->GetWeakPtr(),
+                      ? new AutoClosingStream(session_->CreateHandle(),
                                               &http_server_properties_)
-                      : new QuicHttpStream(session_->GetWeakPtr(),
+                      : new QuicHttpStream(session_->CreateHandle(),
                                            &http_server_properties_));
 
     promised_stream_.reset(use_closing_stream_
-                               ? new AutoClosingStream(session_->GetWeakPtr(),
+                               ? new AutoClosingStream(session_->CreateHandle(),
                                                        &http_server_properties_)
-                               : new QuicHttpStream(session_->GetWeakPtr(),
+                               : new QuicHttpStream(session_->CreateHandle(),
                                                     &http_server_properties_));
 
     push_promise_[":path"] = "/bar";
@@ -721,7 +722,7 @@
             stream_->SendRequest(headers_, &response_, callback_.callback()));
 
   // Start a second request.
-  QuicHttpStream stream2(session_->GetWeakPtr(), &http_server_properties_);
+  QuicHttpStream stream2(session_->CreateHandle(), &http_server_properties_);
   TestCompletionCallback callback2;
   EXPECT_EQ(OK,
             stream2.InitializeStream(&request_, DEFAULT_PRIORITY,
diff --git a/net/quic/chromium/quic_stream_factory.cc b/net/quic/chromium/quic_stream_factory.cc
index 61f79e0..1dc3c599 100644
--- a/net/quic/chromium/quic_stream_factory.cc
+++ b/net/quic/chromium/quic_stream_factory.cc
@@ -607,9 +607,9 @@
   return rv;
 }
 
-void QuicStreamRequest::SetSession(QuicChromiumClientSession* session) {
-  DCHECK(session);
-  session_ = session->GetWeakPtr();
+void QuicStreamRequest::SetSession(
+    std::unique_ptr<QuicChromiumClientSession::Handle> session) {
+  session_ = move(session);
 }
 
 void QuicStreamRequest::OnRequestComplete(int rv) {
@@ -624,16 +624,19 @@
 }
 
 std::unique_ptr<HttpStream> QuicStreamRequest::CreateStream() {
-  if (!session_)
+  if (!session_ || !session_->IsConnected())
     return nullptr;
-  return base::MakeUnique<QuicHttpStream>(session_, http_server_properties_);
+
+  return base::MakeUnique<QuicHttpStream>(std::move(session_),
+                                          http_server_properties_);
 }
 
 std::unique_ptr<BidirectionalStreamImpl>
 QuicStreamRequest::CreateBidirectionalStreamImpl() {
-  if (!session_)
+  if (!session_ || !session_->IsConnected())
     return nullptr;
-  return base::MakeUnique<BidirectionalStreamQuicImpl>(session_);
+
+  return base::MakeUnique<BidirectionalStreamQuicImpl>(std::move(session_));
 }
 
 QuicStreamFactory::QuicStreamFactory(
@@ -874,7 +877,7 @@
         static_cast<QuicChromiumClientSession*>(promised->session());
     DCHECK(session);
     if (session->server_id().privacy_mode() == server_id.privacy_mode()) {
-      request->SetSession(session);
+      request->SetSession(session->CreateHandle());
       ++num_push_streams_created_;
       return OK;
     }
@@ -890,7 +893,7 @@
     SessionMap::iterator it = active_sessions_.find(server_id);
     if (it != active_sessions_.end()) {
       QuicChromiumClientSession* session = it->second;
-      request->SetSession(session);
+      request->SetSession(session->CreateHandle());
       return OK;
     }
   }
@@ -915,7 +918,7 @@
       QuicChromiumClientSession* session = key_value.second;
       if (destination.Equals(all_sessions_[session].destination()) &&
           session->CanPool(server_id.host(), server_id.privacy_mode())) {
-        request->SetSession(session);
+        request->SetSession(session->CreateHandle());
         return OK;
       }
     }
@@ -949,7 +952,7 @@
     if (it == active_sessions_.end())
       return ERR_QUIC_PROTOCOL_ERROR;
     QuicChromiumClientSession* session = it->second;
-    request->SetSession(session);
+    request->SetSession(session->CreateHandle());
   }
   return rv;
 }
@@ -1014,7 +1017,7 @@
       for (QuicStreamRequest* request : requests_iter->second) {
         DCHECK(request->server_id() == server_id);
         // Do not notify |request| yet.
-        request->SetSession(session);
+        request->SetSession(session->CreateHandle());
       }
     }
   }
diff --git a/net/quic/chromium/quic_stream_factory.h b/net/quic/chromium/quic_stream_factory.h
index 9feedb3..2686fdad 100644
--- a/net/quic/chromium/quic_stream_factory.h
+++ b/net/quic/chromium/quic_stream_factory.h
@@ -135,7 +135,7 @@
   std::unique_ptr<BidirectionalStreamImpl> CreateBidirectionalStreamImpl();
 
   // Sets |session_|.
-  void SetSession(QuicChromiumClientSession* session);
+  void SetSession(std::unique_ptr<QuicChromiumClientSession::Handle> session);
 
   const QuicServerId& server_id() const { return server_id_; }
 
@@ -147,7 +147,7 @@
   QuicServerId server_id_;
   NetLogWithSource net_log_;
   CompletionCallback callback_;
-  base::WeakPtr<QuicChromiumClientSession> session_;
+  std::unique_ptr<QuicChromiumClientSession::Handle> session_;
 
   DISALLOW_COPY_AND_ASSIGN(QuicStreamRequest);
 };
diff --git a/net/quic/chromium/quic_stream_factory_test.cc b/net/quic/chromium/quic_stream_factory_test.cc
index 7f7328f..5233f2bb 100644
--- a/net/quic/chromium/quic_stream_factory_test.cc
+++ b/net/quic/chromium/quic_stream_factory_test.cc
@@ -158,8 +158,9 @@
 
 class QuicHttpStreamPeer {
  public:
-  static QuicChromiumClientSession* GetSession(HttpStream* stream) {
-    return static_cast<QuicHttpStream*>(stream)->session_.get();
+  static QuicChromiumClientSession::Handle* GetSessionHandle(
+      HttpStream* stream) {
+    return static_cast<QuicHttpStream*>(stream)->quic_session();
   }
 };
 
@@ -783,7 +784,7 @@
                                  /*cert_verify_flags=*/0, url_, "GET", net_log_,
                                  callback_.callback()));
   // Will reset stream 3.
-  stream = request.CreateStream();
+  stream = request2.CreateStream();
 
   EXPECT_TRUE(stream.get());
 
@@ -4696,11 +4697,11 @@
   std::unique_ptr<HttpStream> stream2 = request2.CreateStream();
   EXPECT_TRUE(stream2.get());
 
-  QuicChromiumClientSession* session1 =
-      QuicHttpStreamPeer::GetSession(stream1.get());
-  QuicChromiumClientSession* session2 =
-      QuicHttpStreamPeer::GetSession(stream2.get());
-  EXPECT_EQ(session1, session2);
+  QuicChromiumClientSession::Handle* session1 =
+      QuicHttpStreamPeer::GetSessionHandle(stream1.get());
+  QuicChromiumClientSession::Handle* session2 =
+      QuicHttpStreamPeer::GetSessionHandle(stream2.get());
+  EXPECT_TRUE(session1->SharesSameSession(*session2));
   EXPECT_EQ(QuicServerId(host_port_pair_, privacy_mode_),
             session1->server_id());
 
@@ -4879,11 +4880,11 @@
   std::unique_ptr<HttpStream> stream2 = request2.CreateStream();
   EXPECT_TRUE(stream2.get());
 
-  QuicChromiumClientSession* session1 =
-      QuicHttpStreamPeer::GetSession(stream1.get());
-  QuicChromiumClientSession* session2 =
-      QuicHttpStreamPeer::GetSession(stream2.get());
-  EXPECT_EQ(session1, session2);
+  QuicChromiumClientSession::Handle* session1 =
+      QuicHttpStreamPeer::GetSessionHandle(stream1.get());
+  QuicChromiumClientSession::Handle* session2 =
+      QuicHttpStreamPeer::GetSessionHandle(stream2.get());
+  EXPECT_TRUE(session1->SharesSameSession(*session2));
 
   EXPECT_EQ(QuicServerId(origin1_, privacy_mode_), session1->server_id());
 
@@ -4954,11 +4955,11 @@
   // |request2| does not pool to the first session, because PrivacyMode does not
   // match.  Instead, another session is opened to the same destination, but
   // with a different QuicServerId.
-  QuicChromiumClientSession* session1 =
-      QuicHttpStreamPeer::GetSession(stream1.get());
-  QuicChromiumClientSession* session2 =
-      QuicHttpStreamPeer::GetSession(stream2.get());
-  EXPECT_NE(session1, session2);
+  QuicChromiumClientSession::Handle* session1 =
+      QuicHttpStreamPeer::GetSessionHandle(stream1.get());
+  QuicChromiumClientSession::Handle* session2 =
+      QuicHttpStreamPeer::GetSessionHandle(stream2.get());
+  EXPECT_FALSE(session1->SharesSameSession(*session2));
 
   EXPECT_EQ(QuicServerId(origin1_, PRIVACY_MODE_DISABLED),
             session1->server_id());
@@ -5037,11 +5038,11 @@
   // |request2| does not pool to the first session, because the certificate does
   // not match.  Instead, another session is opened to the same destination, but
   // with a different QuicServerId.
-  QuicChromiumClientSession* session1 =
-      QuicHttpStreamPeer::GetSession(stream1.get());
-  QuicChromiumClientSession* session2 =
-      QuicHttpStreamPeer::GetSession(stream2.get());
-  EXPECT_NE(session1, session2);
+  QuicChromiumClientSession::Handle* session1 =
+      QuicHttpStreamPeer::GetSessionHandle(stream1.get());
+  QuicChromiumClientSession::Handle* session2 =
+      QuicHttpStreamPeer::GetSessionHandle(stream2.get());
+  EXPECT_FALSE(session1->SharesSameSession(*session2));
 
   EXPECT_EQ(QuicServerId(origin1_, privacy_mode_), session1->server_id());
   EXPECT_EQ(QuicServerId(origin2_, privacy_mode_), session2->server_id());
diff --git a/net/spdy/chromium/multiplexed_http_stream.cc b/net/spdy/chromium/multiplexed_http_stream.cc
index 985f643..41045ff7 100644
--- a/net/spdy/chromium/multiplexed_http_stream.cc
+++ b/net/spdy/chromium/multiplexed_http_stream.cc
@@ -8,21 +8,22 @@
 
 namespace net {
 
-MultiplexedHttpStream::MultiplexedHttpStream(MultiplexedSessionHandle session)
-    : session_(session) {}
+MultiplexedHttpStream::MultiplexedHttpStream(
+    std::unique_ptr<MultiplexedSessionHandle> session)
+    : session_(std::move(session)) {}
 
 MultiplexedHttpStream::~MultiplexedHttpStream() {}
 
 bool MultiplexedHttpStream::GetRemoteEndpoint(IPEndPoint* endpoint) {
-  return session_.GetRemoteEndpoint(endpoint);
+  return session_->GetRemoteEndpoint(endpoint);
 }
 
 void MultiplexedHttpStream::GetSSLInfo(SSLInfo* ssl_info) {
-  session_.GetSSLInfo(ssl_info);
+  session_->GetSSLInfo(ssl_info);
 }
 
 void MultiplexedHttpStream::SaveSSLInfo() {
-  session_.SaveSSLInfo();
+  session_->SaveSSLInfo();
 }
 
 void MultiplexedHttpStream::GetSSLCertRequestInfo(
@@ -36,7 +37,7 @@
     crypto::ECPrivateKey* key,
     TokenBindingType tb_type,
     std::vector<uint8_t>* out) {
-  return session_.GetTokenBindingSignature(key, tb_type, out);
+  return session_->GetTokenBindingSignature(key, tb_type, out);
 }
 
 void MultiplexedHttpStream::Drain(HttpNetworkSession* session) {
diff --git a/net/spdy/chromium/multiplexed_http_stream.h b/net/spdy/chromium/multiplexed_http_stream.h
index 9b0ef5f9..15bd7ff 100644
--- a/net/spdy/chromium/multiplexed_http_stream.h
+++ b/net/spdy/chromium/multiplexed_http_stream.h
@@ -13,7 +13,8 @@
 // Base class for SPDY and QUIC HttpStream subclasses.
 class NET_EXPORT_PRIVATE MultiplexedHttpStream : public HttpStream {
  public:
-  explicit MultiplexedHttpStream(MultiplexedSessionHandle session);
+  explicit MultiplexedHttpStream(
+      std::unique_ptr<MultiplexedSessionHandle> session);
   ~MultiplexedHttpStream() override;
 
   bool GetRemoteEndpoint(IPEndPoint* endpoint) override;
@@ -30,8 +31,12 @@
   // Caches SSL info from the underlying session.
   void SaveSSLInfo();
 
+ protected:
+  MultiplexedSessionHandle* session() { return session_.get(); }
+  const MultiplexedSessionHandle* session() const { return session_.get(); }
+
  private:
-  MultiplexedSessionHandle session_;
+  const std::unique_ptr<MultiplexedSessionHandle> session_;
 };
 
 }  // namespace net
diff --git a/net/spdy/chromium/multiplexed_session.cc b/net/spdy/chromium/multiplexed_session.cc
index 26cf453..e49e69b 100644
--- a/net/spdy/chromium/multiplexed_session.cc
+++ b/net/spdy/chromium/multiplexed_session.cc
@@ -12,12 +12,6 @@
   SaveSSLInfo();
 }
 
-MultiplexedSessionHandle::MultiplexedSessionHandle(
-    const MultiplexedSessionHandle& other) = default;
-
-MultiplexedSessionHandle::MultiplexedSessionHandle(
-    MultiplexedSessionHandle&& other) = default;
-
 MultiplexedSessionHandle::~MultiplexedSessionHandle() {}
 
 bool MultiplexedSessionHandle::GetRemoteEndpoint(IPEndPoint* endpoint) {
diff --git a/net/spdy/chromium/multiplexed_session.h b/net/spdy/chromium/multiplexed_session.h
index ce37a47..aaeb292 100644
--- a/net/spdy/chromium/multiplexed_session.h
+++ b/net/spdy/chromium/multiplexed_session.h
@@ -45,9 +45,7 @@
 class NET_EXPORT_PRIVATE MultiplexedSessionHandle {
  public:
   explicit MultiplexedSessionHandle(base::WeakPtr<MultiplexedSession> session);
-  MultiplexedSessionHandle(const MultiplexedSessionHandle& other);
-  MultiplexedSessionHandle(MultiplexedSessionHandle&& other);
-  ~MultiplexedSessionHandle();
+  virtual ~MultiplexedSessionHandle();
 
   // Gets the remote endpoint of the socket that the HTTP stream is using, if
   // any. Returns true and fills in |endpoint| if it is available; returns false
diff --git a/net/spdy/chromium/spdy_http_stream.cc b/net/spdy/chromium/spdy_http_stream.cc
index bb01b8541..c579ffb 100644
--- a/net/spdy/chromium/spdy_http_stream.cc
+++ b/net/spdy/chromium/spdy_http_stream.cc
@@ -36,7 +36,8 @@
 SpdyHttpStream::SpdyHttpStream(const base::WeakPtr<SpdySession>& spdy_session,
                                bool direct,
                                NetLogSource source_dependency)
-    : MultiplexedHttpStream(MultiplexedSessionHandle(spdy_session)),
+    : MultiplexedHttpStream(
+          base::MakeUnique<MultiplexedSessionHandle>(spdy_session)),
       spdy_session_(spdy_session),
       is_reused_(spdy_session_->IsReused()),
       source_dependency_(source_dependency),
diff --git a/rlz/BUILD.gn b/rlz/BUILD.gn
index 969e3fa9..f830d26a1 100644
--- a/rlz/BUILD.gn
+++ b/rlz/BUILD.gn
@@ -17,27 +17,18 @@
     "chromeos/lib/rlz_value_store_chromeos.cc",
     "chromeos/lib/rlz_value_store_chromeos.h",
     "ios/lib/machine_id_ios.cc",
-    "lib/assert.cc",
-    "lib/assert.h",
-    "lib/crc32.h",
-    "lib/crc32_wrapper.cc",
     "lib/crc8.cc",
     "lib/crc8.h",
     "lib/financial_ping.cc",
     "lib/financial_ping.h",
-    "lib/lib_values.cc",
-    "lib/lib_values.h",
     "lib/machine_id.cc",
     "lib/machine_id.h",
     "lib/recursive_cross_process_lock_posix.cc",
     "lib/recursive_cross_process_lock_posix.h",
-    "lib/rlz_enums.h",
     "lib/rlz_lib.cc",
     "lib/rlz_lib.h",
     "lib/rlz_lib_clear.cc",
     "lib/rlz_value_store.h",
-    "lib/string_utils.cc",
-    "lib/string_utils.h",
     "mac/lib/machine_id_mac.cc",
     "mac/lib/rlz_value_store_mac.h",
     "mac/lib/rlz_value_store_mac.mm",
@@ -61,10 +52,10 @@
   public_configs = [ ":rlz_config" ]
 
   deps = [
+    ":rlz_utils",
     "//base",
     "//base/third_party/dynamic_annotations",
     "//net",
-    "//third_party/zlib",
     "//url",
   ]
 
@@ -86,6 +77,32 @@
   }
 }
 
+source_set("rlz_utils") {
+  sources = [
+    "lib/assert.cc",
+    "lib/assert.h",
+    "lib/crc32.h",
+    "lib/crc32_wrapper.cc",
+    "lib/lib_values.cc",
+    "lib/lib_values.h",
+    "lib/net_response_check.cc",
+    "lib/net_response_check.h",
+    "lib/rlz_api.h",
+    "lib/rlz_enums.h",
+    "lib/string_utils.cc",
+    "lib/string_utils.h",
+  ]
+
+  public_configs = [ ":rlz_config" ]
+
+  deps = [
+    "//base",
+    "//net",
+    "//third_party/zlib",
+    "//url",
+  ]
+}
+
 source_set("test_support") {
   testonly = true
   sources = [
@@ -118,6 +135,7 @@
 
   deps = [
     ":rlz_lib",
+    ":rlz_utils",
     ":test_support",
     "//base",
     "//net:test_support",
@@ -148,6 +166,7 @@
     ]
     deps = [
       ":rlz_lib",
+      ":rlz_utils",
       "//build/config/sanitizers:deps",
       "//third_party/zlib",
     ]
diff --git a/rlz/lib/net_response_check.cc b/rlz/lib/net_response_check.cc
new file mode 100644
index 0000000..e018176
--- /dev/null
+++ b/rlz/lib/net_response_check.cc
@@ -0,0 +1,67 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+#include "rlz/lib/net_response_check.h"
+
+#include "base/strings/string_util.h"
+#include "rlz/lib/assert.h"
+#include "rlz/lib/crc32.h"
+#include "rlz/lib/string_utils.h"
+
+// Checksum validation convenience call for RLZ responses.
+namespace rlz_lib {
+
+bool IsPingResponseValid(const char* response, int* checksum_idx) {
+  if (!response || !response[0])
+    return false;
+
+  if (checksum_idx)
+    *checksum_idx = -1;
+
+  if (strlen(response) > kMaxPingResponseLength) {
+    ASSERT_STRING("IsPingResponseValid: response is too long to parse.");
+    return false;
+  }
+
+  // Find the checksum line.
+  std::string response_string(response);
+
+  std::string checksum_param("\ncrc32: ");
+  int calculated_crc;
+  int checksum_index = response_string.find(checksum_param);
+  if (checksum_index >= 0) {
+    // Calculate checksum of message preceeding checksum line.
+    // (+ 1 to include the \n)
+    std::string message(response_string.substr(0, checksum_index + 1));
+    if (!Crc32(message.c_str(), &calculated_crc))
+      return false;
+  } else {
+    checksum_param = "crc32: ";  // Empty response case.
+    if (!base::StartsWith(response_string, checksum_param,
+                          base::CompareCase::SENSITIVE))
+      return false;
+
+    checksum_index = 0;
+    if (!Crc32("", &calculated_crc))
+      return false;
+  }
+
+  // Find the checksum value on the response.
+  int checksum_end = response_string.find("\n", checksum_index + 1);
+  if (checksum_end < 0)
+    checksum_end = response_string.size();
+
+  int checksum_begin = checksum_index + checksum_param.size();
+  std::string checksum =
+      response_string.substr(checksum_begin, checksum_end - checksum_begin + 1);
+  base::TrimWhitespaceASCII(checksum, base::TRIM_ALL, &checksum);
+
+  if (checksum_idx)
+    *checksum_idx = checksum_index;
+
+  return calculated_crc == HexStringToInteger(checksum.c_str());
+}
+
+}  // namespace rlz_lib
diff --git a/rlz/lib/net_response_check.h b/rlz/lib/net_response_check.h
new file mode 100644
index 0000000..67d13ca9
--- /dev/null
+++ b/rlz/lib/net_response_check.h
@@ -0,0 +1,27 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+#ifndef RLZ_LIB_NET_RESPONSE_CHECK_H_
+#define RLZ_LIB_NET_RESPONSE_CHECK_H_
+
+#include <string>
+#include "rlz/lib/rlz_api.h"
+
+// Checksum validation convenience call for RLZ network responses.
+namespace rlz_lib {
+// The maximum length of a ping response we will parse in bytes. If the response
+// is bigger, please break it up into separate calls.
+const size_t kMaxPingResponseLength = 0x4000;  // 16K
+
+// Checks if a ping response is valid - ie. it has a checksum line which
+// is the CRC-32 checksum of the message up to the checksum. If
+// checksum_idx is not NULL, it will get the index of the checksum, i.e. -
+// the effective end of the message.
+// Access: No restrictions.
+bool RLZ_LIB_API IsPingResponseValid(const char* response, int* checksum_idx);
+
+};  // namespace rlz_lib
+
+#endif  // RLZ_LIB_NET_RESPONSE_CHECK_H_
diff --git a/rlz/lib/rlz_api.h b/rlz/lib/rlz_api.h
new file mode 100644
index 0000000..0205090
--- /dev/null
+++ b/rlz/lib/rlz_api.h
@@ -0,0 +1,15 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+#ifndef RLZ_LIB_RLZ_API_H_
+#define RLZ_LIB_RLZ_API_H_
+
+#if defined(OS_WIN)
+#define RLZ_LIB_API __cdecl
+#else
+#define RLZ_LIB_API
+#endif
+
+#endif  // RLZ_LIB_RLZ_API_H_
diff --git a/rlz/lib/rlz_lib.cc b/rlz/lib/rlz_lib.cc
index 081f80b0..eb4c8d9 100644
--- a/rlz/lib/rlz_lib.cc
+++ b/rlz/lib/rlz_lib.cc
@@ -14,9 +14,9 @@
 #include "base/strings/stringprintf.h"
 #include "build/build_config.h"
 #include "rlz/lib/assert.h"
-#include "rlz/lib/crc32.h"
 #include "rlz/lib/financial_ping.h"
 #include "rlz/lib/lib_values.h"
+#include "rlz/lib/net_response_check.h"
 #include "rlz/lib/rlz_value_store.h"
 #include "rlz/lib/string_utils.h"
 
@@ -377,57 +377,6 @@
   return true;
 }
 
-bool IsPingResponseValid(const char* response, int* checksum_idx) {
-  if (!response || !response[0])
-    return false;
-
-  if (checksum_idx)
-    *checksum_idx = -1;
-
-  if (strlen(response) > kMaxPingResponseLength) {
-    ASSERT_STRING("IsPingResponseValid: response is too long to parse.");
-    return false;
-  }
-
-  // Find the checksum line.
-  std::string response_string(response);
-
-  std::string checksum_param("\ncrc32: ");
-  int calculated_crc;
-  int checksum_index = response_string.find(checksum_param);
-  if (checksum_index >= 0) {
-    // Calculate checksum of message preceeding checksum line.
-    // (+ 1 to include the \n)
-    std::string message(response_string.substr(0, checksum_index + 1));
-    if (!Crc32(message.c_str(), &calculated_crc))
-      return false;
-  } else {
-    checksum_param = "crc32: ";  // Empty response case.
-    if (!base::StartsWith(response_string, checksum_param,
-                          base::CompareCase::SENSITIVE))
-      return false;
-
-    checksum_index = 0;
-    if (!Crc32("", &calculated_crc))
-      return false;
-  }
-
-  // Find the checksum value on the response.
-  int checksum_end = response_string.find("\n", checksum_index + 1);
-  if (checksum_end < 0)
-    checksum_end = response_string.size();
-
-  int checksum_begin = checksum_index + checksum_param.size();
-  std::string checksum = response_string.substr(checksum_begin,
-      checksum_end - checksum_begin + 1);
-  base::TrimWhitespaceASCII(checksum, base::TRIM_ALL, &checksum);
-
-  if (checksum_idx)
-    *checksum_idx = checksum_index;
-
-  return calculated_crc == HexStringToInteger(checksum.c_str());
-}
-
 // Complex helpers built on top of other functions.
 
 bool ParseFinancialPingResponse(Product product, const char* response) {
diff --git a/rlz/lib/rlz_lib.h b/rlz/lib/rlz_lib.h
index 0f98c4c..886bd0c 100644
--- a/rlz/lib/rlz_lib.h
+++ b/rlz/lib/rlz_lib.h
@@ -18,14 +18,9 @@
 
 #include "build/build_config.h"
 
+#include "rlz/lib/rlz_api.h"
 #include "rlz/lib/rlz_enums.h"
 
-#if defined(OS_WIN)
-#define RLZ_LIB_API __cdecl
-#else
-#define RLZ_LIB_API
-#endif
-
 // Define one of
 // + RLZ_NETWORK_IMPLEMENTATION_WIN_INET: Uses win inet to send financial pings.
 // + RLZ_NETWORK_IMPLEMENTATION_CHROME_NET: Uses chrome's network stack to send
@@ -77,9 +72,6 @@
 const size_t kMaxDccLength = 128;
 // The maximum length of a CGI string in bytes.
 const size_t kMaxCgiLength = 2048;
-// The maximum length of a ping response we will parse in bytes. If the response
-// is bigger, please break it up into separate calls.
-const size_t kMaxPingResponseLength = 0x4000;  // 16K
 
 #if defined(RLZ_NETWORK_IMPLEMENTATION_CHROME_NET)
 // Set the URLRequestContextGetter used by SendFinancialPing(). The IO message
@@ -177,15 +169,6 @@
                                           char* request,
                                           size_t request_buffer_size);
 
-// Checks if a ping response is valid - ie. it has a checksum line which
-// is the CRC-32 checksum of the message uptil the checksum. If
-// checksum_idx is not NULL, it will get the index of the checksum, i.e. -
-// the effective end of the message.
-// Access: No restrictions.
-bool RLZ_LIB_API IsPingResponseValid(const char* response,
-                                     int* checksum_idx);
-
-
 // Complex helpers built on top of other functions.
 
 // Parses the responses from the financial server and updates product state
diff --git a/rlz/lib/rlz_lib_test.cc b/rlz/lib/rlz_lib_test.cc
index 911a8f4..a81f317 100644
--- a/rlz/lib/rlz_lib_test.cc
+++ b/rlz/lib/rlz_lib_test.cc
@@ -25,6 +25,7 @@
 #include "testing/gtest/include/gtest/gtest.h"
 
 #include "rlz/lib/financial_ping.h"
+#include "rlz/lib/net_response_check.h"
 #include "rlz/lib/rlz_lib.h"
 #include "rlz/lib/rlz_value_store.h"
 #include "rlz/test/rlz_test_helpers.h"
diff --git a/rlz/win/dll/exports.cc b/rlz/win/dll/exports.cc
index 4d7f8d4..2810772 100644
--- a/rlz/win/dll/exports.cc
+++ b/rlz/win/dll/exports.cc
@@ -6,6 +6,7 @@
 
 #include <stddef.h>
 
+#include "rlz/lib/net_response_check.h"
 #include "rlz/lib/rlz_lib.h"
 
 #define RLZ_DLL_EXPORT extern "C" __declspec(dllexport)
diff --git a/rlz/win/lib/machine_deal.cc b/rlz/win/lib/machine_deal.cc
index 8bfa9db..2a17be7 100644
--- a/rlz/win/lib/machine_deal.cc
+++ b/rlz/win/lib/machine_deal.cc
@@ -17,6 +17,7 @@
 #include "base/win/registry.h"
 #include "rlz/lib/assert.h"
 #include "rlz/lib/lib_values.h"
+#include "rlz/lib/net_response_check.h"
 #include "rlz/win/lib/lib_mutex.h"
 #include "rlz/win/lib/registry_util.h"
 #include "rlz/win/lib/rlz_value_store_registry.h"
diff --git a/third_party/WebKit/LayoutTests/FlagExpectations/enable-blink-features=LayoutNG b/third_party/WebKit/LayoutTests/FlagExpectations/enable-blink-features=LayoutNG
index 659e50d..654ccba 100644
--- a/third_party/WebKit/LayoutTests/FlagExpectations/enable-blink-features=LayoutNG
+++ b/third_party/WebKit/LayoutTests/FlagExpectations/enable-blink-features=LayoutNG
@@ -5653,6 +5653,7 @@
 crbug.com/591099 external/wpt/service-workers/service-worker/fetch-mixed-content-to-outscope.https.html [ Crash ]
 crbug.com/591099 external/wpt/service-workers/service-worker/fetch-request-css-images.https.html [ Crash ]
 crbug.com/591099 external/wpt/service-workers/service-worker/fetch-request-redirect.https.html [ Crash ]
+crbug.com/591099 external/wpt/service-workers/service-worker/fetch-response-taint.https.html [ Crash Pass ]
 crbug.com/591099 external/wpt/service-workers/service-worker/foreign-fetch-basics.https.html [ Crash ]
 crbug.com/591099 external/wpt/service-workers/service-worker/foreign-fetch-cors.https.html [ Crash ]
 crbug.com/591099 external/wpt/service-workers/service-worker/foreign-fetch-workers.https.html [ Crash ]
@@ -15593,7 +15594,6 @@
 crbug.com/591099 http/tests/serviceworker/chromium.fetch-event-headers.html [ Crash ]
 crbug.com/591099 http/tests/serviceworker/fetch-request-fallback.html [ Crash Pass ]
 crbug.com/591099 http/tests/serviceworker/fetch-request-xhr.html [ Failure Pass ]
-crbug.com/591099 http/tests/serviceworker/fetch-response-taint.html [ Crash Pass ]
 crbug.com/591099 http/tests/serviceworker/indexeddb.html [ Pass Timeout ]
 crbug.com/591099 http/tests/serviceworker/navigation-redirect.html [ Pass Timeout ]
 crbug.com/591099 http/tests/serviceworker/sandbox-iframe-register-link-element.html [ Crash ]
diff --git a/third_party/WebKit/LayoutTests/compositing/transform-gained-3d-expected.html b/third_party/WebKit/LayoutTests/compositing/transform-gained-3d-expected.html
new file mode 100644
index 0000000..6431fe9
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/compositing/transform-gained-3d-expected.html
@@ -0,0 +1,4 @@
+<!DOCTYPE html>
+<div style="width:100px; height:100px; background:green;">
+</div>
+The test verifies the compositing system picks up elements that gained depth from transform mutation. This test succeeds if a green box fully occludes the red box below.
\ No newline at end of file
diff --git a/third_party/WebKit/LayoutTests/compositing/transform-gained-3d.html b/third_party/WebKit/LayoutTests/compositing/transform-gained-3d.html
new file mode 100644
index 0000000..9f3633a
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/compositing/transform-gained-3d.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<div style="width:100px; height:100px; background:red; perspective:2px;">
+  <div id="bug" style="position:absolute; left:25px; top:25px; width:50px; height:50px; background:green; transform:translateZ(0);"></div>
+</div>
+The test verifies the compositing system picks up elements that gained depth from transform mutation. This test succeeds if a green box fully occludes the red box below.
+<script src="../resources/run-after-layout-and-paint.js"></script>
+<script>
+runAfterLayoutAndPaint(function(){
+    document.getElementById("bug").style.transform = "translateZ(1px)";
+}, true);
+</script>
\ No newline at end of file
diff --git a/third_party/WebKit/LayoutTests/external/wpt/service-workers/service-worker/fetch-response-taint.https.html b/third_party/WebKit/LayoutTests/external/wpt/service-workers/service-worker/fetch-response-taint.https.html
new file mode 100644
index 0000000..a6e7f984
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/service-workers/service-worker/fetch-response-taint.https.html
@@ -0,0 +1,217 @@
+<!DOCTYPE html>
+<title>Service Worker: Tainting of responses fetched via SW.</title>
+<!-- This test makes a large number of requests sequentially. -->
+<meta name="timeout" content="long">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/get-host-info.sub.js"></script>
+<script src="resources/test-helpers.sub.js"></script>
+<body>
+<script>
+var host_info = get_host_info();
+var BASE_ORIGIN = host_info.HTTPS_ORIGIN;
+var OTHER_ORIGIN = host_info.HTTPS_REMOTE_ORIGIN;
+var BASE_URL = BASE_ORIGIN + base_path() +
+               'resources/fetch-access-control.py?';
+var OTHER_BASE_URL = OTHER_ORIGIN + base_path() +
+                     'resources/fetch-access-control.py?';
+
+function frame_fetch(frame, url, mode, credentials) {
+  var foreignPromise = frame.contentWindow.fetch(
+      new Request(url, {mode: mode, credentials: credentials}))
+
+  // Event loops should be shared between contexts of similar origin, not all
+  // browsers adhere to this expectation at the time of this writing. Incorrect
+  // behavior in this regard can interfere with test execution when the
+  // provided iframe is removed from the document.
+  //
+  // WPT maintains a test dedicated the expected treatment of event loops, so
+  // the following workaround is acceptable in this context.
+  return Promise.resolve(foreignPromise);
+}
+
+var login_and_register;
+promise_test(function(t) {
+    var SCOPE = 'resources/fetch-response-taint-iframe.html';
+    var SCRIPT = 'resources/fetch-rewrite-worker.js';
+    var registration;
+
+    login_and_register = login_https(t, host_info.HTTPS_ORIGIN, host_info.HTTPS_REMOTE_ORIGIN)
+      .then(function() {
+          return service_worker_unregister_and_register(t, SCRIPT, SCOPE);
+        })
+      .then(function(r) {
+          registration = r;
+          return wait_for_state(t, registration.installing, 'activated');
+        })
+      .then(function() { return with_iframe(SCOPE); })
+      .then(function(f) {
+          // This test should not be considered complete until after the
+          // service worker has been unregistered. Currently, `testharness.js`
+          // does not support asynchronous global "tear down" logic, so this
+          // must be expressed using a dedicated `promise_test`. Because the
+          // other sub-tests in this file are declared synchronously, this
+          // test will be the final test executed.
+          promise_test(function(t) {
+              f.remove();
+              return registration.unregister();
+            }, 'restore global state');
+
+          return f;
+        });
+    return login_and_register;
+  }, 'initialize global state');
+
+function ng_test(url, mode, credentials) {
+  promise_test(function(t) {
+      return login_and_register
+        .then(function(frame) {
+            var fetchRequest = frame_fetch(frame, url, mode, credentials);
+            return promise_rejects(t, new TypeError(), fetchRequest);
+          });
+    }, 'url:\"' + url + '\" mode:\"' + mode +
+       '\" credentials:\"' + credentials + '\" should fail.');
+}
+
+function ok_test(url, mode, credentials, expected_type, expected_username) {
+  promise_test(function() {
+      return login_and_register.then(function(frame) {
+            return frame_fetch(frame, url, mode, credentials)
+          })
+        .then(function(res) {
+            assert_equals(res.type, expected_type, 'response type');
+            return res.text();
+          })
+        .then(function(text) {
+            if (expected_type == 'opaque') {
+              assert_equals(text, '');
+            } else {
+              return new Promise(function(resolve) {
+                    var report = resolve;
+                    // text must contain report() call.
+                    eval(text);
+                  })
+                .then(function(result) {
+                    assert_equals(result.username, expected_username);
+                  });
+            }
+          });
+    }, 'fetching url:\"' + url + '\" mode:\"' + mode +
+                        '\" credentials:\"' + credentials + '\" should ' +
+                        'succeed.');
+}
+
+function build_rewrite_url(origin, url, mode, credentials) {
+  return origin + '/?url=' + encodeURIComponent(url) + '&mode=' + mode +
+      '&credentials=' + credentials + '&';
+}
+
+function for_each_origin_mode_credentials(callback) {
+  [BASE_ORIGIN, OTHER_ORIGIN].forEach(function(origin) {
+      ['same-origin', 'no-cors', 'cors'].forEach(function(mode) {
+          ['omit', 'same-origin', 'include'].forEach(function(credentials) {
+              callback(origin, mode, credentials);
+            });
+        });
+    });
+}
+
+ok_test(BASE_URL, 'same-origin', 'omit', 'basic', 'undefined');
+ok_test(BASE_URL, 'same-origin', 'same-origin', 'basic', 'username2s');
+ok_test(BASE_URL, 'same-origin', 'include', 'basic', 'username2s');
+ok_test(BASE_URL, 'no-cors', 'omit', 'basic', 'undefined');
+ok_test(BASE_URL, 'no-cors', 'same-origin', 'basic', 'username2s');
+ok_test(BASE_URL, 'no-cors', 'include', 'basic', 'username2s');
+ok_test(BASE_URL, 'cors', 'omit', 'basic', 'undefined');
+ok_test(BASE_URL, 'cors', 'same-origin', 'basic', 'username2s');
+ok_test(BASE_URL, 'cors', 'include', 'basic', 'username2s');
+ng_test(OTHER_BASE_URL, 'same-origin', 'omit');
+ng_test(OTHER_BASE_URL, 'same-origin', 'same-origin');
+ng_test(OTHER_BASE_URL, 'same-origin', 'include');
+ok_test(OTHER_BASE_URL, 'no-cors', 'omit', 'opaque');
+ok_test(OTHER_BASE_URL, 'no-cors', 'same-origin', 'opaque');
+ok_test(OTHER_BASE_URL, 'no-cors', 'include', 'opaque');
+ng_test(OTHER_BASE_URL, 'cors', 'omit');
+ng_test(OTHER_BASE_URL, 'cors', 'same-origin');
+ng_test(OTHER_BASE_URL, 'cors', 'include');
+ok_test(OTHER_BASE_URL + 'ACAOrigin=*', 'cors', 'omit', 'cors', 'undefined');
+ok_test(OTHER_BASE_URL + 'ACAOrigin=*', 'cors', 'same-origin', 'cors',
+        'undefined');
+ng_test(OTHER_BASE_URL + 'ACAOrigin=*', 'cors', 'include');
+ok_test(OTHER_BASE_URL + 'ACAOrigin=' + BASE_ORIGIN + '&ACACredentials=true',
+        'cors', 'include', 'cors', 'username1s')
+
+for_each_origin_mode_credentials(function(origin, mode, credentials) {
+  var url = build_rewrite_url(
+      origin, BASE_URL, 'same-origin', 'omit');
+  // Fetch to the other origin with same-origin mode should fail.
+  if (origin == OTHER_ORIGIN && mode == 'same-origin') {
+    ng_test(url, mode, credentials);
+  } else {
+    // The response type from the SW should be basic
+    ok_test(url, mode, credentials, 'basic', 'undefined');
+  }
+});
+
+for_each_origin_mode_credentials(function(origin, mode, credentials) {
+  var url = build_rewrite_url(
+      origin, BASE_URL, 'same-origin', 'same-origin');
+
+  // Fetch to the other origin with same-origin mode should fail.
+  if (origin == OTHER_ORIGIN && mode == 'same-origin') {
+    ng_test(url, mode, credentials);
+  } else {
+    // The response type from the SW should be basic.
+    ok_test(url, mode, credentials, 'basic', 'username2s');
+  }
+});
+
+for_each_origin_mode_credentials(function(origin, mode, credentials) {
+  var url = build_rewrite_url(
+      origin, OTHER_BASE_URL, 'same-origin', 'omit');
+  // The response from the SW should be an error.
+  ng_test(url, mode, credentials);
+});
+
+for_each_origin_mode_credentials(function(origin, mode, credentials) {
+  var url = build_rewrite_url(
+      origin, OTHER_BASE_URL, 'no-cors', 'omit');
+
+  // SW can respond only to no-cors requests.
+  if (mode != 'no-cors') {
+    ng_test(url, mode, credentials);
+  } else {
+    // The response type from the SW should be opaque.
+    ok_test(url, mode, credentials, 'opaque');
+  }
+});
+
+for_each_origin_mode_credentials(function(origin, mode, credentials) {
+  var url = build_rewrite_url(
+      origin, OTHER_BASE_URL + 'ACAOrigin=*', 'cors', 'omit');
+
+  // Fetch to the other origin with same-origin mode should fail.
+  if (origin == OTHER_ORIGIN && mode == 'same-origin') {
+    ng_test(url, mode, credentials);
+  } else {
+    // The response from the SW should be cors.
+    ok_test(url, mode, credentials, 'cors', 'undefined');
+  }
+});
+
+for_each_origin_mode_credentials(function(origin, mode, credentials) {
+  var url = build_rewrite_url(
+      origin,
+      OTHER_BASE_URL + 'ACAOrigin=' + BASE_ORIGIN +
+      '&ACACredentials=true',
+      'cors', 'include');
+  // Fetch to the other origin with same-origin mode should fail.
+  if (origin == OTHER_ORIGIN && mode == 'same-origin') {
+    ng_test(url, mode, credentials);
+  } else {
+    // The response from the SW should be cors.
+    ok_test(url, mode, credentials, 'cors', 'username1s');
+  }
+});
+</script>
+</body>
diff --git a/third_party/WebKit/LayoutTests/http/tests/serviceworker/resources/fetch-response-taint-iframe.html b/third_party/WebKit/LayoutTests/external/wpt/service-workers/service-worker/resources/fetch-response-taint-iframe.html
similarity index 100%
rename from third_party/WebKit/LayoutTests/http/tests/serviceworker/resources/fetch-response-taint-iframe.html
rename to third_party/WebKit/LayoutTests/external/wpt/service-workers/service-worker/resources/fetch-response-taint-iframe.html
diff --git a/third_party/WebKit/LayoutTests/http/tests/serviceworker/fetch-response-taint.html b/third_party/WebKit/LayoutTests/http/tests/serviceworker/fetch-response-taint.html
deleted file mode 100644
index de93c03..0000000
--- a/third_party/WebKit/LayoutTests/http/tests/serviceworker/fetch-response-taint.html
+++ /dev/null
@@ -1,193 +0,0 @@
-<!DOCTYPE html>
-<title>Service Worker: Tainting of responses fetched via SW.</title>
-<script src="../resources/testharness.js"></script>
-<script src="../resources/testharnessreport.js"></script>
-<script src="../resources/get-host-info.js?pipe=sub"></script>
-<script src="resources/test-helpers.js"></script>
-<body>
-<script>
-var host_info = get_host_info();
-var BASE_ORIGIN = host_info.HTTP_ORIGIN;
-var OTHER_ORIGIN = host_info.HTTP_REMOTE_ORIGIN;
-var BASE_URL = BASE_ORIGIN + base_path() +
-               'resources/fetch-access-control.php?';
-var OTHER_BASE_URL = OTHER_ORIGIN + base_path() +
-                     'resources/fetch-access-control.php?';
-
-function frame_fetch(frame, url, mode, credentials) {
-  return frame.contentWindow.fetch(
-      new Request(url, {mode: mode, credentials: credentials}));
-}
-
-function ng_test(frame, url, mode, credentials) {
-  return frame_fetch(frame, url, mode, credentials).then(
-      function() {
-        throw new Error('fetching url:\"' + url + '\" mode:\"' + mode +
-                        '\" credentials:\"' + credentials + '\" should fail.');
-      },
-      function() {});
-}
-
-function ok_test(frame, url, mode, credentials, expected_type,
-                 expected_username) {
-  return frame_fetch(frame, url, mode, credentials)
-    .then(function(res) {
-        assert_equals(res.type, expected_type);
-        return res.text();
-      })
-    .then(function(text) {
-        if (expected_type == 'opaque') {
-          assert_equals(text, '');
-        } else {
-          return new Promise(function(resolve) {
-                var report = resolve;
-                // text must contain report() call.
-                eval(text);
-              })
-            .then(function(result) {
-                assert_equals(result.username, expected_username);
-              });
-        }
-      })
-    .catch(function(reason) {
-        throw new Error('fetching url:\"' + url + '\" mode:\"' + mode +
-                        '\" credentials:\"' + credentials + '\" should ' +
-                        'success. - ' + reason.message);
-      });
-}
-
-function build_rewrite_url(origin, url, mode, credentials) {
-  return origin + '/?url=' + encodeURIComponent(url) + '&mode=' + mode +
-      '&credentials=' + credentials + '&';
-}
-
-function for_each_origin_mode_credentials(callback) {
-  [BASE_ORIGIN, OTHER_ORIGIN].forEach(function(origin) {
-      ['same-origin', 'no-cors', 'cors'].forEach(function(mode) {
-          ['omit', 'same-origin', 'include'].forEach(function(credentials) {
-              callback(origin, mode, credentials);
-            });
-        });
-    });
-}
-
-promise_test(function(t) {
-    var SCOPE = 'resources/fetch-response-taint-iframe.html';
-    var SCRIPT = 'resources/fetch-rewrite-worker.js';
-    var frame = undefined;
-
-    return login(t, host_info.HTTP_ORIGIN, host_info.HTTP_REMOTE_ORIGIN)
-      .then(function() {
-          return service_worker_unregister_and_register(t, SCRIPT, SCOPE);
-        })
-      .then(function(registration) {
-          return wait_for_state(t, registration.installing, 'activated');
-        })
-      .then(function() { return with_iframe(SCOPE); })
-      .then(function(f) {
-          frame = f;
-          var promises = [
-            ok_test(f, BASE_URL, 'same-origin', 'omit', 'basic', 'undefined'),
-            ok_test(f, BASE_URL, 'same-origin', 'same-origin', 'basic',
-                    'username1'),
-            ok_test(f, BASE_URL, 'same-origin', 'include', 'basic',
-                    'username1'),
-            ok_test(f, BASE_URL, 'no-cors', 'omit', 'basic', 'undefined'),
-            ok_test(f, BASE_URL, 'no-cors', 'same-origin', 'basic',
-                    'username1'),
-            ok_test(f, BASE_URL, 'no-cors', 'include', 'basic', 'username1'),
-            ok_test(f, BASE_URL, 'cors', 'omit', 'basic', 'undefined'),
-            ok_test(f, BASE_URL, 'cors', 'same-origin', 'basic', 'username1'),
-            ok_test(f, BASE_URL, 'cors', 'include', 'basic', 'username1'),
-            ng_test(f, OTHER_BASE_URL, 'same-origin', 'omit'),
-            ng_test(f, OTHER_BASE_URL, 'same-origin', 'same-origin'),
-            ng_test(f, OTHER_BASE_URL, 'same-origin', 'include'),
-            ok_test(f, OTHER_BASE_URL, 'no-cors', 'omit', 'opaque'),
-            ok_test(f, OTHER_BASE_URL, 'no-cors', 'same-origin', 'opaque'),
-            ok_test(f, OTHER_BASE_URL, 'no-cors', 'include', 'opaque'),
-            ng_test(f, OTHER_BASE_URL, 'cors', 'omit'),
-            ng_test(f, OTHER_BASE_URL, 'cors', 'same-origin'),
-            ng_test(f, OTHER_BASE_URL, 'cors', 'include'),
-            ok_test(f, OTHER_BASE_URL + 'ACAOrigin=*', 'cors', 'omit', 'cors',
-                    'undefined'),
-            ok_test(f, OTHER_BASE_URL + 'ACAOrigin=*', 'cors', 'same-origin',
-                    'cors', 'undefined'),
-            ng_test(f, OTHER_BASE_URL + 'ACAOrigin=*', 'cors', 'include'),
-            ok_test(f,
-                    OTHER_BASE_URL + 'ACAOrigin=' + BASE_ORIGIN +
-                    '&ACACredentials=true',
-                    'cors', 'include', 'cors', 'username2')
-          ];
-
-          for_each_origin_mode_credentials(function(origin, mode, credentials) {
-            var url = build_rewrite_url(
-                origin, BASE_URL, 'same-origin', 'omit');
-            // Fetch to the other origin with same-origin mode should fail.
-            if (origin == OTHER_ORIGIN && mode == 'same-origin')
-              return promises.push(ng_test(f, url, mode, credentials));
-            // The response type from the SW should be basic
-            promises.push(
-                ok_test(f, url, mode, credentials, 'basic', 'undefined'));
-          });
-
-          for_each_origin_mode_credentials(function(origin, mode, credentials) {
-            var url = build_rewrite_url(
-                origin, BASE_URL, 'same-origin', 'same-origin');
-            // Fetch to the other origin with same-origin mode should fail.
-            if (origin == OTHER_ORIGIN && mode == 'same-origin')
-              return promises.push(ng_test(f, url, mode, credentials));
-            // The response type from the SW should be basic.
-            promises.push(
-                ok_test(f, url, mode, credentials, 'basic', 'username1'));
-          });
-
-          for_each_origin_mode_credentials(function(origin, mode, credentials) {
-            var url = build_rewrite_url(
-                origin, OTHER_BASE_URL, 'same-origin', 'omit');
-            // The response from the SW should be an error.
-            promises.push(ng_test(f, url, mode, credentials));
-          });
-
-          for_each_origin_mode_credentials(function(origin, mode, credentials) {
-            var url = build_rewrite_url(
-                origin, OTHER_BASE_URL, 'no-cors', 'omit');
-            // SW can respond only to no-cors requests.
-            if (mode != 'no-cors')
-              return promises.push(ng_test(f, url, mode, credentials));
-            // The response type from the SW should be opaque.
-            promises.push(ok_test(f, url, mode, credentials, 'opaque'));
-          });
-
-          for_each_origin_mode_credentials(function(origin, mode, credentials) {
-            var url = build_rewrite_url(
-                origin, OTHER_BASE_URL + 'ACAOrigin=*', 'cors', 'omit');
-            // Fetch to the other origin with same-origin mode should fail.
-            if (origin == OTHER_ORIGIN && mode == 'same-origin')
-              return promises.push(ng_test(f, url, mode, credentials));
-            // The response from the SW should be cors.
-            promises.push(
-                ok_test(f, url, mode, credentials, 'cors', 'undefined'));
-          });
-
-          for_each_origin_mode_credentials(function(origin, mode, credentials) {
-            var url = build_rewrite_url(
-                origin,
-                OTHER_BASE_URL + 'ACAOrigin=' + BASE_ORIGIN +
-                '&ACACredentials=true',
-                'cors', 'include');
-            // Fetch to the other origin with same-origin mode should fail.
-            if (origin == OTHER_ORIGIN && mode == 'same-origin')
-              return promises.push(ng_test(f, url, mode, credentials));
-            // The response from the SW should be cors.
-            promises.push(
-                ok_test(f, url, mode, credentials, 'cors', 'username2'));
-          });
-          return Promise.all(promises);
-        })
-      .then(function(f) {
-          frame.remove()
-        })
-      .catch(unreached_rejection(t));
-  }, 'Verify the tainting of responses fetched via SW');
-</script>
-</body>
diff --git a/third_party/WebKit/LayoutTests/imagecapture/takephoto-with-photosettings.html b/third_party/WebKit/LayoutTests/imagecapture/takephoto-with-photosettings.html
new file mode 100644
index 0000000..86bfea5
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/imagecapture/takephoto-with-photosettings.html
@@ -0,0 +1,71 @@
+<!DOCTYPE html>
+<script src="../resources/testharness.js"></script>
+<script src="../resources/testharnessreport.js"></script>
+<script src="../resources/mojo-helpers.js"></script>
+<script src="resources/mock-imagecapture.js"></script>
+<body>
+<canvas id='canvas' width=10 height=10/>
+</body>
+<script>
+
+const fillLightModeNames = ["off", "auto", "flash"];
+
+// This test verifies that ImageCapture can call takePhoto with a PhotoSettings
+// argument, with a mock Mojo interface implementation.
+
+async_test(function(t) {
+  var canvas = document.getElementById('canvas');
+  var context = canvas.getContext("2d");
+  context.fillStyle = "red";
+  context.fillRect(0, 0, 10, 10);
+  var stream = canvas.captureStream();
+
+  var theMock = null;
+  const optionsDict = { imageWidth : 1080,
+                        imageHeight : 100,
+                        redEyeReduction : true,
+                        fillLightMode : "flash"
+                      };
+  mockImageCaptureReady
+    .then(mock => {
+      theMock = mock;
+      return new ImageCapture(stream.getVideoTracks()[0]);
+    },
+    error => {
+      assert_unreached("Error creating MockImageCapture: " + error);
+    })
+    .then(capturer => {
+      return capturer.takePhoto(optionsDict);
+    })
+    .then(blob => {
+      // JS Blob is almost-opaque, can only check |type| and |size|.
+      assert_equals(blob.type, 'image/cat');
+      assert_equals(blob.size, 2);
+
+      assert_equals(true, theMock.options().has_width, 'has_width');
+      assert_equals(optionsDict.imageWidth, theMock.options().width, 'width');
+      assert_equals(true, theMock.options().has_height, 'has_height');
+      assert_equals(optionsDict.imageHeight, theMock.options().height,
+                    'height');
+
+      // Depending on how mojo boolean packing in integers is arranged, this can
+      // be a number instead of a boolean, compare directly.
+      // TODO(mcasas): Revert to assert_equals() when yzshen@ has sorted it out.
+      assert_true(
+          optionsDict.redEyeReduction == theMock.options().red_eye_reduction,
+          'red_eye_reduction');
+
+      assert_equals(true, theMock.options().has_fill_light_mode,
+                    'has_fill_light_mode');
+      assert_equals(optionsDict.fillLightMode,
+                    fillLightModeNames[theMock.options().fill_light_mode],
+                    'fillLightMode');
+
+      t.done();
+    })
+    .catch(error => {
+      assert_unreached("Error during takePhoto(): " + error.message);
+    });
+}, 'exercises ImageCapture.takePhoto(PhotoSettings dictionary)');
+
+</script>
diff --git a/third_party/WebKit/LayoutTests/paint/invalidation/subpixel-offset-scaled-transform-expected.html b/third_party/WebKit/LayoutTests/paint/invalidation/subpixel-offset-scaled-transform-expected.html
new file mode 100644
index 0000000..f37c7eef
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/paint/invalidation/subpixel-offset-scaled-transform-expected.html
@@ -0,0 +1,6 @@
+<!DOCTYPE html>
+<style>
+.square { width: 40px; height: 40px; background: blue; position: relative; }
+</style>
+<div class="square"></div>
+<div class="square" style="top: 10px; left: 1px"></div>
diff --git a/third_party/WebKit/LayoutTests/paint/invalidation/subpixel-offset-scaled-transform-expected.txt b/third_party/WebKit/LayoutTests/paint/invalidation/subpixel-offset-scaled-transform-expected.txt
new file mode 100644
index 0000000..48fe0fb
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/paint/invalidation/subpixel-offset-scaled-transform-expected.txt
@@ -0,0 +1,29 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "LayoutBlockFlow (relative positioned) DIV class='child scale'",
+          "rect": [9, 58, 40, 40],
+          "reason": "bounds change"
+        },
+        {
+          "object": "LayoutBlockFlow (relative positioned) DIV class='child scale'",
+          "rect": [8, 58, 40, 40],
+          "reason": "bounds change"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutBlockFlow (relative positioned) DIV class='child scale'",
+      "reason": "bounds change"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/paint/invalidation/subpixel-offset-scaled-transform.html b/third_party/WebKit/LayoutTests/paint/invalidation/subpixel-offset-scaled-transform.html
new file mode 100644
index 0000000..21e8d6a
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/paint/invalidation/subpixel-offset-scaled-transform.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<style>
+.container { position: relative; width: 0; height: 0; }
+.scale { transform-origin: 0 0; transform: scale(40); }
+.child { width: 1px; height: 1px; background: blue; position: relative; left: 0.4px }
+</style>
+<div class="container scale">
+  <div class="child"></div>
+</div>
+<div class="container" style="top: 50px">
+  <div class="child scale"></div>
+</div>
+<script src="resources/text-based-repaint.js"></script>
+<script>
+function repaintTest() {
+  for (var container of document.getElementsByClassName('container'))
+    container.style.left = '0.3px';
+}
+onload = runRepaintAndPixelTest;
+</script>
diff --git a/third_party/WebKit/LayoutTests/paint/transforms/subpixel-scale-align-expected.html b/third_party/WebKit/LayoutTests/paint/transforms/subpixel-scale-align-expected.html
new file mode 100644
index 0000000..3f732ec0
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/paint/transforms/subpixel-scale-align-expected.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<style>
+div {
+  position: absolute;
+  width: 16px;
+  height: 16px;
+  background: green;
+}
+</style>
+<div style="top: 8px; left: 8px"></div>
+<div style="top: 28px; left: 8px"></div>
+<div style="top: 48px; left: 8px"></div>
+<div style="top: 68px; left: 8px"></div>
+<div style="top: 89px; left: 9px"></div>
+<div style="top: 109px; left: 9px"></div>
+<div style="top: 129px; left: 9px"></div>
+<div style="top: 149px; left: 9px"></div>
+<div style="top: 169px; left: 9px"></div>
+<div style="top: 189px; left: 9px"></div>
diff --git a/third_party/WebKit/LayoutTests/paint/transforms/subpixel-scale-align.html b/third_party/WebKit/LayoutTests/paint/transforms/subpixel-scale-align.html
new file mode 100644
index 0000000..21b30424
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/paint/transforms/subpixel-scale-align.html
@@ -0,0 +1,63 @@
+<!DOCTYPE html>
+<style>
+.container {
+  position: absolute;
+}
+.compare {
+  position: absolute;
+  top: 8px;
+  left: 8px;
+  width: 16px;
+  height: 16px;
+  background: red;
+}
+.target {
+  position: absolute;
+  transform: scale(0.5);
+  top: 0;
+  left: 0;
+  width: 32px;
+  height: 32px;
+  background: green;
+}
+</style>
+<div class="container" style="top: 0.1px; left: 0.1px">
+  <div class="compare"></div>
+  <div class="target"></div>
+</div>
+<div class="container" style="top: 20.2px; left: 0.2px">
+  <div class="compare"></div>
+  <div class="target"></div>
+</div>
+<div class="container" style="top: 40.3px; left: 0.3px">
+  <div class="compare"></div>
+  <div class="target"></div>
+</div>
+<div class="container" style="top: 60.4px; left: 0.4px">
+  <div class="compare"></div>
+  <div class="target"></div>
+</div>
+<div class="container" style="top: 80.5px; left: 0.5px">
+  <div class="compare"></div>
+  <div class="target"></div>
+</div>
+<div class="container" style="top: 100.6px; left: 0.6px">
+  <div class="compare"></div>
+  <div class="target"></div>
+</div>
+<div class="container" style="top: 120.7px; left: 0.7px">
+  <div class="compare"></div>
+  <div class="target"></div>
+</div>
+<div class="container" style="top: 140.8px; left: 0.8px">
+  <div class="compare"></div>
+  <div class="target"></div>
+</div>
+<div class="container" style="top: 160.9px; left: 0.9px">
+  <div class="compare"></div>
+  <div class="target"></div>
+</div>
+<div class="container" style="top: 181px; left: 1px">
+  <div class="compare"></div>
+  <div class="target"></div>
+</div>
diff --git a/third_party/WebKit/LayoutTests/platform/linux/editing/selection/transformed-selection-rects-expected.png b/third_party/WebKit/LayoutTests/platform/linux/editing/selection/transformed-selection-rects-expected.png
index ffa75241..6e090ae 100644
--- a/third_party/WebKit/LayoutTests/platform/linux/editing/selection/transformed-selection-rects-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/linux/editing/selection/transformed-selection-rects-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/virtual/disable-spinvalidation/paint/invalidation/reflection-repaint-test-expected.txt b/third_party/WebKit/LayoutTests/platform/linux/virtual/disable-spinvalidation/paint/invalidation/reflection-repaint-test-expected.txt
index 93ee8bef..19181a7 100644
--- a/third_party/WebKit/LayoutTests/platform/linux/virtual/disable-spinvalidation/paint/invalidation/reflection-repaint-test-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/linux/virtual/disable-spinvalidation/paint/invalidation/reflection-repaint-test-expected.txt
@@ -13,7 +13,7 @@
         },
         {
           "object": "LayoutText #text",
-          "rect": [23, 51, 71, 109],
+          "rect": [23, 51, 72, 110],
           "reason": "full"
         }
       ]
diff --git a/third_party/WebKit/LayoutTests/platform/linux/virtual/disable-spinvalidation/paint/invalidation/svg/animated-path-inside-transformed-html-expected.txt b/third_party/WebKit/LayoutTests/platform/linux/virtual/disable-spinvalidation/paint/invalidation/svg/animated-path-inside-transformed-html-expected.txt
index 6ca436d..f25ac6e 100644
--- a/third_party/WebKit/LayoutTests/platform/linux/virtual/disable-spinvalidation/paint/invalidation/svg/animated-path-inside-transformed-html-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/linux/virtual/disable-spinvalidation/paint/invalidation/svg/animated-path-inside-transformed-html-expected.txt
@@ -29,12 +29,12 @@
         },
         {
           "object": "LayoutSVGRect rect id='rect'",
-          "rect": [354, 127, 105, 103],
+          "rect": [354, 127, 105, 102],
           "reason": "full"
         },
         {
           "object": "LayoutSVGRect rect id='rect'",
-          "rect": [110, 84, 103, 102],
+          "rect": [111, 84, 101, 102],
           "reason": "full"
         },
         {
diff --git a/third_party/WebKit/LayoutTests/platform/linux/virtual/disable-spinvalidation/paint/invalidation/transform-inline-layered-child-expected.txt b/third_party/WebKit/LayoutTests/platform/linux/virtual/disable-spinvalidation/paint/invalidation/transform-inline-layered-child-expected.txt
index 304916f1..95f325b 100644
--- a/third_party/WebKit/LayoutTests/platform/linux/virtual/disable-spinvalidation/paint/invalidation/transform-inline-layered-child-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/linux/virtual/disable-spinvalidation/paint/invalidation/transform-inline-layered-child-expected.txt
@@ -35,11 +35,6 @@
           "object": "LayoutText #text",
           "rect": [300, 302, 80, 177],
           "reason": "subtree"
-        },
-        {
-          "object": "LayoutText #text",
-          "rect": [151, 87, 1, 1],
-          "reason": "subtree"
         }
       ]
     }
diff --git a/third_party/WebKit/LayoutTests/platform/linux/virtual/disable-spinvalidation/paint/invalidation/transform-layout-repaint-expected.txt b/third_party/WebKit/LayoutTests/platform/linux/virtual/disable-spinvalidation/paint/invalidation/transform-layout-repaint-expected.txt
index 1565faa6..48ff40a 100644
--- a/third_party/WebKit/LayoutTests/platform/linux/virtual/disable-spinvalidation/paint/invalidation/transform-layout-repaint-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/linux/virtual/disable-spinvalidation/paint/invalidation/transform-layout-repaint-expected.txt
@@ -13,7 +13,7 @@
         },
         {
           "object": "LayoutText #text",
-          "rect": [52, 51, 42, 31],
+          "rect": [52, 51, 43, 32],
           "reason": "full"
         }
       ]
diff --git a/third_party/WebKit/LayoutTests/platform/mac/editing/selection/transformed-selection-rects-expected.png b/third_party/WebKit/LayoutTests/platform/mac/editing/selection/transformed-selection-rects-expected.png
index 15339ede..8a736e7 100644
--- a/third_party/WebKit/LayoutTests/platform/mac/editing/selection/transformed-selection-rects-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/mac/editing/selection/transformed-selection-rects-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/transforms/transform-on-inline-expected.png b/third_party/WebKit/LayoutTests/platform/mac/transforms/transform-on-inline-expected.png
index 7d9365b..52e37ed 100644
--- a/third_party/WebKit/LayoutTests/platform/mac/transforms/transform-on-inline-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/mac/transforms/transform-on-inline-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/virtual/disable-spinvalidation/paint/invalidation/svg/animated-path-inside-transformed-html-expected.txt b/third_party/WebKit/LayoutTests/platform/mac/virtual/disable-spinvalidation/paint/invalidation/svg/animated-path-inside-transformed-html-expected.txt
index 689c7ec8..f4df5d1 100644
--- a/third_party/WebKit/LayoutTests/platform/mac/virtual/disable-spinvalidation/paint/invalidation/svg/animated-path-inside-transformed-html-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/mac/virtual/disable-spinvalidation/paint/invalidation/svg/animated-path-inside-transformed-html-expected.txt
@@ -29,12 +29,12 @@
         },
         {
           "object": "LayoutSVGRect rect id='rect'",
-          "rect": [354, 125, 105, 103],
+          "rect": [354, 125, 105, 102],
           "reason": "full"
         },
         {
           "object": "LayoutSVGRect rect id='rect'",
-          "rect": [110, 82, 103, 102],
+          "rect": [111, 82, 101, 102],
           "reason": "full"
         },
         {
diff --git a/third_party/WebKit/LayoutTests/platform/mac/virtual/disable-spinvalidation/paint/invalidation/transform-inline-layered-child-expected.txt b/third_party/WebKit/LayoutTests/platform/mac/virtual/disable-spinvalidation/paint/invalidation/transform-inline-layered-child-expected.txt
index f17c98c..ddd4957 100644
--- a/third_party/WebKit/LayoutTests/platform/mac/virtual/disable-spinvalidation/paint/invalidation/transform-inline-layered-child-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/mac/virtual/disable-spinvalidation/paint/invalidation/transform-inline-layered-child-expected.txt
@@ -35,11 +35,6 @@
           "object": "LayoutInline (relative positioned) SPAN id='child'",
           "rect": [300, 300, 80, 162],
           "reason": "subtree"
-        },
-        {
-          "object": "LayoutText #text",
-          "rect": [147, 85, 1, 1],
-          "reason": "subtree"
         }
       ]
     }
diff --git a/third_party/WebKit/LayoutTests/platform/win/virtual/disable-spinvalidation/paint/invalidation/reflection-repaint-test-expected.txt b/third_party/WebKit/LayoutTests/platform/win/virtual/disable-spinvalidation/paint/invalidation/reflection-repaint-test-expected.txt
deleted file mode 100644
index 72790fc..0000000
--- a/third_party/WebKit/LayoutTests/platform/win/virtual/disable-spinvalidation/paint/invalidation/reflection-repaint-test-expected.txt
+++ /dev/null
@@ -1,41 +0,0 @@
-{
-  "layers": [
-    {
-      "name": "LayoutView #document",
-      "bounds": [800, 600],
-      "contentsOpaque": true,
-      "drawsContent": true,
-      "paintInvalidations": [
-        {
-          "object": "LayoutBlockFlow DIV id='target'",
-          "rect": [22, 50, 226, 167],
-          "reason": "forced by layout"
-        },
-        {
-          "object": "LayoutText #text",
-          "rect": [23, 51, 70, 109],
-          "reason": "full"
-        }
-      ]
-    }
-  ],
-  "objectPaintInvalidations": [
-    {
-      "object": "LayoutBlockFlow DIV id='target'",
-      "reason": "forced by layout"
-    },
-    {
-      "object": "RootInlineBox",
-      "reason": "forced by layout"
-    },
-    {
-      "object": "LayoutText #text",
-      "reason": "full"
-    },
-    {
-      "object": "InlineTextBox 'PASS'",
-      "reason": "full"
-    }
-  ]
-}
-
diff --git a/third_party/WebKit/LayoutTests/platform/win/virtual/disable-spinvalidation/paint/invalidation/svg/animated-path-inside-transformed-html-expected.txt b/third_party/WebKit/LayoutTests/platform/win/virtual/disable-spinvalidation/paint/invalidation/svg/animated-path-inside-transformed-html-expected.txt
index 0e9a774..29c47d1 100644
--- a/third_party/WebKit/LayoutTests/platform/win/virtual/disable-spinvalidation/paint/invalidation/svg/animated-path-inside-transformed-html-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/win/virtual/disable-spinvalidation/paint/invalidation/svg/animated-path-inside-transformed-html-expected.txt
@@ -29,12 +29,12 @@
         },
         {
           "object": "LayoutSVGRect rect id='rect'",
-          "rect": [354, 127, 105, 103],
+          "rect": [354, 127, 105, 102],
           "reason": "full"
         },
         {
           "object": "LayoutSVGRect rect id='rect'",
-          "rect": [110, 84, 103, 102],
+          "rect": [111, 84, 101, 102],
           "reason": "full"
         },
         {
diff --git a/third_party/WebKit/LayoutTests/platform/win/virtual/disable-spinvalidation/paint/invalidation/transform-inline-layered-child-expected.txt b/third_party/WebKit/LayoutTests/platform/win/virtual/disable-spinvalidation/paint/invalidation/transform-inline-layered-child-expected.txt
index d055788..9bb7e0e 100644
--- a/third_party/WebKit/LayoutTests/platform/win/virtual/disable-spinvalidation/paint/invalidation/transform-inline-layered-child-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/win/virtual/disable-spinvalidation/paint/invalidation/transform-inline-layered-child-expected.txt
@@ -35,11 +35,6 @@
           "object": "LayoutText #text",
           "rect": [300, 301, 80, 178],
           "reason": "subtree"
-        },
-        {
-          "object": "LayoutText #text",
-          "rect": [151, 87, 1, 1],
-          "reason": "subtree"
         }
       ]
     }
diff --git a/third_party/WebKit/LayoutTests/platform/win/virtual/disable-spinvalidation/paint/invalidation/transform-layout-repaint-expected.txt b/third_party/WebKit/LayoutTests/platform/win/virtual/disable-spinvalidation/paint/invalidation/transform-layout-repaint-expected.txt
index 513d320..1565faa6 100644
--- a/third_party/WebKit/LayoutTests/platform/win/virtual/disable-spinvalidation/paint/invalidation/transform-layout-repaint-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/win/virtual/disable-spinvalidation/paint/invalidation/transform-layout-repaint-expected.txt
@@ -13,7 +13,7 @@
         },
         {
           "object": "LayoutText #text",
-          "rect": [52, 51, 41, 31],
+          "rect": [52, 51, 42, 31],
           "reason": "full"
         }
       ]
diff --git a/third_party/WebKit/LayoutTests/virtual/disable-spinvalidation/paint/invalidation/invalidation-with-scale-transform-expected.txt b/third_party/WebKit/LayoutTests/virtual/disable-spinvalidation/paint/invalidation/invalidation-with-scale-transform-expected.txt
deleted file mode 100644
index 6d795a45..0000000
--- a/third_party/WebKit/LayoutTests/virtual/disable-spinvalidation/paint/invalidation/invalidation-with-scale-transform-expected.txt
+++ /dev/null
@@ -1,29 +0,0 @@
-{
-  "layers": [
-    {
-      "name": "LayoutView #document",
-      "bounds": [800, 600],
-      "contentsOpaque": true,
-      "drawsContent": true,
-      "paintInvalidations": [
-        {
-          "object": "LayoutBlockFlow (positioned) DIV id='target'",
-          "rect": [85, 70, 91, 91],
-          "reason": "bounds change"
-        },
-        {
-          "object": "LayoutBlockFlow (positioned) DIV id='target'",
-          "rect": [84, 70, 91, 91],
-          "reason": "bounds change"
-        }
-      ]
-    }
-  ],
-  "objectPaintInvalidations": [
-    {
-      "object": "LayoutBlockFlow (positioned) DIV id='target'",
-      "reason": "bounds change"
-    }
-  ]
-}
-
diff --git a/third_party/WebKit/LayoutTests/virtual/disable-spinvalidation/paint/invalidation/subpixel-offset-scaled-transform-expected.txt b/third_party/WebKit/LayoutTests/virtual/disable-spinvalidation/paint/invalidation/subpixel-offset-scaled-transform-expected.txt
new file mode 100644
index 0000000..9330bf2
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/virtual/disable-spinvalidation/paint/invalidation/subpixel-offset-scaled-transform-expected.txt
@@ -0,0 +1,43 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "LayoutBlockFlow (relative positioned) DIV class='child'",
+          "rect": [8, 8, 81, 40],
+          "reason": "bounds change"
+        },
+        {
+          "object": "LayoutBlockFlow (relative positioned) DIV class='child'",
+          "rect": [8, 8, 80, 40],
+          "reason": "bounds change"
+        },
+        {
+          "object": "LayoutBlockFlow (relative positioned) DIV class='child scale'",
+          "rect": [8, 58, 41, 40],
+          "reason": "bounds change"
+        },
+        {
+          "object": "LayoutBlockFlow (relative positioned) DIV class='child scale'",
+          "rect": [8, 58, 41, 40],
+          "reason": "bounds change"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutBlockFlow (relative positioned) DIV class='child'",
+      "reason": "bounds change"
+    },
+    {
+      "object": "LayoutBlockFlow (relative positioned) DIV class='child scale'",
+      "reason": "bounds change"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/Source/bindings/modules/v8/generated.gni b/third_party/WebKit/Source/bindings/modules/v8/generated.gni
index edb5d48..67873331 100644
--- a/third_party/WebKit/Source/bindings/modules/v8/generated.gni
+++ b/third_party/WebKit/Source/bindings/modules/v8/generated.gni
@@ -32,10 +32,6 @@
   "$bindings_modules_v8_output_dir/ClientOrServiceWorkerOrMessagePort.h",
   "$bindings_modules_v8_output_dir/CSSImageValueOrHTMLImageElementOrSVGImageElementOrHTMLVideoElementOrHTMLCanvasElementOrImageBitmapOrOffscreenCanvas.cpp",
   "$bindings_modules_v8_output_dir/CSSImageValueOrHTMLImageElementOrSVGImageElementOrHTMLVideoElementOrHTMLCanvasElementOrImageBitmapOrOffscreenCanvas.h",
-  "$bindings_modules_v8_output_dir/DecodeErrorCallback.cpp",
-  "$bindings_modules_v8_output_dir/DecodeErrorCallback.h",
-  "$bindings_modules_v8_output_dir/DecodeSuccessCallback.cpp",
-  "$bindings_modules_v8_output_dir/DecodeSuccessCallback.h",
   "$bindings_modules_v8_output_dir/DictionaryOrString.cpp",
   "$bindings_modules_v8_output_dir/DictionaryOrString.h",
   "$bindings_modules_v8_output_dir/DoubleOrConstrainDoubleRange.cpp",
diff --git a/third_party/WebKit/Source/core/layout/LayoutBox.cpp b/third_party/WebKit/Source/core/layout/LayoutBox.cpp
index dbf533c..b444f08 100644
--- a/third_party/WebKit/Source/core/layout/LayoutBox.cpp
+++ b/third_party/WebKit/Source/core/layout/LayoutBox.cpp
@@ -1177,11 +1177,11 @@
     const LayoutObject* ancestor,
     VisualRectFlags visual_rect_flags,
     TransformState& transform_state) const {
-  bool preserve3D = container_object->Style()->Preserves3D();
+  bool container_preserve_3d = container_object->Style()->Preserves3D();
 
   TransformState::TransformAccumulation accumulation =
-      preserve3D ? TransformState::kAccumulateTransform
-                 : TransformState::kFlattenTransform;
+      container_preserve_3d ? TransformState::kAccumulateTransform
+                            : TransformState::kFlattenTransform;
 
   // If there is no transform on this box, adjust for container offset and
   // container scrolling, then apply container clip.
@@ -1195,32 +1195,45 @@
     return true;
   }
 
-  // Otherwise, apply the following:
-  // 1. Transform.
-  // 2. Container offset.
-  // 3. Container scroll offset.
-  // 4. Perspective applied by container.
-  // 5. Transform flattening.
-  // 6. Expansion for pixel snapping.
-  // 7. Container clip.
+  // Otherwise, do the following:
+  // 1. Expand for pixel snapping.
+  // 2. Generate transformation matrix combining, in this order
+  //    a) transform,
+  //    b) container offset,
+  //    c) container scroll offset,
+  //    d) perspective applied by container.
+  // 3. Apply transform Transform+flattening.
+  // 4. Apply container clip.
 
-  // 1. Transform.
+  // 1. Expand for pixel snapping.
+  // Use EnclosingBoundingBox because we cannot properly compute pixel
+  // snapping for painted elements within the transform since we don't know
+  // the desired subpixel accumulation at this point, and the transform may
+  // include a scale. This only makes sense for non-preserve3D.
+  if (!StyleRef().Preserves3D()) {
+    transform_state.Flatten();
+    transform_state.SetQuad(
+        FloatQuad(transform_state.LastPlanarQuad().EnclosingBoundingBox()));
+  }
+
+  // 2. Generate transformation matrix.
+  // a) Transform.
   TransformationMatrix transform;
   if (Layer() && Layer()->Transform())
     transform.Multiply(Layer()->CurrentTransform());
 
-  // 2. Container offset.
+  // b) Container offset.
   transform.PostTranslate(container_offset.X().ToFloat(),
                           container_offset.Y().ToFloat());
 
-  // 3. Container scroll offset.
+  // c) Container scroll offset.
   if (container_object->IsBox() && container_object != ancestor &&
       container_object->HasOverflowClip()) {
     IntSize offset = -ToLayoutBox(container_object)->ScrolledContentOffset();
     transform.PostTranslate(offset.Width(), offset.Height());
   }
 
-  // 4. Perspective applied by container.
+  // d) Perspective applied by container.
   if (container_object && container_object->HasLayer() &&
       container_object->Style()->HasPerspective()) {
     // Perspective on the container affects us, so we have to factor it in here.
@@ -1237,21 +1250,12 @@
     transform = perspective_matrix * transform;
   }
 
-  // 5. Transform flattening.
+  // 3. Apply transform and flatten.
   transform_state.ApplyTransform(transform, accumulation);
-
-  // 6. Expansion for pixel snapping.
-  // Use enclosingBoundingBox because we cannot properly compute pixel
-  // snapping for painted elements within the transform since we don't know
-  // the desired subpixel accumulation at this point, and the transform may
-  // include a scale.
-  if (!preserve3D) {
+  if (!container_preserve_3d)
     transform_state.Flatten();
-    transform_state.SetQuad(
-        FloatQuad(transform_state.LastPlanarQuad().EnclosingBoundingBox()));
-  }
 
-  // 7. Container clip.
+  // 4. Apply container clip.
   if (container_object->IsBox() && container_object != ancestor &&
       container_object->HasClipRelatedProperty()) {
     return ToLayoutBox(container_object)
diff --git a/third_party/WebKit/Source/core/paint/ObjectPaintProperties.h b/third_party/WebKit/Source/core/paint/ObjectPaintProperties.h
index c63e8c6..3fe3c0b 100644
--- a/third_party/WebKit/Source/core/paint/ObjectPaintProperties.h
+++ b/third_party/WebKit/Source/core/paint/ObjectPaintProperties.h
@@ -8,6 +8,7 @@
 #include <memory>
 #include "core/CoreExport.h"
 #include "platform/geometry/LayoutPoint.h"
+#include "platform/graphics/CompositorElementId.h"
 #include "platform/graphics/paint/ClipPaintPropertyNode.h"
 #include "platform/graphics/paint/EffectPaintPropertyNode.h"
 #include "platform/graphics/paint/PaintChunkProperties.h"
@@ -274,6 +275,13 @@
   }
 #endif
 
+  CompositorElementId& GetCompositorElementId() {
+    return compositor_element_id_;
+  }
+  void SetCompositorElementId(const CompositorElementId& id) {
+    compositor_element_id_ = id;
+  }
+
  private:
   ObjectPaintProperties() {}
 
@@ -319,6 +327,7 @@
   RefPtr<TransformPaintPropertyNode> svg_local_to_border_box_transform_;
   RefPtr<TransformPaintPropertyNode> scroll_translation_;
   RefPtr<TransformPaintPropertyNode> scrollbar_paint_offset_;
+  CompositorElementId compositor_element_id_;
 };
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/core/paint/PaintLayer.cpp b/third_party/WebKit/Source/core/paint/PaintLayer.cpp
index de41d0c..ee53d12b8 100644
--- a/third_party/WebKit/Source/core/paint/PaintLayer.cpp
+++ b/third_party/WebKit/Source/core/paint/PaintLayer.cpp
@@ -429,8 +429,10 @@
 
   UpdateTransformationMatrix();
 
-  if (had3d_transform != Has3DTransform())
+  if (had3d_transform != Has3DTransform()) {
+    SetNeedsCompositingInputsUpdateInternal();
     MarkAncestorChainForDescendantDependentFlagsUpdate();
+  }
 
   if (FrameView* frame_view = GetLayoutObject().GetDocument().View())
     frame_view->SetNeedsUpdateGeometries();
diff --git a/third_party/WebKit/Source/core/paint/PaintLayerPainter.cpp b/third_party/WebKit/Source/core/paint/PaintLayerPainter.cpp
index 8504021..93a7f66 100644
--- a/third_party/WebKit/Source/core/paint/PaintLayerPainter.cpp
+++ b/third_party/WebKit/Source/core/paint/PaintLayerPainter.cpp
@@ -833,12 +833,18 @@
   LayoutPoint delta;
   paint_layer_.ConvertToLayerCoords(painting_info.root_layer, delta);
   delta.MoveBy(fragment_translation);
+  delta += painting_info.sub_pixel_accumulation;
+  IntPoint rounded_delta = RoundedIntPoint(delta);
+
   TransformationMatrix transform(
       paint_layer_.RenderableTransform(painting_info.GetGlobalPaintFlags()));
-  IntPoint rounded_delta = RoundedIntPoint(delta);
   transform.PostTranslate(rounded_delta.X(), rounded_delta.Y());
-  LayoutSize adjusted_sub_pixel_accumulation =
-      painting_info.sub_pixel_accumulation + (delta - rounded_delta);
+
+  LayoutSize new_sub_pixel_accumulation;
+  if (transform.IsIdentityOrTranslation())
+    new_sub_pixel_accumulation += delta - rounded_delta;
+  // Otherwise discard the sub-pixel remainder because paint offset can't be
+  // transformed by a non-translation transform.
 
   // TODO(jbroman): Put the real transform origin here, instead of using a
   // matrix with the origin baked in.
@@ -852,7 +858,7 @@
       &paint_layer_,
       LayoutRect(EnclosingIntRect(
           transform.Inverse().MapRect(painting_info.paint_dirty_rect))),
-      painting_info.GetGlobalPaintFlags(), adjusted_sub_pixel_accumulation);
+      painting_info.GetGlobalPaintFlags(), new_sub_pixel_accumulation);
   transformed_painting_info.ancestor_has_clip_path_clipping =
       painting_info.ancestor_has_clip_path_clipping;
 
diff --git a/third_party/WebKit/Source/core/paint/PaintPropertyTreeBuilder.cpp b/third_party/WebKit/Source/core/paint/PaintPropertyTreeBuilder.cpp
index 5fabc8b..206782a 100644
--- a/third_party/WebKit/Source/core/paint/PaintPropertyTreeBuilder.cpp
+++ b/third_party/WebKit/Source/core/paint/PaintPropertyTreeBuilder.cpp
@@ -269,6 +269,17 @@
         RoundedIntPoint(context.current.paint_offset);
     LayoutPoint fractional_paint_offset =
         LayoutPoint(context.current.paint_offset - rounded_paint_offset);
+    if (fractional_paint_offset != LayoutPoint()) {
+      // If the object has a non-translation transform, discard the fractional
+      // paint offset which can't be transformed by the transform.
+      TransformationMatrix matrix;
+      object.StyleRef().ApplyTransform(
+          matrix, LayoutSize(), ComputedStyle::kExcludeTransformOrigin,
+          ComputedStyle::kIncludeMotionPath,
+          ComputedStyle::kIncludeIndependentTransformProperties);
+      if (!matrix.IsIdentityOrTranslation())
+        fractional_paint_offset = LayoutPoint();
+    }
 
     force_subtree_update |= properties.UpdatePaintOffsetTranslation(
         context.current.transform,
@@ -283,8 +294,8 @@
         object.IsLayoutView()) {
       context.absolute_position.transform = properties.PaintOffsetTranslation();
       context.fixed_position.transform = properties.PaintOffsetTranslation();
-      context.absolute_position.paint_offset = LayoutPoint();
-      context.fixed_position.paint_offset = LayoutPoint();
+      context.absolute_position.paint_offset = fractional_paint_offset;
+      context.fixed_position.paint_offset = fractional_paint_offset;
     }
   } else {
     if (auto* properties = object.GetMutableForPainting().PaintProperties())
@@ -413,16 +424,12 @@
       if (style.Preserves3D() && !rendering_context_id)
         rendering_context_id = PtrHash<const LayoutObject>::GetHash(&object);
 
-      CompositorElementId compositor_element_id =
-          style.HasCurrentTransformAnimation()
-              ? CreateDomNodeBasedCompositorElementId(object)
-              : CompositorElementId();
-
       auto& properties = *object.GetMutableForPainting().PaintProperties();
       force_subtree_update |= properties.UpdateTransform(
           context.current.transform, matrix, TransformOrigin(box),
           context.current.should_flatten_inherited_transform,
-          rendering_context_id, compositing_reasons, compositor_element_id);
+          rendering_context_id, compositing_reasons,
+          properties.GetCompositorElementId());
     } else {
       if (auto* properties = object.GetMutableForPainting().PaintProperties())
         force_subtree_update |= properties->ClearTransform();
@@ -583,18 +590,13 @@
                                                 style.BlendMode())
               : SkBlendMode::kSrcOver;
 
-      CompositorElementId compositor_element_id =
-          style.HasCurrentOpacityAnimation()
-              ? CreateDomNodeBasedCompositorElementId(object)
-              : CompositorElementId();
-
       DCHECK(!style.HasCurrentOpacityAnimation() ||
              compositing_reasons != kCompositingReasonNone);
 
       force_subtree_update |= properties.UpdateEffect(
           context.current_effect, context.current.transform, output_clip,
           kColorFilterNone, CompositorFilterOperations(), style.Opacity(),
-          blend_mode, compositing_reasons, compositor_element_id);
+          blend_mode, compositing_reasons, properties.GetCompositorElementId());
       if (has_mask) {
         // TODO(crbug.com/683425): PaintArtifactCompositor does not handle
         // grouping (i.e. descendant-dependent compositing reason) properly
@@ -675,10 +677,6 @@
       // We may begin to composite our subtree prior to an animation starts,
       // but a compositor element ID is only needed when an animation is
       // current.
-      CompositorElementId compositor_element_id =
-          style.HasCurrentFilterAnimation()
-              ? CreateDomNodeBasedCompositorElementId(object)
-              : CompositorElementId();
       CompositingReasons compositing_reasons =
           CompositingReasonFinder::RequiresCompositingForFilterAnimation(style)
               ? kCompositingReasonActiveAnimation
@@ -690,7 +688,7 @@
       force_subtree_update |= properties.UpdateFilter(
           context.current_effect, context.current.transform, output_clip,
           kColorFilterNone, std::move(filter), 1.f, SkBlendMode::kSrcOver,
-          compositing_reasons, compositor_element_id);
+          compositing_reasons, properties.GetCompositorElementId());
     } else {
       if (auto* properties = object.GetMutableForPainting().PaintProperties())
         force_subtree_update |= properties->ClearFilter();
@@ -975,17 +973,15 @@
           force_subtree_update = true;
       }
 
-      CompositorElementId compositor_element_id =
-          CreateDomNodeBasedCompositorElementId(object);
       TransformationMatrix matrix = TransformationMatrix().Translate(
           -scroll_offset.Width(), -scroll_offset.Height());
       force_subtree_update |= properties.UpdateScrollTranslation(
           context.current.transform, matrix, FloatPoint3D(),
           context.current.should_flatten_inherited_transform,
           context.current.rendering_context_id, kCompositingReasonNone,
-          compositor_element_id, context.current.scroll, scroll_clip,
-          scroll_bounds, user_scrollable_horizontal, user_scrollable_vertical,
-          reasons, scrollable_area);
+          properties.GetCompositorElementId(), context.current.scroll,
+          scroll_clip, scroll_bounds, user_scrollable_horizontal,
+          user_scrollable_vertical, reasons, scrollable_area);
     } else {
       // Ensure pre-existing properties are cleared.
       if (auto* properties = object.GetMutableForPainting().PaintProperties())
@@ -1196,7 +1192,13 @@
   bool had_paint_properties = object.PaintProperties();
 
   if (needs_paint_properties) {
-    object.GetMutableForPainting().EnsurePaintProperties();
+    ObjectPaintProperties& paint_properties =
+        object.GetMutableForPainting().EnsurePaintProperties();
+    if (!had_paint_properties &&
+        RuntimeEnabledFeatures::slimmingPaintV2Enabled()) {
+      paint_properties.SetCompositorElementId(
+          CreateDomNodeBasedCompositorElementId(object));
+    }
   } else {
     object.GetMutableForPainting().ClearPaintProperties();
     if (had_paint_properties)
diff --git a/third_party/WebKit/Source/core/paint/PaintPropertyTreeBuilderTest.cpp b/third_party/WebKit/Source/core/paint/PaintPropertyTreeBuilderTest.cpp
index d7c246a..874829b 100644
--- a/third_party/WebKit/Source/core/paint/PaintPropertyTreeBuilderTest.cpp
+++ b/third_party/WebKit/Source/core/paint/PaintPropertyTreeBuilderTest.cpp
@@ -80,45 +80,45 @@
   Settings::SetMockScrollbarsEnabled(false);
 }
 
-#define CHECK_VISUAL_RECT(expected, sourceObject, ancestorObject, slopFactor) \
-  do {                                                                        \
-    if ((sourceObject)->HasLayer() && (ancestorObject)->HasLayer()) {         \
-      LayoutRect source((sourceObject)->LocalVisualRect());                   \
-      source.MoveBy((sourceObject)->PaintOffset());                           \
-      auto contents_properties = (ancestorObject)->ContentsProperties();      \
-      FloatClipRect actual_float_rect((FloatRect(source)));                   \
-      GeometryMapper::SourceToDestinationVisualRect(                          \
-          *(sourceObject)->LocalBorderBoxProperties(), contents_properties,   \
-          actual_float_rect);                                                 \
-      LayoutRect actual(actual_float_rect.Rect());                            \
-      actual.MoveBy(-(ancestorObject)->PaintOffset());                        \
-      SCOPED_TRACE("GeometryMapper: ");                                       \
-      EXPECT_EQ(expected, actual);                                            \
-    }                                                                         \
-                                                                              \
-    if (slopFactor == LayoutUnit::Max())                                      \
-      break;                                                                  \
-    LayoutRect slow_path_rect = (sourceObject)->LocalVisualRect();            \
-    (sourceObject)                                                            \
-        ->MapToVisualRectInAncestorSpace(ancestorObject, slow_path_rect);     \
-    if (slopFactor) {                                                         \
-      LayoutRect inflated_expected = LayoutRect(expected);                    \
-      inflated_expected.Inflate(slopFactor);                                  \
-      SCOPED_TRACE(String::Format(                                            \
-          "Old path rect: %s, Expected: %s, Inflated expected: %s",           \
-          slow_path_rect.ToString().Ascii().data(),                           \
-          expected.ToString().Ascii().data(),                                 \
-          inflated_expected.ToString().Ascii().data()));                      \
-      EXPECT_TRUE(slow_path_rect.Contains(LayoutRect(expected)));             \
-      EXPECT_TRUE(inflated_expected.Contains(slow_path_rect));                \
-    } else {                                                                  \
-      SCOPED_TRACE("Slow path: ");                                            \
-      EXPECT_EQ(expected, slow_path_rect);                                    \
-    }                                                                         \
+#define CHECK_VISUAL_RECT(expected, source_object, ancestor, slop_factor)      \
+  do {                                                                         \
+    if ((source_object)->HasLayer() && (ancestor)->HasLayer()) {               \
+      LayoutRect source((source_object)->LocalVisualRect());                   \
+      source.MoveBy((source_object)->PaintOffset());                           \
+      auto contents_properties = (ancestor)->ContentsProperties();             \
+      FloatClipRect actual_float_rect((FloatRect(source)));                    \
+      GeometryMapper::SourceToDestinationVisualRect(                           \
+          *(source_object)->LocalBorderBoxProperties(), contents_properties,   \
+          actual_float_rect);                                                  \
+      LayoutRect actual(actual_float_rect.Rect());                             \
+      actual.MoveBy(-(ancestor)->PaintOffset());                               \
+      SCOPED_TRACE("GeometryMapper: ");                                        \
+      EXPECT_EQ(expected, actual);                                             \
+    }                                                                          \
+                                                                               \
+    if (slop_factor == LayoutUnit::Max())                                      \
+      break;                                                                   \
+    LayoutRect slow_path_rect = (source_object)->LocalVisualRect();            \
+    (source_object)->MapToVisualRectInAncestorSpace(ancestor, slow_path_rect); \
+    if (slop_factor) {                                                         \
+      LayoutRect inflated_expected = LayoutRect(expected);                     \
+      inflated_expected.Inflate(slop_factor);                                  \
+      SCOPED_TRACE(String::Format(                                             \
+          "Slow path rect: %s, Expected: %s, Inflated expected: %s",           \
+          slow_path_rect.ToString().Ascii().data(),                            \
+          expected.ToString().Ascii().data(),                                  \
+          inflated_expected.ToString().Ascii().data()));                       \
+      EXPECT_TRUE(LayoutRect(EnclosingIntRect(slow_path_rect))                 \
+                      .Contains(LayoutRect(expected)));                        \
+      EXPECT_TRUE(inflated_expected.Contains(slow_path_rect));                 \
+    } else {                                                                   \
+      SCOPED_TRACE("Slow path: ");                                             \
+      EXPECT_EQ(expected, slow_path_rect);                                     \
+    }                                                                          \
   } while (0)
 
-#define CHECK_EXACT_VISUAL_RECT(expected, sourceObject, ancestorObject) \
-  CHECK_VISUAL_RECT(expected, sourceObject, ancestorObject, 0)
+#define CHECK_EXACT_VISUAL_RECT(expected, source_object, ancestor) \
+  CHECK_VISUAL_RECT(expected, source_object, ancestor, 0)
 
 INSTANTIATE_TEST_CASE_P(All, PaintPropertyTreeBuilderTest, ::testing::Bool());
 
@@ -1768,6 +1768,67 @@
 }
 
 TEST_P(PaintPropertyTreeBuilderTest,
+       NonTranslationTransformShouldResetSubpixelPaintOffset) {
+  SetBodyInnerHTML(
+      "<style>"
+      "  * { margin: 0; }"
+      "  div { position: relative; }"
+      "  #a {"
+      "    width: 70px;"
+      "    height: 70px;"
+      "    left: 0.9px;"
+      "    top: 0.9px;"
+      "  }"
+      "  #b {"
+      "    width: 40px;"
+      "    height: 40px;"
+      "    transform: scale(10);"
+      "    transform-origin: 0 0;"
+      "  }"
+      "  #c {"
+      "    width: 40px;"
+      "    height: 40px;"
+      "    left: 0.6px;"
+      "    top: 0.6px;"
+      "  }"
+      "</style>"
+      "<div id='a'>"
+      "  <div id='b'>"
+      "    <div id='c'></div>"
+      "  </div>"
+      "</div>");
+  FrameView* frame_view = GetDocument().View();
+
+  LayoutObject* b = GetDocument().getElementById("b")->GetLayoutObject();
+  const ObjectPaintProperties* b_properties = b->PaintProperties();
+  EXPECT_EQ(TransformationMatrix().Scale(10),
+            b_properties->Transform()->Matrix());
+  // The paint offset transform should not be snapped.
+  EXPECT_EQ(TransformationMatrix().Translate(1, 1),
+            b_properties->Transform()->Parent()->Matrix());
+  EXPECT_EQ(LayoutPoint(), b->PaintOffset());
+  // Visual rects via the non-paint properties system use enclosingIntRect
+  // before applying transforms, because they are computed bottom-up and
+  // therefore can't apply pixel snapping. Therefore apply a slop of 1px.
+  CHECK_VISUAL_RECT(LayoutRect(LayoutUnit(1), LayoutUnit(1), LayoutUnit(400),
+                               LayoutUnit(400)),
+                    b, frame_view->GetLayoutView(), 1);
+
+  // c's painting should start at c_offset.
+  LayoutObject* c = GetDocument().getElementById("c")->GetLayoutObject();
+  LayoutUnit c_offset = LayoutUnit(0.6);
+  EXPECT_EQ(LayoutPoint(c_offset, c_offset), c->PaintOffset());
+  // Visual rects via the non-paint properties system use enclosingIntRect
+  // before applying transforms, because they are computed bottom-up and
+  // therefore can't apply pixel snapping. Therefore apply a slop of 1px
+  // in the transformed space (c_offset * 10 in view space) and 1px in the
+  // view space.
+  CHECK_VISUAL_RECT(LayoutRect(c_offset * 10 + 1, c_offset * 10 + 1,
+                               LayoutUnit(400), LayoutUnit(400)),
+                    c, frame_view->GetLayoutView(), c_offset * 10 + 1);
+}
+
+TEST_P(PaintPropertyTreeBuilderTest,
        PaintOffsetWithPixelSnappingThroughMultipleTransforms) {
   SetBodyInnerHTML(
       "<style>"
@@ -3162,20 +3223,20 @@
 }
 
 TEST_P(PaintPropertyTreeBuilderTest,
-       TransformNodeNotAnimatedHasNoCompositorElementId) {
+       TransformNodeNotAnimatedStillHasCompositorElementId) {
   SetBodyInnerHTML("<div id='target' style='transform: translateX(2em)'></div");
   const ObjectPaintProperties* properties = PaintPropertiesForElement("target");
   EXPECT_TRUE(properties->Transform());
-  EXPECT_EQ(CompositorElementId(),
+  EXPECT_NE(CompositorElementId(),
             properties->Transform()->GetCompositorElementId());
 }
 
 TEST_P(PaintPropertyTreeBuilderTest,
-       EffectNodeNotAnimatedHasNoCompositorElementId) {
+       EffectNodeNotAnimatedStillHasCompositorElementId) {
   SetBodyInnerHTML("<div id='target' style='opacity: 0.5'></div");
   const ObjectPaintProperties* properties = PaintPropertiesForElement("target");
   EXPECT_TRUE(properties->Effect());
-  EXPECT_EQ(CompositorElementId(),
+  EXPECT_NE(CompositorElementId(),
             properties->Effect()->GetCompositorElementId());
 }
 
diff --git a/third_party/WebKit/Source/core/paint/PaintPropertyTreeUpdateTests.cpp b/third_party/WebKit/Source/core/paint/PaintPropertyTreeUpdateTests.cpp
index ec994cc..4269939 100644
--- a/third_party/WebKit/Source/core/paint/PaintPropertyTreeUpdateTests.cpp
+++ b/third_party/WebKit/Source/core/paint/PaintPropertyTreeUpdateTests.cpp
@@ -571,7 +571,7 @@
 }
 
 TEST_P(PaintPropertyTreeUpdateTest,
-       TransformNodeLosesCompositorElementIdWhenAnimationRemoved) {
+       TransformNodeDoesNotLoseCompositorElementIdWhenAnimationRemoved) {
   LoadTestData("transform-animation.html");
 
   Element* target = GetDocument().getElementById("target");
@@ -586,12 +586,12 @@
   // Remove the animation but keep the transform on the element.
   target->removeAttribute(HTMLNames::classAttr);
   GetDocument().View()->UpdateAllLifecyclePhases();
-  EXPECT_EQ(CompositorElementId(),
+  EXPECT_NE(CompositorElementId(),
             properties->Transform()->GetCompositorElementId());
 }
 
 TEST_P(PaintPropertyTreeUpdateTest,
-       EffectNodeLosesCompositorElementIdWhenAnimationRemoved) {
+       EffectNodeDoesNotLoseCompositorElementIdWhenAnimationRemoved) {
   LoadTestData("opacity-animation.html");
 
   Element* target = GetDocument().getElementById("target");
@@ -605,7 +605,7 @@
 
   target->removeAttribute(HTMLNames::classAttr);
   GetDocument().View()->UpdateAllLifecyclePhases();
-  EXPECT_EQ(CompositorElementId(),
+  EXPECT_NE(CompositorElementId(),
             properties->Effect()->GetCompositorElementId());
 }
 
diff --git a/third_party/WebKit/Source/modules/imagecapture/ImageCapture.cpp b/third_party/WebKit/Source/modules/imagecapture/ImageCapture.cpp
index ba70929c..26ece51 100644
--- a/third_party/WebKit/Source/modules/imagecapture/ImageCapture.cpp
+++ b/third_party/WebKit/Source/modules/imagecapture/ImageCapture.cpp
@@ -136,14 +136,15 @@
   // scriptState->getExecutionContext()->getSecurityOrigin()->toString()
   service_->GetCapabilities(
       stream_track_->Component()->Source()->Id(),
-      ConvertToBaseCallback(WTF::Bind(&ImageCapture::OnPhotoCapabilities,
-                                      WrapPersistent(this),
-                                      WrapPersistent(resolver))));
+      ConvertToBaseCallback(WTF::Bind(
+          &ImageCapture::OnMojoPhotoCapabilities, WrapPersistent(this),
+          WrapPersistent(resolver), false /* trigger_take_photo */)));
   return promise;
 }
 
 ScriptPromise ImageCapture::setOptions(ScriptState* script_state,
-                                       const PhotoSettings& photo_settings) {
+                                       const PhotoSettings& photo_settings,
+                                       bool trigger_take_photo /* = false */) {
   ScriptPromiseResolver* resolver = ScriptPromiseResolver::Create(script_state);
   ScriptPromise promise = resolver->Promise();
 
@@ -178,11 +179,11 @@
         ParseFillLightMode(photo_settings.fillLightMode());
   }
 
-  service_->SetOptions(stream_track_->Component()->Source()->Id(),
-                       std::move(settings),
-                       ConvertToBaseCallback(WTF::Bind(
-                           &ImageCapture::OnSetOptions, WrapPersistent(this),
-                           WrapPersistent(resolver))));
+  service_->SetOptions(
+      stream_track_->Component()->Source()->Id(), std::move(settings),
+      ConvertToBaseCallback(
+          WTF::Bind(&ImageCapture::OnMojoSetOptions, WrapPersistent(this),
+                    WrapPersistent(resolver), trigger_take_photo)));
   return promise;
 }
 
@@ -208,11 +209,17 @@
   // scriptState->getExecutionContext()->getSecurityOrigin()->toString()
   service_->TakePhoto(stream_track_->Component()->Source()->Id(),
                       ConvertToBaseCallback(WTF::Bind(
-                          &ImageCapture::OnTakePhoto, WrapPersistent(this),
+                          &ImageCapture::OnMojoTakePhoto, WrapPersistent(this),
                           WrapPersistent(resolver))));
   return promise;
 }
 
+ScriptPromise ImageCapture::takePhoto(ScriptState* script_state,
+                                      const PhotoSettings& photo_settings) {
+  return setOptions(script_state, photo_settings,
+                    true /* trigger_take_photo */);
+}
+
 ScriptPromise ImageCapture::grabFrame(ScriptState* script_state) {
   ScriptPromiseResolver* resolver = ScriptPromiseResolver::Create(script_state);
   ScriptPromise promise = resolver->Promise();
@@ -381,11 +388,11 @@
     settings->torch = constraints.torch().getAsBoolean();
   }
 
-  service_->SetOptions(stream_track_->Component()->Source()->Id(),
-                       std::move(settings),
-                       ConvertToBaseCallback(WTF::Bind(
-                           &ImageCapture::OnSetOptions, WrapPersistent(this),
-                           WrapPersistent(resolver))));
+  service_->SetOptions(
+      stream_track_->Component()->Source()->Id(), std::move(settings),
+      ConvertToBaseCallback(
+          WTF::Bind(&ImageCapture::OnMojoSetOptions, WrapPersistent(this),
+                    WrapPersistent(resolver), false /* trigger_take_photo */)));
 }
 
 const MediaTrackConstraintSet& ImageCapture::GetMediaTrackConstraints() const {
@@ -476,45 +483,57 @@
   // to avoid blocking the main UI thread.
   service_->GetCapabilities(
       stream_track_->Component()->Source()->Id(),
-      ConvertToBaseCallback(WTF::Bind(&ImageCapture::OnCapabilitiesUpdate,
-                                      WrapPersistent(this))));
+      ConvertToBaseCallback(WTF::Bind(
+          &ImageCapture::UpdateMediaTrackCapabilities, WrapPersistent(this))));
 }
 
-void ImageCapture::OnPhotoCapabilities(
+void ImageCapture::OnMojoPhotoCapabilities(
     ScriptPromiseResolver* resolver,
+    bool trigger_take_photo,
     media::mojom::blink::PhotoCapabilitiesPtr capabilities) {
   if (!service_requests_.Contains(resolver))
     return;
+
   if (capabilities.is_null()) {
     resolver->Reject(DOMException::Create(kUnknownError, "platform error"));
-  } else {
-    // Update the local capabilities cache.
-    OnCapabilitiesUpdateInternal(*capabilities);
-
-    PhotoCapabilities* caps = PhotoCapabilities::Create();
-
-    caps->SetRedEyeReduction(capabilities->red_eye_reduction);
-    // TODO(mcasas): Remove the explicit MediaSettingsRange::create() when
-    // mojo::StructTraits supports garbage-collected mappings,
-    // https://crbug.com/700180.
-    if (capabilities->height->min != 0 || capabilities->height->max != 0) {
-      caps->SetImageHeight(
-          MediaSettingsRange::Create(std::move(capabilities->height)));
-    }
-    if (capabilities->width->min != 0 || capabilities->width->max != 0) {
-      caps->SetImageWidth(
-          MediaSettingsRange::Create(std::move(capabilities->width)));
-    }
-
-    if (!capabilities->fill_light_mode.IsEmpty())
-      caps->SetFillLightMode(capabilities->fill_light_mode);
-
-    resolver->Resolve(caps);
+    service_requests_.erase(resolver);
+    return;
   }
+
+  PhotoCapabilities* caps = PhotoCapabilities::Create();
+  caps->SetRedEyeReduction(capabilities->red_eye_reduction);
+  // TODO(mcasas): Remove the explicit MediaSettingsRange::create() when
+  // mojo::StructTraits supports garbage-collected mappings,
+  // https://crbug.com/700180.
+  if (capabilities->height->min != 0 || capabilities->height->max != 0) {
+    caps->SetImageHeight(
+        MediaSettingsRange::Create(std::move(capabilities->height)));
+  }
+  if (capabilities->width->min != 0 || capabilities->width->max != 0) {
+    caps->SetImageWidth(
+        MediaSettingsRange::Create(std::move(capabilities->width)));
+  }
+  if (!capabilities->fill_light_mode.IsEmpty())
+    caps->SetFillLightMode(capabilities->fill_light_mode);
+
+  // Update the local track capabilities cache.
+  UpdateMediaTrackCapabilities(std::move(capabilities));
+
+  if (trigger_take_photo) {
+    service_->TakePhoto(stream_track_->Component()->Source()->Id(),
+                        ConvertToBaseCallback(WTF::Bind(
+                            &ImageCapture::OnMojoTakePhoto,
+                            WrapPersistent(this), WrapPersistent(resolver))));
+    return;
+  }
+
+  resolver->Resolve(caps);
   service_requests_.erase(resolver);
 }
 
-void ImageCapture::OnSetOptions(ScriptPromiseResolver* resolver, bool result) {
+void ImageCapture::OnMojoSetOptions(ScriptPromiseResolver* resolver,
+                                    bool trigger_take_photo,
+                                    bool result) {
   if (!service_requests_.Contains(resolver))
     return;
 
@@ -527,67 +546,65 @@
   // Retrieve the current device status after setting the options.
   service_->GetCapabilities(
       stream_track_->Component()->Source()->Id(),
-      ConvertToBaseCallback(WTF::Bind(&ImageCapture::OnPhotoCapabilities,
-                                      WrapPersistent(this),
-                                      WrapPersistent(resolver))));
+      ConvertToBaseCallback(WTF::Bind(
+          &ImageCapture::OnMojoPhotoCapabilities, WrapPersistent(this),
+          WrapPersistent(resolver), trigger_take_photo)));
 }
 
-void ImageCapture::OnTakePhoto(ScriptPromiseResolver* resolver,
-                               media::mojom::blink::BlobPtr blob) {
+void ImageCapture::OnMojoTakePhoto(ScriptPromiseResolver* resolver,
+                                   media::mojom::blink::BlobPtr blob) {
   if (!service_requests_.Contains(resolver))
     return;
 
   // TODO(mcasas): Should be using a mojo::StructTraits.
-  if (blob->data.IsEmpty())
+  if (blob->data.IsEmpty()) {
     resolver->Reject(DOMException::Create(kUnknownError, "platform error"));
-  else
+  } else {
     resolver->Resolve(
         Blob::Create(blob->data.data(), blob->data.size(), blob->mime_type));
+  }
   service_requests_.erase(resolver);
 }
 
-void ImageCapture::OnCapabilitiesUpdate(
+void ImageCapture::UpdateMediaTrackCapabilities(
     media::mojom::blink::PhotoCapabilitiesPtr capabilities) {
-  if (!capabilities.is_null())
-    OnCapabilitiesUpdateInternal(*capabilities);
-}
+  if (!capabilities)
+    return;
 
-void ImageCapture::OnCapabilitiesUpdateInternal(
-    const media::mojom::blink::PhotoCapabilities& capabilities) {
   WTF::Vector<WTF::String> supported_white_balance_modes;
   supported_white_balance_modes.ReserveInitialCapacity(
-      capabilities.supported_white_balance_modes.size());
-  for (const auto& supported_mode : capabilities.supported_white_balance_modes)
+      capabilities->supported_white_balance_modes.size());
+  for (const auto& supported_mode : capabilities->supported_white_balance_modes)
     supported_white_balance_modes.push_back(ToString(supported_mode));
   if (!supported_white_balance_modes.IsEmpty()) {
     capabilities_.setWhiteBalanceMode(std::move(supported_white_balance_modes));
     settings_.setWhiteBalanceMode(
-        ToString(capabilities.current_white_balance_mode));
+        ToString(capabilities->current_white_balance_mode));
   }
 
   WTF::Vector<WTF::String> supported_exposure_modes;
   supported_exposure_modes.ReserveInitialCapacity(
-      capabilities.supported_exposure_modes.size());
-  for (const auto& supported_mode : capabilities.supported_exposure_modes)
+      capabilities->supported_exposure_modes.size());
+  for (const auto& supported_mode : capabilities->supported_exposure_modes)
     supported_exposure_modes.push_back(ToString(supported_mode));
   if (!supported_exposure_modes.IsEmpty()) {
     capabilities_.setExposureMode(std::move(supported_exposure_modes));
-    settings_.setExposureMode(ToString(capabilities.current_exposure_mode));
+    settings_.setExposureMode(ToString(capabilities->current_exposure_mode));
   }
 
   WTF::Vector<WTF::String> supported_focus_modes;
   supported_focus_modes.ReserveInitialCapacity(
-      capabilities.supported_focus_modes.size());
-  for (const auto& supported_mode : capabilities.supported_focus_modes)
+      capabilities->supported_focus_modes.size());
+  for (const auto& supported_mode : capabilities->supported_focus_modes)
     supported_focus_modes.push_back(ToString(supported_mode));
   if (!supported_focus_modes.IsEmpty()) {
     capabilities_.setFocusMode(std::move(supported_focus_modes));
-    settings_.setFocusMode(ToString(capabilities.current_focus_mode));
+    settings_.setFocusMode(ToString(capabilities->current_focus_mode));
   }
 
   HeapVector<Point2D> current_points_of_interest;
-  if (!capabilities.points_of_interest.IsEmpty()) {
-    for (const auto& point : capabilities.points_of_interest) {
+  if (!capabilities->points_of_interest.IsEmpty()) {
+    for (const auto& point : capabilities->points_of_interest) {
       Point2D web_point;
       web_point.setX(point->x);
       web_point.setY(point->y);
@@ -599,54 +616,54 @@
   // TODO(mcasas): Remove the explicit MediaSettingsRange::create() when
   // mojo::StructTraits supports garbage-collected mappings,
   // https://crbug.com/700180.
-  if (capabilities.exposure_compensation->max !=
-      capabilities.exposure_compensation->min) {
+  if (capabilities->exposure_compensation->max !=
+      capabilities->exposure_compensation->min) {
     capabilities_.setExposureCompensation(
-        MediaSettingsRange::Create(*capabilities.exposure_compensation));
+        MediaSettingsRange::Create(*capabilities->exposure_compensation));
     settings_.setExposureCompensation(
-        capabilities.exposure_compensation->current);
+        capabilities->exposure_compensation->current);
   }
-  if (capabilities.color_temperature->max !=
-      capabilities.color_temperature->min) {
+  if (capabilities->color_temperature->max !=
+      capabilities->color_temperature->min) {
     capabilities_.setColorTemperature(
-        MediaSettingsRange::Create(*capabilities.color_temperature));
-    settings_.setColorTemperature(capabilities.color_temperature->current);
+        MediaSettingsRange::Create(*capabilities->color_temperature));
+    settings_.setColorTemperature(capabilities->color_temperature->current);
   }
-  if (capabilities.iso->max != capabilities.iso->min) {
-    capabilities_.setIso(MediaSettingsRange::Create(*capabilities.iso));
-    settings_.setIso(capabilities.iso->current);
+  if (capabilities->iso->max != capabilities->iso->min) {
+    capabilities_.setIso(MediaSettingsRange::Create(*capabilities->iso));
+    settings_.setIso(capabilities->iso->current);
   }
 
-  if (capabilities.brightness->max != capabilities.brightness->min) {
+  if (capabilities->brightness->max != capabilities->brightness->min) {
     capabilities_.setBrightness(
-        MediaSettingsRange::Create(*capabilities.brightness));
-    settings_.setBrightness(capabilities.brightness->current);
+        MediaSettingsRange::Create(*capabilities->brightness));
+    settings_.setBrightness(capabilities->brightness->current);
   }
-  if (capabilities.contrast->max != capabilities.contrast->min) {
+  if (capabilities->contrast->max != capabilities->contrast->min) {
     capabilities_.setContrast(
-        MediaSettingsRange::Create(*capabilities.contrast));
-    settings_.setContrast(capabilities.contrast->current);
+        MediaSettingsRange::Create(*capabilities->contrast));
+    settings_.setContrast(capabilities->contrast->current);
   }
-  if (capabilities.saturation->max != capabilities.saturation->min) {
+  if (capabilities->saturation->max != capabilities->saturation->min) {
     capabilities_.setSaturation(
-        MediaSettingsRange::Create(*capabilities.saturation));
-    settings_.setSaturation(capabilities.saturation->current);
+        MediaSettingsRange::Create(*capabilities->saturation));
+    settings_.setSaturation(capabilities->saturation->current);
   }
-  if (capabilities.sharpness->max != capabilities.sharpness->min) {
+  if (capabilities->sharpness->max != capabilities->sharpness->min) {
     capabilities_.setSharpness(
-        MediaSettingsRange::Create(*capabilities.sharpness));
-    settings_.setSharpness(capabilities.sharpness->current);
+        MediaSettingsRange::Create(*capabilities->sharpness));
+    settings_.setSharpness(capabilities->sharpness->current);
   }
 
-  if (capabilities.zoom->max != capabilities.zoom->min) {
-    capabilities_.setZoom(MediaSettingsRange::Create(*capabilities.zoom));
-    settings_.setZoom(capabilities.zoom->current);
+  if (capabilities->zoom->max != capabilities->zoom->min) {
+    capabilities_.setZoom(MediaSettingsRange::Create(*capabilities->zoom));
+    settings_.setZoom(capabilities->zoom->current);
   }
 
-  if (capabilities.supports_torch)
-    capabilities_.setTorch(capabilities.supports_torch);
-  if (capabilities.supports_torch)
-    settings_.setTorch(capabilities.torch);
+  if (capabilities->supports_torch)
+    capabilities_.setTorch(capabilities->supports_torch);
+  if (capabilities->supports_torch)
+    settings_.setTorch(capabilities->torch);
 }
 
 void ImageCapture::OnServiceConnectionError() {
diff --git a/third_party/WebKit/Source/modules/imagecapture/ImageCapture.h b/third_party/WebKit/Source/modules/imagecapture/ImageCapture.h
index ff815ee..2bc4dd4 100644
--- a/third_party/WebKit/Source/modules/imagecapture/ImageCapture.h
+++ b/third_party/WebKit/Source/modules/imagecapture/ImageCapture.h
@@ -56,9 +56,12 @@
 
   ScriptPromise getPhotoCapabilities(ScriptState*);
 
-  ScriptPromise setOptions(ScriptState*, const PhotoSettings&);
+  ScriptPromise setOptions(ScriptState*,
+                           const PhotoSettings&,
+                           bool trigger_take_photo = false);
 
   ScriptPromise takePhoto(ScriptState*);
+  ScriptPromise takePhoto(ScriptState*, const PhotoSettings&);
 
   ScriptPromise grabFrame(ScriptState*);
 
@@ -77,14 +80,15 @@
  private:
   ImageCapture(ExecutionContext*, MediaStreamTrack*);
 
-  void OnPhotoCapabilities(ScriptPromiseResolver*,
-                           media::mojom::blink::PhotoCapabilitiesPtr);
-  void OnSetOptions(ScriptPromiseResolver*, bool);
-  void OnTakePhoto(ScriptPromiseResolver*, media::mojom::blink::BlobPtr);
-  void OnCapabilitiesUpdate(media::mojom::blink::PhotoCapabilitiesPtr);
+  void OnMojoPhotoCapabilities(ScriptPromiseResolver*,
+                               bool trigger_take_photo,
+                               media::mojom::blink::PhotoCapabilitiesPtr);
+  void OnMojoSetOptions(ScriptPromiseResolver*,
+                        bool trigger_take_photo,
+                        bool result);
+  void OnMojoTakePhoto(ScriptPromiseResolver*, media::mojom::blink::BlobPtr);
 
-  void OnCapabilitiesUpdateInternal(
-      const media::mojom::blink::PhotoCapabilities&);
+  void UpdateMediaTrackCapabilities(media::mojom::blink::PhotoCapabilitiesPtr);
   void OnServiceConnectionError();
 
   Member<MediaStreamTrack> stream_track_;
diff --git a/third_party/WebKit/Source/modules/imagecapture/ImageCapture.idl b/third_party/WebKit/Source/modules/imagecapture/ImageCapture.idl
index db1a39d..704d5528 100644
--- a/third_party/WebKit/Source/modules/imagecapture/ImageCapture.idl
+++ b/third_party/WebKit/Source/modules/imagecapture/ImageCapture.idl
@@ -17,6 +17,6 @@
 
     [CallWith=ScriptState] Promise<PhotoCapabilities> getPhotoCapabilities();
     [CallWith=ScriptState] Promise<void> setOptions(PhotoSettings photoSettings);
-    [CallWith=ScriptState] Promise<Blob> takePhoto();
+    [CallWith=ScriptState] Promise<Blob> takePhoto(optional PhotoSettings photoSettings);
     [CallWith=ScriptState] Promise<ImageBitmap> grabFrame();
 };
diff --git a/third_party/WebKit/Source/modules/modules_idl_files.gni b/third_party/WebKit/Source/modules/modules_idl_files.gni
index 25cabb6..9c864ba 100644
--- a/third_party/WebKit/Source/modules/modules_idl_files.gni
+++ b/third_party/WebKit/Source/modules/modules_idl_files.gni
@@ -281,6 +281,7 @@
                     "vr/VRStageParameters.idl",
                     "webaudio/AnalyserNode.idl",
                     "webaudio/AudioBuffer.idl",
+                    "webaudio/AudioBufferCallback.idl",
                     "webaudio/AudioBufferSourceNode.idl",
                     "webaudio/AudioContext.idl",
                     "webaudio/AudioDestinationNode.idl",
diff --git a/third_party/WebKit/Source/modules/webaudio/AsyncAudioDecoder.cpp b/third_party/WebKit/Source/modules/webaudio/AsyncAudioDecoder.cpp
index d2ba5b2..c43bcff 100644
--- a/third_party/WebKit/Source/modules/webaudio/AsyncAudioDecoder.cpp
+++ b/third_party/WebKit/Source/modules/webaudio/AsyncAudioDecoder.cpp
@@ -24,11 +24,9 @@
  */
 
 #include "modules/webaudio/AsyncAudioDecoder.h"
-
-#include "bindings/modules/v8/DecodeErrorCallback.h"
-#include "bindings/modules/v8/DecodeSuccessCallback.h"
 #include "core/dom/DOMArrayBuffer.h"
 #include "modules/webaudio/AudioBuffer.h"
+#include "modules/webaudio/AudioBufferCallback.h"
 #include "modules/webaudio/BaseAudioContext.h"
 #include "platform/CrossThreadFunctional.h"
 #include "platform/audio/AudioBus.h"
@@ -42,8 +40,8 @@
 
 void AsyncAudioDecoder::DecodeAsync(DOMArrayBuffer* audio_data,
                                     float sample_rate,
-                                    DecodeSuccessCallback* success_callback,
-                                    DecodeErrorCallback* error_callback,
+                                    AudioBufferCallback* success_callback,
+                                    AudioBufferCallback* error_callback,
                                     ScriptPromiseResolver* resolver,
                                     BaseAudioContext* context) {
   DCHECK(IsMainThread());
@@ -64,8 +62,8 @@
 void AsyncAudioDecoder::DecodeOnBackgroundThread(
     DOMArrayBuffer* audio_data,
     float sample_rate,
-    DecodeSuccessCallback* success_callback,
-    DecodeErrorCallback* error_callback,
+    AudioBufferCallback* success_callback,
+    AudioBufferCallback* error_callback,
     ScriptPromiseResolver* resolver,
     BaseAudioContext* context) {
   DCHECK(!IsMainThread());
@@ -91,8 +89,8 @@
 }
 
 void AsyncAudioDecoder::NotifyComplete(DOMArrayBuffer*,
-                                       DecodeSuccessCallback* success_callback,
-                                       DecodeErrorCallback* error_callback,
+                                       AudioBufferCallback* success_callback,
+                                       AudioBufferCallback* error_callback,
                                        AudioBus* audio_bus,
                                        ScriptPromiseResolver* resolver,
                                        BaseAudioContext* context) {
diff --git a/third_party/WebKit/Source/modules/webaudio/AsyncAudioDecoder.h b/third_party/WebKit/Source/modules/webaudio/AsyncAudioDecoder.h
index a5d739111..e5ba089 100644
--- a/third_party/WebKit/Source/modules/webaudio/AsyncAudioDecoder.h
+++ b/third_party/WebKit/Source/modules/webaudio/AsyncAudioDecoder.h
@@ -34,9 +34,8 @@
 
 class BaseAudioContext;
 class AudioBuffer;
+class AudioBufferCallback;
 class AudioBus;
-class DecodeErrorCallback;
-class DecodeSuccessCallback;
 class DOMArrayBuffer;
 class ScriptPromiseResolver;
 
@@ -59,8 +58,8 @@
   // appropriately when finished.
   void DecodeAsync(DOMArrayBuffer* audio_data,
                    float sample_rate,
-                   DecodeSuccessCallback*,
-                   DecodeErrorCallback*,
+                   AudioBufferCallback* success_callback,
+                   AudioBufferCallback* error_callback,
                    ScriptPromiseResolver*,
                    BaseAudioContext*);
 
@@ -68,13 +67,13 @@
   AudioBuffer* CreateAudioBufferFromAudioBus(AudioBus*);
   static void DecodeOnBackgroundThread(DOMArrayBuffer* audio_data,
                                        float sample_rate,
-                                       DecodeSuccessCallback*,
-                                       DecodeErrorCallback*,
+                                       AudioBufferCallback* success_callback,
+                                       AudioBufferCallback* error_callback,
                                        ScriptPromiseResolver*,
                                        BaseAudioContext*);
   static void NotifyComplete(DOMArrayBuffer* audio_data,
-                             DecodeSuccessCallback*,
-                             DecodeErrorCallback*,
+                             AudioBufferCallback* success_callback,
+                             AudioBufferCallback* error_callback,
                              AudioBus*,
                              ScriptPromiseResolver*,
                              BaseAudioContext*);
diff --git a/third_party/WebKit/Source/modules/webaudio/AudioBufferCallback.h b/third_party/WebKit/Source/modules/webaudio/AudioBufferCallback.h
new file mode 100644
index 0000000..e8ccc98
--- /dev/null
+++ b/third_party/WebKit/Source/modules/webaudio/AudioBufferCallback.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2011, Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1.  Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2.  Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ */
+
+#ifndef AudioBufferCallback_h
+#define AudioBufferCallback_h
+
+#include "platform/heap/Handle.h"
+#include "platform/wtf/build_config.h"
+
+namespace blink {
+
+class AudioBuffer;
+class DOMException;
+
+class AudioBufferCallback
+    : public GarbageCollectedFinalized<AudioBufferCallback> {
+ public:
+  virtual ~AudioBufferCallback() {}
+  DEFINE_INLINE_VIRTUAL_TRACE() {}
+  virtual void handleEvent(AudioBuffer*) = 0;
+  virtual void handleEvent(DOMException*) = 0;
+};
+
+}  // namespace blink
+
+#endif  // AudioBufferCallback_h
diff --git a/third_party/WebKit/Source/modules/webaudio/AudioBufferCallback.idl b/third_party/WebKit/Source/modules/webaudio/AudioBufferCallback.idl
new file mode 100644
index 0000000..83b627d
--- /dev/null
+++ b/third_party/WebKit/Source/modules/webaudio/AudioBufferCallback.idl
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2011, Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1.  Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2.  Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ */
+
+callback interface AudioBufferCallback {
+    void handleEvent(AudioBuffer audioBuffer);
+    void handleEvent(DOMException exception);
+};
diff --git a/third_party/WebKit/Source/modules/webaudio/AudioContext.cpp b/third_party/WebKit/Source/modules/webaudio/AudioContext.cpp
index f0a5e2f0..98dfc4dd 100644
--- a/third_party/WebKit/Source/modules/webaudio/AudioContext.cpp
+++ b/third_party/WebKit/Source/modules/webaudio/AudioContext.cpp
@@ -13,6 +13,7 @@
 #include "core/frame/UseCounter.h"
 #include "core/timing/DOMWindowPerformance.h"
 #include "core/timing/Performance.h"
+#include "modules/webaudio/AudioBufferCallback.h"
 #include "modules/webaudio/AudioContextOptions.h"
 #include "modules/webaudio/AudioTimestamp.h"
 #include "modules/webaudio/DefaultAudioDestinationNode.h"
diff --git a/third_party/WebKit/Source/modules/webaudio/BUILD.gn b/third_party/WebKit/Source/modules/webaudio/BUILD.gn
index 8cfcd64..b10d919 100644
--- a/third_party/WebKit/Source/modules/webaudio/BUILD.gn
+++ b/third_party/WebKit/Source/modules/webaudio/BUILD.gn
@@ -16,6 +16,7 @@
     "AudioBasicProcessorHandler.h",
     "AudioBuffer.cpp",
     "AudioBuffer.h",
+    "AudioBufferCallback.h",
     "AudioBufferSourceNode.cpp",
     "AudioBufferSourceNode.h",
     "AudioContext.cpp",
diff --git a/third_party/WebKit/Source/modules/webaudio/BaseAudioContext.cpp b/third_party/WebKit/Source/modules/webaudio/BaseAudioContext.cpp
index fc9ae96..7e9161d 100644
--- a/third_party/WebKit/Source/modules/webaudio/BaseAudioContext.cpp
+++ b/third_party/WebKit/Source/modules/webaudio/BaseAudioContext.cpp
@@ -29,8 +29,6 @@
 #include "bindings/core/v8/ExceptionMessages.h"
 #include "bindings/core/v8/ExceptionState.h"
 #include "bindings/core/v8/ScriptPromiseResolver.h"
-#include "bindings/modules/v8/DecodeErrorCallback.h"
-#include "bindings/modules/v8/DecodeSuccessCallback.h"
 #include "core/dom/DOMException.h"
 #include "core/dom/Document.h"
 #include "core/dom/ExceptionCode.h"
@@ -42,6 +40,7 @@
 #include "modules/mediastream/MediaStream.h"
 #include "modules/webaudio/AnalyserNode.h"
 #include "modules/webaudio/AudioBuffer.h"
+#include "modules/webaudio/AudioBufferCallback.h"
 #include "modules/webaudio/AudioBufferSourceNode.h"
 #include "modules/webaudio/AudioContext.h"
 #include "modules/webaudio/AudioListener.h"
@@ -273,25 +272,8 @@
 ScriptPromise BaseAudioContext::decodeAudioData(
     ScriptState* script_state,
     DOMArrayBuffer* audio_data,
-    ExceptionState& exception_state) {
-  return decodeAudioData(script_state, audio_data, nullptr, nullptr,
-                         exception_state);
-}
-
-ScriptPromise BaseAudioContext::decodeAudioData(
-    ScriptState* script_state,
-    DOMArrayBuffer* audio_data,
-    DecodeSuccessCallback* success_callback,
-    ExceptionState& exception_state) {
-  return decodeAudioData(script_state, audio_data, success_callback, nullptr,
-                         exception_state);
-}
-
-ScriptPromise BaseAudioContext::decodeAudioData(
-    ScriptState* script_state,
-    DOMArrayBuffer* audio_data,
-    DecodeSuccessCallback* success_callback,
-    DecodeErrorCallback* error_callback,
+    AudioBufferCallback* success_callback,
+    AudioBufferCallback* error_callback,
     ExceptionState& exception_state) {
   DCHECK(IsMainThread());
   DCHECK(audio_data);
@@ -321,7 +303,7 @@
         kDataCloneError, "Cannot decode detached ArrayBuffer");
     resolver->Reject(error);
     if (error_callback) {
-      error_callback->call(this, error);
+      error_callback->handleEvent(error);
     }
   }
 
@@ -331,22 +313,22 @@
 void BaseAudioContext::HandleDecodeAudioData(
     AudioBuffer* audio_buffer,
     ScriptPromiseResolver* resolver,
-    DecodeSuccessCallback* success_callback,
-    DecodeErrorCallback* error_callback) {
+    AudioBufferCallback* success_callback,
+    AudioBufferCallback* error_callback) {
   DCHECK(IsMainThread());
 
   if (audio_buffer) {
     // Resolve promise successfully and run the success callback
     resolver->Resolve(audio_buffer);
     if (success_callback)
-      success_callback->call(this, audio_buffer);
+      success_callback->handleEvent(audio_buffer);
   } else {
     // Reject the promise and run the error callback
     DOMException* error =
         DOMException::Create(kEncodingError, "Unable to decode audio data");
     resolver->Reject(error);
     if (error_callback)
-      error_callback->call(this, error);
+      error_callback->handleEvent(error);
   }
 
   // We've resolved the promise.  Remove it now.
diff --git a/third_party/WebKit/Source/modules/webaudio/BaseAudioContext.h b/third_party/WebKit/Source/modules/webaudio/BaseAudioContext.h
index 56c22776..492e496 100644
--- a/third_party/WebKit/Source/modules/webaudio/BaseAudioContext.h
+++ b/third_party/WebKit/Source/modules/webaudio/BaseAudioContext.h
@@ -51,6 +51,7 @@
 
 class AnalyserNode;
 class AudioBuffer;
+class AudioBufferCallback;
 class AudioBufferSourceNode;
 class AudioContextOptions;
 class AudioListener;
@@ -61,8 +62,6 @@
 class ConstantSourceNode;
 class ConvolverNode;
 class DelayNode;
-class DecodeErrorCallback;
-class DecodeSuccessCallback;
 class Document;
 class DynamicsCompressorNode;
 class ExceptionState;
@@ -170,25 +169,16 @@
   // Asynchronous audio file data decoding.
   ScriptPromise decodeAudioData(ScriptState*,
                                 DOMArrayBuffer* audio_data,
-                                DecodeSuccessCallback*,
-                                DecodeErrorCallback*,
-                                ExceptionState&);
-
-  ScriptPromise decodeAudioData(ScriptState*,
-                                DOMArrayBuffer* audio_data,
-                                ExceptionState&);
-
-  ScriptPromise decodeAudioData(ScriptState*,
-                                DOMArrayBuffer* audio_data,
-                                DecodeSuccessCallback*,
+                                AudioBufferCallback* success_callback,
+                                AudioBufferCallback* error_callback,
                                 ExceptionState&);
 
   // Handles the promise and callbacks when |decodeAudioData| is finished
   // decoding.
   void HandleDecodeAudioData(AudioBuffer*,
                              ScriptPromiseResolver*,
-                             DecodeSuccessCallback*,
-                             DecodeErrorCallback*);
+                             AudioBufferCallback* success_callback,
+                             AudioBufferCallback* error_callback);
 
   AudioListener* listener() { return listener_; }
 
diff --git a/third_party/WebKit/Source/modules/webaudio/BaseAudioContext.idl b/third_party/WebKit/Source/modules/webaudio/BaseAudioContext.idl
index f058169..d8d4d6d 100644
--- a/third_party/WebKit/Source/modules/webaudio/BaseAudioContext.idl
+++ b/third_party/WebKit/Source/modules/webaudio/BaseAudioContext.idl
@@ -3,16 +3,12 @@
 // found in the LICENSE file.
 
 // See https://webaudio.github.io/web-audio-api/#BaseAudioContext
-
 enum AudioContextState {
     "suspended",
     "running",
     "closed"
 };
 
-callback DecodeErrorCallback = void (DOMException error);
-callback DecodeSuccessCallback = void (AudioBuffer decodedData);
-
 [
     ActiveScriptWrappable,
     DependentLifetime,
@@ -35,7 +31,7 @@
     [RaisesException] AudioBuffer createBuffer(unsigned long numberOfChannels, unsigned long numberOfFrames, float sampleRate);
 
     // Asynchronous audio file data decoding.
-    [RaisesException, MeasureAs=AudioContextDecodeAudioData, CallWith=ScriptState] Promise<AudioBuffer> decodeAudioData(ArrayBuffer audioData, optional DecodeSuccessCallback successCallback, optional DecodeErrorCallback  errorCallback);
+    [RaisesException, MeasureAs=AudioContextDecodeAudioData, CallWith=ScriptState] Promise<AudioBuffer> decodeAudioData(ArrayBuffer audioData, optional AudioBufferCallback successCallback, optional AudioBufferCallback errorCallback);
 
     // Sources
     [RaisesException, MeasureAs=AudioContextCreateBufferSource] AudioBufferSourceNode createBufferSource();
diff --git a/third_party/WebKit/Source/platform/RuntimeEnabledFeatures.json5 b/third_party/WebKit/Source/platform/RuntimeEnabledFeatures.json5
index 5766ba6..bafe49f 100644
--- a/third_party/WebKit/Source/platform/RuntimeEnabledFeatures.json5
+++ b/third_party/WebKit/Source/platform/RuntimeEnabledFeatures.json5
@@ -67,8 +67,7 @@
     },
     {
       name: "AccessibilityObjectModel",
-      settable_from_internals: true,
-      status: "test",
+      status: "experimental",
     },
     {
       name: "AllowContentInitiatedDataUrlNavigations",
diff --git a/third_party/WebKit/Source/platform/graphics/paint/PropertyTreeState.cpp b/third_party/WebKit/Source/platform/graphics/paint/PropertyTreeState.cpp
index ce23989e..8d9a47d 100644
--- a/third_party/WebKit/Source/platform/graphics/paint/PropertyTreeState.cpp
+++ b/third_party/WebKit/Source/platform/graphics/paint/PropertyTreeState.cpp
@@ -40,30 +40,18 @@
 
 const CompositorElementId PropertyTreeState::GetCompositorElementId(
     const CompositorElementIdSet& element_ids) const {
-// The effect or transform nodes could have a compositor element id. The order
-// doesn't matter as the element id should be the same on all that have a
-// non-default CompositorElementId.
-//
-// Note that PropertyTreeState acts as a context that accumulates state as we
-// traverse the tree building layers. This means that we could see a compositor
-// element id 'A' for a parent layer in conjunction with a compositor element id
-// 'B' for a child layer. To preserve uniqueness of element ids, then, we check
-// for presence in the |element_ids| set (which represents element ids already
-// previously attached to a layer). This is an interim step while we pursue
-// broader rework of animation subsystem noted in http://crbug.com/709137.
-#if DCHECK_IS_ON()
-  CompositorElementId expected_element_id;
-  CompositorElementId effect_element_id = Effect()->GetCompositorElementId();
-  if (effect_element_id && !element_ids.Contains(effect_element_id)) {
-    expected_element_id = effect_element_id;
-  }
-  CompositorElementId transform_element_id =
-      Transform()->GetCompositorElementId();
-  if (expected_element_id && transform_element_id &&
-      !element_ids.Contains(transform_element_id)) {
-    DCHECK_EQ(expected_element_id, transform_element_id);
-  }
-#endif
+  // The effect or transform nodes could have a compositor element id. The order
+  // doesn't matter as the element id should be the same on all that have a
+  // non-default CompositorElementId.
+  //
+  // Note that PropertyTreeState acts as a context that accumulates state as we
+  // traverse the tree building layers. This means that we could see a
+  // compositor element id 'A' for a parent layer in conjunction with a
+  // compositor element id 'B' for a child layer. To preserve uniqueness of
+  // element ids, then, we check for presence in the |element_ids| set (which
+  // represents element ids already previously attached to a layer). This is an
+  // interim step while we pursue broader rework of animation subsystem noted in
+  // http://crbug.com/709137.
   if (Effect()->GetCompositorElementId() &&
       !element_ids.Contains(Effect()->GetCompositorElementId()))
     return Effect()->GetCompositorElementId();
diff --git a/third_party/WebKit/Source/platform/mojo/KURLSecurityOriginTest.cpp b/third_party/WebKit/Source/platform/mojo/KURLSecurityOriginTest.cpp
index 942c407..417f3234 100644
--- a/third_party/WebKit/Source/platform/mojo/KURLSecurityOriginTest.cpp
+++ b/third_party/WebKit/Source/platform/mojo/KURLSecurityOriginTest.cpp
@@ -78,12 +78,22 @@
       SecurityOrigin::Create("http", "www.google.com", 80);
   RefPtr<SecurityOrigin> output;
   EXPECT_TRUE(proxy->BounceOrigin(non_unique, &output));
+  EXPECT_TRUE(non_unique->IsSameSchemeHostPortAndSuborigin(output.Get()));
   EXPECT_TRUE(non_unique->IsSameSchemeHostPort(output.Get()));
-  EXPECT_FALSE(non_unique->IsUnique());
+  EXPECT_FALSE(output->HasSuborigin());
+  EXPECT_FALSE(output->IsUnique());
 
   RefPtr<SecurityOrigin> unique = SecurityOrigin::CreateUnique();
   EXPECT_TRUE(proxy->BounceOrigin(unique, &output));
   EXPECT_TRUE(output->IsUnique());
+
+  RefPtr<SecurityOrigin> with_sub_origin =
+      SecurityOrigin::Create("http", "www.google.com", 80, "suborigin");
+  EXPECT_TRUE(proxy->BounceOrigin(with_sub_origin, &output));
+  EXPECT_TRUE(with_sub_origin->IsSameSchemeHostPortAndSuborigin(output.Get()));
+  EXPECT_TRUE(with_sub_origin->IsSameSchemeHostPort(output.Get()));
+  EXPECT_TRUE(output->HasSuborigin());
+  EXPECT_FALSE(output->IsUnique());
 }
 
 }  // namespace url
diff --git a/third_party/WebKit/Source/platform/mojo/SecurityOriginStructTraits.h b/third_party/WebKit/Source/platform/mojo/SecurityOriginStructTraits.h
index e77529d..88551da 100644
--- a/third_party/WebKit/Source/platform/mojo/SecurityOriginStructTraits.h
+++ b/third_party/WebKit/Source/platform/mojo/SecurityOriginStructTraits.h
@@ -23,6 +23,10 @@
   static uint16_t port(const RefPtr<::blink::SecurityOrigin>& origin) {
     return origin->EffectivePort();
   }
+  static WTF::String suborigin(const RefPtr<::blink::SecurityOrigin>& origin) {
+    WTF::String suborigin = origin->GetSuborigin()->GetName();
+    return suborigin.IsNull() ? "" : suborigin;
+  }
   static bool unique(const RefPtr<::blink::SecurityOrigin>& origin) {
     return origin->IsUnique();
   }
@@ -33,10 +37,13 @@
     } else {
       WTF::String scheme;
       WTF::String host;
-      if (!data.ReadScheme(&scheme) || !data.ReadHost(&host))
+      WTF::String suborigin;
+      if (!data.ReadScheme(&scheme) || !data.ReadHost(&host) ||
+          !data.ReadSuborigin(&suborigin))
         return false;
 
-      *out = ::blink::SecurityOrigin::Create(scheme, host, data.port());
+      *out =
+          ::blink::SecurityOrigin::Create(scheme, host, data.port(), suborigin);
     }
 
     // If a unique origin was created, but the unique flag wasn't set, then
diff --git a/third_party/WebKit/Source/web/LocalFrameClientImpl.cpp b/third_party/WebKit/Source/web/LocalFrameClientImpl.cpp
index 365d784..35672e3 100644
--- a/third_party/WebKit/Source/web/LocalFrameClientImpl.cpp
+++ b/third_party/WebKit/Source/web/LocalFrameClientImpl.cpp
@@ -31,6 +31,8 @@
 
 #include "web/LocalFrameClientImpl.h"
 
+#include <memory>
+
 #include "bindings/core/v8/ScriptController.h"
 #include "core/HTMLNames.h"
 #include "core/dom/Document.h"
@@ -107,8 +109,6 @@
 #include "web/WebLocalFrameImpl.h"
 #include "web/WebPluginContainerImpl.h"
 
-#include <memory>
-
 namespace blink {
 
 namespace {
@@ -733,8 +733,7 @@
   params.attribute_values = param_values;
   params.load_manually = load_manually;
 
-  WebPlugin* web_plugin =
-      web_frame_->Client()->CreatePlugin(web_frame_, params);
+  WebPlugin* web_plugin = web_frame_->Client()->CreatePlugin(params);
   if (!web_plugin)
     return nullptr;
 
diff --git a/third_party/WebKit/Source/web/WebRuntimeFeatures.cpp b/third_party/WebKit/Source/web/WebRuntimeFeatures.cpp
index 0778e85d..19db183 100644
--- a/third_party/WebKit/Source/web/WebRuntimeFeatures.cpp
+++ b/third_party/WebKit/Source/web/WebRuntimeFeatures.cpp
@@ -68,10 +68,6 @@
   RuntimeEnabledFeatures::setAccelerated2dCanvasEnabled(enable);
 }
 
-void WebRuntimeFeatures::EnableAccessibilityObjectModel(bool enable) {
-  RuntimeEnabledFeatures::setAccessibilityObjectModelEnabled(enable);
-}
-
 void WebRuntimeFeatures::EnableAudioOutputDevices(bool enable) {
   RuntimeEnabledFeatures::setAudioOutputDevicesEnabled(enable);
 }
diff --git a/third_party/WebKit/Source/web/tests/FakeWebPlugin.cpp b/third_party/WebKit/Source/web/tests/FakeWebPlugin.cpp
index c72d4548..43b7d16a3 100644
--- a/third_party/WebKit/Source/web/tests/FakeWebPlugin.cpp
+++ b/third_party/WebKit/Source/web/tests/FakeWebPlugin.cpp
@@ -32,8 +32,7 @@
 
 namespace blink {
 
-FakeWebPlugin::FakeWebPlugin(WebFrame* frame, const WebPluginParams& params)
-    : frame_(frame) {}
+FakeWebPlugin::FakeWebPlugin(const WebPluginParams& params) {}
 
 FakeWebPlugin::~FakeWebPlugin() {}
 
@@ -43,8 +42,7 @@
 }
 
 void FakeWebPlugin::Destroy() {
-  container_ = 0;
-  frame_ = 0;
+  container_ = nullptr;
   delete this;
 }
 
diff --git a/third_party/WebKit/Source/web/tests/FakeWebPlugin.h b/third_party/WebKit/Source/web/tests/FakeWebPlugin.h
index 1a5eb12..0e040a3 100644
--- a/third_party/WebKit/Source/web/tests/FakeWebPlugin.h
+++ b/third_party/WebKit/Source/web/tests/FakeWebPlugin.h
@@ -36,7 +36,6 @@
 namespace blink {
 
 class WebDragData;
-class WebFrame;
 class WebInputEvent;
 class WebPluginContainer;
 class WebURLResponse;
@@ -44,7 +43,7 @@
 
 class FakeWebPlugin : public WebPlugin {
  public:
-  FakeWebPlugin(WebFrame*, const WebPluginParams&);
+  explicit FakeWebPlugin(const WebPluginParams&);
 
   // WebPlugin methods:
   bool Initialize(WebPluginContainer*) override;
@@ -82,7 +81,6 @@
   WebPluginContainer* Container() const { return container_; }
 
  private:
-  WebFrame* frame_;
   WebPluginContainer* container_;
 };
 
diff --git a/third_party/WebKit/Source/web/tests/WebHelperPluginTest.cpp b/third_party/WebKit/Source/web/tests/WebHelperPluginTest.cpp
index fc67ed2..40bf957 100644
--- a/third_party/WebKit/Source/web/tests/WebHelperPluginTest.cpp
+++ b/third_party/WebKit/Source/web/tests/WebHelperPluginTest.cpp
@@ -17,8 +17,8 @@
 
 class FakePlaceholderWebPlugin : public FakeWebPlugin {
  public:
-  FakePlaceholderWebPlugin(WebFrame* frame, const WebPluginParams& params)
-      : FakeWebPlugin(frame, params) {}
+  explicit FakePlaceholderWebPlugin(const WebPluginParams& params)
+      : FakeWebPlugin(params) {}
   ~FakePlaceholderWebPlugin() override {}
 
   bool IsPlaceholder() override { return true; }
@@ -29,10 +29,9 @@
   WebHelperPluginFrameClient() : create_placeholder_(false) {}
   ~WebHelperPluginFrameClient() override {}
 
-  WebPlugin* CreatePlugin(WebLocalFrame* frame,
-                          const WebPluginParams& params) override {
-    return create_placeholder_ ? new FakePlaceholderWebPlugin(frame, params)
-                               : new FakeWebPlugin(frame, params);
+  WebPlugin* CreatePlugin(const WebPluginParams& params) override {
+    return create_placeholder_ ? new FakePlaceholderWebPlugin(params)
+                               : new FakeWebPlugin(params);
   }
 
   void SetCreatePlaceholder(bool create_placeholder) {
diff --git a/third_party/WebKit/Source/web/tests/WebPluginContainerTest.cpp b/third_party/WebKit/Source/web/tests/WebPluginContainerTest.cpp
index aeddb19..a46cc935 100644
--- a/third_party/WebKit/Source/web/tests/WebPluginContainerTest.cpp
+++ b/third_party/WebKit/Source/web/tests/WebPluginContainerTest.cpp
@@ -57,7 +57,6 @@
 #include "public/platform/WebURLLoaderMockFactory.h"
 #include "public/web/WebDocument.h"
 #include "public/web/WebElement.h"
-#include "public/web/WebFrame.h"
 #include "public/web/WebFrameClient.h"
 #include "public/web/WebPluginParams.h"
 #include "public/web/WebPrintParams.h"
@@ -109,9 +108,8 @@
 template <typename T>
 class CustomPluginWebFrameClient : public FrameTestHelpers::TestWebFrameClient {
  public:
-  WebPlugin* CreatePlugin(WebLocalFrame* frame,
-                          const WebPluginParams& params) override {
-    return new T(frame, params);
+  WebPlugin* CreatePlugin(const WebPluginParams& params) override {
+    return new T(params);
   }
 };
 
@@ -121,12 +119,9 @@
 // as markup text.
 class TestPlugin : public FakeWebPlugin {
  public:
-  TestPlugin(WebFrame* frame,
-             const WebPluginParams& params,
+  TestPlugin(const WebPluginParams& params,
              TestPluginWebFrameClient* test_client)
-      : FakeWebPlugin(frame, params) {
-    test_client_ = test_client;
-  }
+      : FakeWebPlugin(params), test_client_(test_client) {}
 
   bool HasSelection() const override { return true; }
   WebString SelectionAsText() const override { return WebString("x"); }
@@ -136,21 +131,20 @@
   void PrintPage(int page_number, WebCanvas*) override;
 
  private:
-  TestPluginWebFrameClient* test_client_;
+  TestPluginWebFrameClient* const test_client_;
 };
 
 class TestPluginWebFrameClient : public FrameTestHelpers::TestWebFrameClient {
-  WebPlugin* CreatePlugin(WebLocalFrame* frame,
-                          const WebPluginParams& params) override {
+  WebPlugin* CreatePlugin(const WebPluginParams& params) override {
     if (params.mime_type == "application/x-webkit-test-webplugin" ||
         params.mime_type == "application/pdf")
-      return new TestPlugin(frame, params, this);
-    return WebFrameClient::CreatePlugin(frame, params);
+      return new TestPlugin(params, this);
+    return WebFrameClient::CreatePlugin(params);
   }
 
  public:
   void OnPrintPage() { printed_page_ = true; }
-  bool PrintedAtLeastOnePage() { return printed_page_; }
+  bool PrintedAtLeastOnePage() const { return printed_page_; }
 
  private:
   bool printed_page_ = false;
@@ -455,9 +449,8 @@
 // A class to facilitate testing that events are correctly received by plugins.
 class EventTestPlugin : public FakeWebPlugin {
  public:
-  EventTestPlugin(WebFrame* frame, const WebPluginParams& params)
-      : FakeWebPlugin(frame, params),
-        last_event_type_(WebInputEvent::kUndefined) {}
+  explicit EventTestPlugin(const WebPluginParams& params)
+      : FakeWebPlugin(params), last_event_type_(WebInputEvent::kUndefined) {}
 
   WebInputEventResult HandleInputEvent(const WebInputEvent& event,
                                        WebCursorInfo&) override {
@@ -941,21 +934,21 @@
   // Plugin that checks isRectTopmost in destroy().
   class TopmostPlugin : public FakeWebPlugin {
    public:
-    TopmostPlugin(WebFrame* frame, const WebPluginParams& params)
-        : FakeWebPlugin(frame, params) {}
+    explicit TopmostPlugin(const WebPluginParams& params)
+        : FakeWebPlugin(params) {}
 
     bool IsRectTopmost() { return Container()->IsRectTopmost(topmost_rect); }
 
     void Destroy() override {
-      // In destroy, isRectTopmost is no longer valid.
+      // In destroy, IsRectTopmost is no longer valid.
       EXPECT_FALSE(Container()->IsRectTopmost(topmost_rect));
       FakeWebPlugin::Destroy();
     }
   };
 
   RegisterMockedURL("plugin_container.html");
-  CustomPluginWebFrameClient<TopmostPlugin>
-      plugin_web_frame_client;  // Must outlive webViewHelper.
+  // The client must outlive WebViewHelper.
+  CustomPluginWebFrameClient<TopmostPlugin> plugin_web_frame_client;
   FrameTestHelpers::WebViewHelper web_view_helper;
   WebView* web_view = web_view_helper.InitializeAndLoad(
       base_url_ + "plugin_container.html", true, &plugin_web_frame_client);
@@ -986,8 +979,8 @@
 
 class CompositedPlugin : public FakeWebPlugin {
  public:
-  CompositedPlugin(WebLocalFrame* frame, const WebPluginParams& params)
-      : FakeWebPlugin(frame, params),
+  explicit CompositedPlugin(const WebPluginParams& params)
+      : FakeWebPlugin(params),
         layer_(Platform::Current()->CompositorSupport()->CreateLayer()) {}
 
   WebLayer* GetWebLayer() const { return layer_.get(); }
diff --git a/third_party/WebKit/public/web/WebFrameClient.h b/third_party/WebKit/public/web/WebFrameClient.h
index 55411c5..fffda44 100644
--- a/third_party/WebKit/public/web/WebFrameClient.h
+++ b/third_party/WebKit/public/web/WebFrameClient.h
@@ -117,9 +117,7 @@
   // Factory methods -----------------------------------------------------
 
   // May return null.
-  virtual WebPlugin* CreatePlugin(WebLocalFrame*, const WebPluginParams&) {
-    return 0;
-  }
+  virtual WebPlugin* CreatePlugin(const WebPluginParams&) { return nullptr; }
 
   // May return null.
   // WebContentDecryptionModule* may be null if one has not yet been set.
@@ -128,11 +126,11 @@
                                             WebMediaPlayerEncryptedMediaClient*,
                                             WebContentDecryptionModule*,
                                             const WebString& sink_id) {
-    return 0;
+    return nullptr;
   }
 
   // May return null.
-  virtual WebMediaSession* CreateMediaSession() { return 0; }
+  virtual WebMediaSession* CreateMediaSession() { return nullptr; }
 
   // May return null.
   virtual std::unique_ptr<WebApplicationCacheHost> CreateApplicationCacheHost(
@@ -149,7 +147,7 @@
   // May return null.
   virtual WebWorkerContentSettingsClientProxy*
   CreateWorkerContentSettingsClientProxy() {
-    return 0;
+    return nullptr;
   }
 
   // Returns a new WebWorkerFetchContext for a dedicated worker. Ownership of
@@ -164,14 +162,14 @@
   virtual WebExternalPopupMenu* CreateExternalPopupMenu(
       const WebPopupMenuInfo&,
       WebExternalPopupMenuClient*) {
-    return 0;
+    return nullptr;
   }
 
   // Services ------------------------------------------------------------
 
   // A frame specific cookie jar.  May return null, in which case
   // WebKitPlatformSupport::cookieJar() will be called to access cookies.
-  virtual WebCookieJar* CookieJar() { return 0; }
+  virtual WebCookieJar* CookieJar() { return nullptr; }
 
   // Returns a blame context for attributing work belonging to this frame.
   virtual BlameContext* GetFrameBlameContext() { return nullptr; }
@@ -322,7 +320,7 @@
     WebContentSecurityPolicyDisposition
         should_check_main_world_content_security_policy;
 
-    NavigationPolicyInfo(WebURLRequest& url_request)
+    explicit NavigationPolicyInfo(WebURLRequest& url_request)
         : extra_data(nullptr),
           url_request(url_request),
           navigation_type(kWebNavigationTypeOther),
@@ -480,12 +478,12 @@
   // Push API ---------------------------------------------------
 
   // Used to access the embedder for the Push API.
-  virtual WebPushClient* PushClient() { return 0; }
+  virtual WebPushClient* PushClient() { return nullptr; }
 
   // Presentation API ----------------------------------------------------
 
   // Used to access the embedder for the Presentation API.
-  virtual WebPresentationClient* PresentationClient() { return 0; }
+  virtual WebPresentationClient* PresentationClient() { return nullptr; }
 
   // InstalledApp API ----------------------------------------------------
 
@@ -519,7 +517,7 @@
       WebColorChooserClient*,
       const WebColor&,
       const WebVector<WebColorSuggestion>&) {
-    return 0;
+    return nullptr;
   }
 
   // Displays a modal alert dialog containing the given message. Returns
@@ -671,11 +669,11 @@
   virtual void WillStartUsingPeerConnectionHandler(
       WebRTCPeerConnectionHandler*) {}
 
-  virtual WebUserMediaClient* UserMediaClient() { return 0; }
+  virtual WebUserMediaClient* UserMediaClient() { return nullptr; }
 
   // Encrypted Media -------------------------------------------------
 
-  virtual WebEncryptedMediaClient* EncryptedMediaClient() { return 0; }
+  virtual WebEncryptedMediaClient* EncryptedMediaClient() { return nullptr; }
 
   // User agent ------------------------------------------------------
 
@@ -702,7 +700,7 @@
 
   // Access the embedder API for (client-based) screen orientation client .
   virtual WebScreenOrientationClient* GetWebScreenOrientationClient() {
-    return 0;
+    return nullptr;
   }
 
   // Accessibility -------------------------------------------------------
diff --git a/third_party/WebKit/public/web/WebRuntimeFeatures.h b/third_party/WebKit/public/web/WebRuntimeFeatures.h
index 296171a3..6122784 100644
--- a/third_party/WebKit/public/web/WebRuntimeFeatures.h
+++ b/third_party/WebKit/public/web/WebRuntimeFeatures.h
@@ -69,7 +69,6 @@
   BLINK_EXPORT static bool IsOriginTrialsEnabled();
 
   BLINK_EXPORT static void EnableAccelerated2dCanvas(bool);
-  BLINK_EXPORT static void EnableAccessibilityObjectModel(bool);
   BLINK_EXPORT static void EnableAudioOutputDevices(bool);
   BLINK_EXPORT static void EnableCanvas2dImageChromium(bool);
   BLINK_EXPORT static void EnableColorCorrectRendering(bool);
diff --git a/third_party/closure_compiler/compiled_resources.gyp b/third_party/closure_compiler/compiled_resources.gyp
index 85e3373b..286a8f6 100644
--- a/third_party/closure_compiler/compiled_resources.gyp
+++ b/third_party/closure_compiler/compiled_resources.gyp
@@ -20,7 +20,6 @@
       'target_name': 'compiled_resources',
       'type': 'none',
       'dependencies': [
-        '../../chrome/browser/resources/bookmark_manager/js/compiled_resources.gyp:*',
         '../../chrome/browser/resources/chromeos/compiled_resources.gyp:*',
         '../../chrome/browser/resources/extensions/compiled_resources.gyp:*',
         '../../chrome/browser/resources/help/compiled_resources.gyp:*',
diff --git a/third_party/closure_compiler/compiled_resources2.gyp b/third_party/closure_compiler/compiled_resources2.gyp
index 7316a7b..5b7d01e6 100644
--- a/third_party/closure_compiler/compiled_resources2.gyp
+++ b/third_party/closure_compiler/compiled_resources2.gyp
@@ -14,6 +14,7 @@
       'target_name': 'compiled_resources2',
       'type': 'none',
       'dependencies': [
+        '<(DEPTH)/chrome/browser/resources/bookmark_manager/js/compiled_resources2.gyp:*',
         '<(DEPTH)/chrome/browser/resources/chromeos/braille_ime/compiled_resources2.gyp:*',
         '<(DEPTH)/chrome/browser/resources/chromeos/login/compiled_resources2.gyp:*',
         '<(DEPTH)/chrome/browser/resources/chromeos/network_ui/compiled_resources2.gyp:*',
diff --git a/tools/ipc_fuzzer/fuzzer/fuzzer.cc b/tools/ipc_fuzzer/fuzzer/fuzzer.cc
index 159ee81f..a8d3c6c 100644
--- a/tools/ipc_fuzzer/fuzzer/fuzzer.cc
+++ b/tools/ipc_fuzzer/fuzzer/fuzzer.cc
@@ -1616,14 +1616,17 @@
     std::string scheme = p->scheme();
     std::string host = p->host();
     uint16_t port = p->port();
+    std::string suborigin = p->suborigin();
     if (!FuzzParam(&scheme, fuzzer))
       return false;
     if (!FuzzParam(&host, fuzzer))
       return false;
     if (!FuzzParam(&port, fuzzer))
       return false;
+    if (!FuzzParam(&suborigin, fuzzer))
+      return false;
     *p = url::Origin::UnsafelyCreateOriginWithoutNormalization(scheme, host,
-                                                               port);
+                                                               port, suborigin);
 
     // Force a unique origin 1% of the time:
     if (RandInRange(100) == 1)
diff --git a/ui/aura/mus/window_tree_host_mus.cc b/ui/aura/mus/window_tree_host_mus.cc
index a6500d0..8ff144e9 100644
--- a/ui/aura/mus/window_tree_host_mus.cc
+++ b/ui/aura/mus/window_tree_host_mus.cc
@@ -30,10 +30,6 @@
 
 static uint32_t accelerated_widget_count = 1;
 
-bool IsUsingTestContext() {
-  return aura::Env::GetInstance()->context_factory()->DoesCreateTestContexts();
-}
-
 }  // namespace
 
 ////////////////////////////////////////////////////////////////////////////////
@@ -60,20 +56,16 @@
   // process then ui::Compositor will not a cc::FrameSinkId.
   CreateCompositor(init_params.frame_sink_id);
   gfx::AcceleratedWidget accelerated_widget;
-  if (IsUsingTestContext()) {
-    accelerated_widget = gfx::kNullAcceleratedWidget;
-  } else {
 // We need accelerated widget numbers to be different for each
 // window and fit in the smallest sizeof(AcceleratedWidget) uint32_t
 // has this property.
 #if defined(OS_WIN) || defined(OS_ANDROID)
-    accelerated_widget =
-        reinterpret_cast<gfx::AcceleratedWidget>(accelerated_widget_count++);
+  accelerated_widget =
+      reinterpret_cast<gfx::AcceleratedWidget>(accelerated_widget_count++);
 #else
-    accelerated_widget =
-        static_cast<gfx::AcceleratedWidget>(accelerated_widget_count++);
+  accelerated_widget =
+      static_cast<gfx::AcceleratedWidget>(accelerated_widget_count++);
 #endif
-  }
   OnAcceleratedWidgetAvailable(accelerated_widget,
                                GetDisplay().device_scale_factor());
 
diff --git a/ui/aura/test/aura_mus_test_base.cc b/ui/aura/test/aura_mus_test_base.cc
index e89b1eb..b1f9770 100644
--- a/ui/aura/test/aura_mus_test_base.cc
+++ b/ui/aura/test/aura_mus_test_base.cc
@@ -12,8 +12,20 @@
 AuraMusWmTestBase::~AuraMusWmTestBase() {}
 
 void AuraMusWmTestBase::SetUp() {
+  // Run AuraTestBase::SetUp() first because it puts an InProcessContextFactory
+  // in env.
   EnableMusWithTestWindowTree();
   AuraTestBase::SetUp();
+
+  aura::Env* env = aura::Env::GetInstance();
+  DCHECK(env);
+  context_factory_to_restore_ = env->context_factory();
+  env->set_context_factory(&context_factory_);
+}
+
+void AuraMusWmTestBase::TearDown() {
+  aura::Env::GetInstance()->set_context_factory(context_factory_to_restore_);
+  AuraTestBase::TearDown();
 }
 
 AuraMusClientTestBase::AuraMusClientTestBase() {}
@@ -21,9 +33,21 @@
 AuraMusClientTestBase::~AuraMusClientTestBase() {}
 
 void AuraMusClientTestBase::SetUp() {
+  // Run AuraTestBase::SetUp() first because it puts an InProcessContextFactory
+  // in env.
   EnableMusWithTestWindowTree();
   set_window_manager_delegate(nullptr);
   AuraTestBase::SetUp();
+
+  aura::Env* env = aura::Env::GetInstance();
+  DCHECK(env);
+  context_factory_to_restore_ = env->context_factory();
+  env->set_context_factory(&context_factory_);
+}
+
+void AuraMusClientTestBase::TearDown() {
+  aura::Env::GetInstance()->set_context_factory(context_factory_to_restore_);
+  AuraTestBase::TearDown();
 }
 
 }  // namespace test
diff --git a/ui/aura/test/aura_mus_test_base.h b/ui/aura/test/aura_mus_test_base.h
index 8964dfba..4d700c3 100644
--- a/ui/aura/test/aura_mus_test_base.h
+++ b/ui/aura/test/aura_mus_test_base.h
@@ -7,6 +7,7 @@
 
 #include "base/macros.h"
 #include "ui/aura/test/aura_test_base.h"
+#include "ui/compositor/test/fake_context_factory.h"
 
 namespace aura {
 namespace test {
@@ -23,8 +24,12 @@
 
   // AuraTestBase:
   void SetUp() override;
+  void TearDown() override;
 
  private:
+  ui::FakeContextFactory context_factory_;
+  ui::ContextFactory* context_factory_to_restore_ = nullptr;
+
   DISALLOW_COPY_AND_ASSIGN(AuraMusWmTestBase);
 };
 
@@ -41,8 +46,12 @@
 
   // AuraTestBase:
   void SetUp() override;
+  void TearDown() override;
 
  private:
+  ui::FakeContextFactory context_factory_;
+  ui::ContextFactory* context_factory_to_restore_ = nullptr;
+
   DISALLOW_COPY_AND_ASSIGN(AuraMusClientTestBase);
 };
 
diff --git a/url/mojo/origin.mojom b/url/mojo/origin.mojom
index 884357ba..90d1ef8 100644
--- a/url/mojo/origin.mojom
+++ b/url/mojo/origin.mojom
@@ -8,5 +8,6 @@
   string scheme;
   string host;
   uint16 port;
+  string suborigin;
   bool unique;
 };
diff --git a/url/mojo/origin_struct_traits.h b/url/mojo/origin_struct_traits.h
index 3a10a41..3b8d453 100644
--- a/url/mojo/origin_struct_traits.h
+++ b/url/mojo/origin_struct_traits.h
@@ -18,6 +18,9 @@
   static uint16_t port(const url::Origin& r) {
     return r.port();
   }
+  static const std::string& suborigin(const url::Origin& r) {
+    return r.suborigin();
+  }
   static bool unique(const url::Origin& r) {
     return r.unique();
   }
@@ -25,12 +28,13 @@
     if (data.unique()) {
       *out = url::Origin();
     } else {
-      base::StringPiece scheme, host;
-      if (!data.ReadScheme(&scheme) || !data.ReadHost(&host))
+      base::StringPiece scheme, host, suborigin;
+      if (!data.ReadScheme(&scheme) || !data.ReadHost(&host) ||
+          !data.ReadSuborigin(&suborigin))
         return false;
 
-      *out = url::Origin::UnsafelyCreateOriginWithoutNormalization(scheme, host,
-                                                                   data.port());
+      *out = url::Origin::UnsafelyCreateOriginWithoutNormalization(
+          scheme, host, data.port(), suborigin);
     }
 
     // If a unique origin was created, but the unique flag wasn't set, then
diff --git a/url/mojo/url_gurl_struct_traits_unittest.cc b/url/mojo/url_gurl_struct_traits_unittest.cc
index 8556e0a..d5efe74 100644
--- a/url/mojo/url_gurl_struct_traits_unittest.cc
+++ b/url/mojo/url_gurl_struct_traits_unittest.cc
@@ -76,15 +76,21 @@
 
   // Test basic Origin serialization.
   Origin non_unique = Origin::UnsafelyCreateOriginWithoutNormalization(
-    "http", "www.google.com", 80);
+      "http", "www.google.com", 80, "");
   Origin output;
   EXPECT_TRUE(proxy->BounceOrigin(non_unique, &output));
   EXPECT_EQ(non_unique, output);
-  EXPECT_FALSE(non_unique.unique());
+  EXPECT_FALSE(output.unique());
 
   Origin unique;
   EXPECT_TRUE(proxy->BounceOrigin(unique, &output));
   EXPECT_TRUE(output.unique());
+
+  Origin with_sub_origin = Origin::CreateFromNormalizedTupleWithSuborigin(
+      "http", "www.google.com", 80, "suborigin");
+  EXPECT_TRUE(proxy->BounceOrigin(with_sub_origin, &output));
+  EXPECT_EQ(with_sub_origin, output);
+  EXPECT_FALSE(output.unique());
 }
 
 }  // namespace url
diff --git a/url/origin.cc b/url/origin.cc
index 53600b1..d15ba43f 100644
--- a/url/origin.cc
+++ b/url/origin.cc
@@ -107,8 +107,10 @@
 Origin Origin::UnsafelyCreateOriginWithoutNormalization(
     base::StringPiece scheme,
     base::StringPiece host,
-    uint16_t port) {
-  return Origin(scheme, host, port, "", SchemeHostPort::CHECK_CANONICALIZATION);
+    uint16_t port,
+    base::StringPiece suborigin) {
+  return Origin(scheme, host, port, suborigin,
+                SchemeHostPort::CHECK_CANONICALIZATION);
 }
 
 Origin Origin::CreateFromNormalizedTupleWithSuborigin(
@@ -173,7 +175,8 @@
 }
 
 bool Origin::operator<(const Origin& other) const {
-  return tuple_ < other.tuple_;
+  return tuple_ < other.tuple_ ||
+         (tuple_.Equals(other.tuple_) && suborigin_ < other.suborigin_);
 }
 
 std::ostream& operator<<(std::ostream& out, const url::Origin& origin) {
diff --git a/url/origin.h b/url/origin.h
index 4b838e4..9e6b492a 100644
--- a/url/origin.h
+++ b/url/origin.h
@@ -89,9 +89,9 @@
   // 3. 'file' URLs all parse as ("file", "", 0).
   explicit Origin(const GURL& url);
 
-  // Creates an Origin from a |scheme|, |host|, and |port|. All the parameters
-  // must be valid and canonicalized. Do not use this method to create unique
-  // origins. Use Origin() for that.
+  // Creates an Origin from a |scheme|, |host|, |port| and |suborigin|. All the
+  // parameters must be valid and canonicalized. Do not use this method to
+  // create unique origins. Use Origin() for that.
   //
   // This constructor should be used in order to pass 'Origin' objects back and
   // forth over IPC (as transitioning through GURL would risk potentially
@@ -100,7 +100,8 @@
   static Origin UnsafelyCreateOriginWithoutNormalization(
       base::StringPiece scheme,
       base::StringPiece host,
-      uint16_t port);
+      uint16_t port,
+      base::StringPiece suborigin);
 
   // Creates an origin without sanity checking that the host is canonicalized.
   // This should only be used when converting between already normalized types,
diff --git a/url/origin_unittest.cc b/url/origin_unittest.cc
index 4e1139c..b179591 100644
--- a/url/origin_unittest.cc
+++ b/url/origin_unittest.cc
@@ -363,7 +363,7 @@
     SCOPED_TRACE(testing::Message() << test.scheme << "://" << test.host << ":"
                                     << test.port);
     url::Origin origin = url::Origin::UnsafelyCreateOriginWithoutNormalization(
-        test.scheme, test.host, test.port);
+        test.scheme, test.host, test.port, "");
     EXPECT_EQ(test.scheme, origin.scheme());
     EXPECT_EQ(test.host, origin.host());
     EXPECT_EQ(test.port, origin.port());
@@ -400,7 +400,7 @@
     SCOPED_TRACE(testing::Message() << test.scheme << "://" << test.host << ":"
                                     << test.port);
     url::Origin origin = url::Origin::UnsafelyCreateOriginWithoutNormalization(
-        test.scheme, test.host, test.port);
+        test.scheme, test.host, test.port, "");
     EXPECT_EQ("", origin.scheme());
     EXPECT_EQ("", origin.host());
     EXPECT_EQ(0, origin.port());
@@ -430,7 +430,7 @@
                                     << test.port);
     url::Origin origin = url::Origin::UnsafelyCreateOriginWithoutNormalization(
         std::string(test.scheme, test.scheme_length),
-        std::string(test.host, test.host_length), test.port);
+        std::string(test.host, test.host_length), test.port, "");
     EXPECT_EQ("", origin.scheme());
     EXPECT_EQ("", origin.host());
     EXPECT_EQ(0, origin.port());