diff --git a/ash/accelerators/accelerator_controller_unittest.cc b/ash/accelerators/accelerator_controller_unittest.cc
index afb7706e..93ca448 100644
--- a/ash/accelerators/accelerator_controller_unittest.cc
+++ b/ash/accelerators/accelerator_controller_unittest.cc
@@ -1286,8 +1286,8 @@
   }
 
   void ResetStateIfNeeded() {
-    if (WmShell::Get()->session_controller()->IsScreenLocked() ||
-        WmShell::Get()->session_controller()->IsUserSessionBlocked()) {
+    if (Shell::Get()->session_controller()->IsScreenLocked() ||
+        Shell::Get()->session_controller()->IsUserSessionBlocked()) {
       UnblockUserSession();
     }
   }
diff --git a/ash/accelerators/accelerator_filter_unittest.cc b/ash/accelerators/accelerator_filter_unittest.cc
index d53f393b..ef17f19 100644
--- a/ash/accelerators/accelerator_filter_unittest.cc
+++ b/ash/accelerators/accelerator_filter_unittest.cc
@@ -150,7 +150,7 @@
 
 TEST_F(AcceleratorFilterTest, SearchKeyShortcutsAreAlwaysHandled) {
   SessionController* const session_controller =
-      WmShell::Get()->session_controller();
+      Shell::Get()->session_controller();
   EXPECT_FALSE(session_controller->IsScreenLocked());
 
   ui::test::EventGenerator generator(Shell::GetPrimaryRootWindow());
diff --git a/ash/aura/wm_shell_aura.cc b/ash/aura/wm_shell_aura.cc
index 0c495e8..3511001 100644
--- a/ash/aura/wm_shell_aura.cc
+++ b/ash/aura/wm_shell_aura.cc
@@ -291,15 +291,6 @@
       accelerator_controller_delegate_.get(), nullptr);
 }
 
-void WmShellAura::SessionStateChanged(session_manager::SessionState state) {
-  // Create the shelf if necessary.
-  WmShell::SessionStateChanged(state);
-
-  // Recreate the keyboard after initial login and after multiprofile login.
-  if (state == session_manager::SessionState::ACTIVE)
-    Shell::GetInstance()->CreateKeyboard();
-}
-
 void WmShellAura::OnDisplayConfigurationChanging() {
   for (auto& observer : display_observers_)
     observer.OnDisplayConfigurationChanging();
diff --git a/ash/aura/wm_shell_aura.h b/ash/aura/wm_shell_aura.h
index 9d5eef4..9e913c36 100644
--- a/ash/aura/wm_shell_aura.h
+++ b/ash/aura/wm_shell_aura.h
@@ -86,9 +86,6 @@
   std::unique_ptr<AcceleratorController> CreateAcceleratorController() override;
 
  private:
-  // SessionStateObserver:
-  void SessionStateChanged(session_manager::SessionState state) override;
-
   // WindowTreeHostManager::Observer:
   void OnDisplayConfigurationChanging() override;
   void OnDisplayConfigurationChanged() override;
diff --git a/ash/common/accelerators/accelerator_controller.cc b/ash/common/accelerators/accelerator_controller.cc
index 36aa5bf..926149f 100644
--- a/ash/common/accelerators/accelerator_controller.cc
+++ b/ash/common/accelerators/accelerator_controller.cc
@@ -85,7 +85,7 @@
   bool HasClickedListener() override { return true; }
 
   void Click() override {
-    if (!WmShell::Get()->session_controller()->IsUserSessionBlocked())
+    if (!Shell::Get()->session_controller()->IsUserSessionBlocked())
       Shell::Get()->shell_delegate()->OpenKeyboardShortcutHelpPage();
   }
 
@@ -499,12 +499,12 @@
 }
 
 bool CanHandleLock() {
-  return WmShell::Get()->session_controller()->CanLockScreen();
+  return Shell::Get()->session_controller()->CanLockScreen();
 }
 
 void HandleLock() {
   base::RecordAction(UserMetricsAction("Accel_LockScreen_L"));
-  WmShell::Get()->session_controller()->LockScreen();
+  Shell::Get()->session_controller()->LockScreen();
 }
 
 void HandleShowStylusTools() {
@@ -531,7 +531,7 @@
 
 bool CanHandleCycleUser() {
   return Shell::Get()->shell_delegate()->IsMultiProfilesEnabled() &&
-         WmShell::Get()->session_controller()->NumberOfLoggedInUsers() > 1;
+         Shell::Get()->session_controller()->NumberOfLoggedInUsers() > 1;
 }
 
 void HandleCycleUser(CycleUserDirection direction) {
@@ -545,7 +545,7 @@
       base::RecordAction(UserMetricsAction("Accel_Switch_To_Previous_User"));
       break;
   }
-  WmShell::Get()->session_controller()->CycleActiveUser(direction);
+  Shell::Get()->session_controller()->CycleActiveUser(direction);
 }
 
 bool CanHandleToggleCapsLock(const ui::Accelerator& accelerator,
@@ -1225,12 +1225,12 @@
           actions_allowed_in_pinned_mode_.end()) {
     return RESTRICTION_PREVENT_PROCESSING_AND_PROPAGATION;
   }
-  if (!wm_shell->session_controller()->IsActiveUserSessionStarted() &&
+  if (!Shell::Get()->session_controller()->IsActiveUserSessionStarted() &&
       actions_allowed_at_login_screen_.find(action) ==
           actions_allowed_at_login_screen_.end()) {
     return RESTRICTION_PREVENT_PROCESSING;
   }
-  if (wm_shell->session_controller()->IsScreenLocked() &&
+  if (Shell::Get()->session_controller()->IsScreenLocked() &&
       actions_allowed_at_lock_screen_.find(action) ==
           actions_allowed_at_lock_screen_.end()) {
     return RESTRICTION_PREVENT_PROCESSING;
diff --git a/ash/common/mojo_interface_factory.cc b/ash/common/mojo_interface_factory.cc
index f52469d..975c6e8 100644
--- a/ash/common/mojo_interface_factory.cc
+++ b/ash/common/mojo_interface_factory.cc
@@ -60,11 +60,11 @@
 
 void BindSessionControllerRequestOnMainThread(
     mojom::SessionControllerRequest request) {
-  WmShell::Get()->session_controller()->BindRequest(std::move(request));
+  Shell::Get()->session_controller()->BindRequest(std::move(request));
 }
 
 void BindShelfRequestOnMainThread(mojom::ShelfControllerRequest request) {
-  WmShell::Get()->shelf_controller()->BindRequest(std::move(request));
+  Shell::Get()->shelf_controller()->BindRequest(std::move(request));
 }
 
 void BindShutdownControllerRequestOnMainThread(
diff --git a/ash/common/session/session_state_observer.cc b/ash/common/session/session_state_observer.cc
index 709a92da..303a2c0e 100644
--- a/ash/common/session/session_state_observer.cc
+++ b/ash/common/session/session_state_observer.cc
@@ -5,20 +5,19 @@
 #include "ash/common/session/session_state_observer.h"
 
 #include "ash/common/session/session_controller.h"
-#include "ash/common/wm_shell.h"
+#include "ash/shell.h"
 
 namespace ash {
 
 ScopedSessionStateObserver::ScopedSessionStateObserver(
     SessionStateObserver* observer)
     : observer_(observer) {
-  WmShell::Get()->session_controller()->AddSessionStateObserver(observer_);
+  Shell::Get()->session_controller()->AddSessionStateObserver(observer_);
 }
 
 ScopedSessionStateObserver::~ScopedSessionStateObserver() {
-  if (WmShell::Get()) {
-    WmShell::Get()->session_controller()->RemoveSessionStateObserver(observer_);
-  }
+  if (Shell::HasInstance())
+    Shell::Get()->session_controller()->RemoveSessionStateObserver(observer_);
 }
 
 }  // namespace ash
diff --git a/ash/common/shelf/shelf_layout_manager.cc b/ash/common/shelf/shelf_layout_manager.cc
index bfc9dd4f6..f145a7e 100644
--- a/ash/common/shelf/shelf_layout_manager.cc
+++ b/ash/common/shelf/shelf_layout_manager.cc
@@ -160,9 +160,8 @@
   Shell::GetInstance()->AddShellObserver(this);
   WmShell::Get()->AddLockStateObserver(this);
   Shell::GetInstance()->activation_client()->AddObserver(this);
-  WmShell::Get()->session_controller()->AddSessionStateObserver(this);
-  state_.session_state =
-      WmShell::Get()->session_controller()->GetSessionState();
+  Shell::Get()->session_controller()->AddSessionStateObserver(this);
+  state_.session_state = Shell::Get()->session_controller()->GetSessionState();
 }
 
 ShelfLayoutManager::~ShelfLayoutManager() {
@@ -173,7 +172,7 @@
     observer.WillDeleteShelfLayoutManager();
   Shell::GetInstance()->RemoveShellObserver(this);
   WmShell::Get()->RemoveLockStateObserver(this);
-  WmShell::Get()->session_controller()->RemoveSessionStateObserver(this);
+  Shell::Get()->session_controller()->RemoveSessionStateObserver(this);
 }
 
 void ShelfLayoutManager::PrepareForShutdown() {
@@ -340,7 +339,7 @@
 
 bool ShelfLayoutManager::ProcessGestureEvent(const ui::GestureEvent& event) {
   // The gestures are disabled in the lock/login screen.
-  SessionController* controller = WmShell::Get()->session_controller();
+  SessionController* controller = Shell::Get()->session_controller();
   if (!controller->NumberOfLoggedInUsers() || controller->IsScreenLocked())
     return false;
 
@@ -437,7 +436,7 @@
   // On login screen if keyboard has been just hidden, update bounds just once
   // but ignore target_bounds.work_area_insets since shelf overlaps with login
   // window.
-  if (WmShell::Get()->session_controller()->IsUserSessionBlocked() &&
+  if (Shell::Get()->session_controller()->IsUserSessionBlocked() &&
       keyboard_is_about_to_hide) {
     WmWindow* window = WmWindow::Get(shelf_widget_->GetNativeWindow());
     WmShell::Get()->SetDisplayWorkAreaInsets(window, gfx::Insets());
diff --git a/ash/common/shelf/shelf_locking_manager.cc b/ash/common/shelf/shelf_locking_manager.cc
index a897049..84bfcbfa 100644
--- a/ash/common/shelf/shelf_locking_manager.cc
+++ b/ash/common/shelf/shelf_locking_manager.cc
@@ -15,7 +15,7 @@
     : shelf_(shelf), stored_alignment_(shelf->GetAlignment()) {
   DCHECK(shelf_);
   WmShell::Get()->AddLockStateObserver(this);
-  SessionController* controller = WmShell::Get()->session_controller();
+  SessionController* controller = Shell::Get()->session_controller();
   session_locked_ =
       controller->GetSessionState() != session_manager::SessionState::ACTIVE;
   screen_locked_ = controller->IsScreenLocked();
@@ -25,7 +25,7 @@
 
 ShelfLockingManager::~ShelfLockingManager() {
   WmShell::Get()->RemoveLockStateObserver(this);
-  WmShell::Get()->session_controller()->RemoveSessionStateObserver(this);
+  Shell::Get()->session_controller()->RemoveSessionStateObserver(this);
   Shell::GetInstance()->RemoveShellObserver(this);
 }
 
diff --git a/ash/common/shelf/shelf_widget.cc b/ash/common/shelf/shelf_widget.cc
index 55e51428..256c532 100644
--- a/ash/common/shelf/shelf_widget.cc
+++ b/ash/common/shelf/shelf_widget.cc
@@ -183,7 +183,7 @@
   // TODO(jamescook): Move ownership to RootWindowController.
   status_area_widget_ = new StatusAreaWidget(status_container, wm_shelf_);
   status_area_widget_->CreateTrayViews();
-  if (WmShell::Get()->session_controller()->IsActiveUserSessionStarted())
+  if (Shell::Get()->session_controller()->IsActiveUserSessionStarted())
     status_area_widget_->Show();
   Shell::Get()->focus_cycler()->AddWidget(status_area_widget_);
   background_animator_.AddObserver(status_area_widget_);
@@ -231,9 +231,8 @@
 ShelfView* ShelfWidget::CreateShelfView() {
   DCHECK(!shelf_view_);
 
-  shelf_view_ =
-      new ShelfView(WmShell::Get()->shelf_model(),
-                    WmShell::Get()->shelf_delegate(), wm_shelf_, this);
+  shelf_view_ = new ShelfView(Shell::Get()->shelf_model(),
+                              Shell::Get()->shelf_delegate(), wm_shelf_, this);
   shelf_view_->Init();
   GetContentsView()->AddChildView(shelf_view_);
   return shelf_view_;
@@ -252,7 +251,7 @@
   // hidden because ShelfWidget is transparent. Some of the ShelfView visibility
   // code could be simplified. http://crbug.com/674773
   shelf_view_->SetVisible(
-      WmShell::Get()->session_controller()->IsActiveUserSessionStarted());
+      Shell::Get()->session_controller()->IsActiveUserSessionStarted());
   shelf_layout_manager_->LayoutShelf();
   shelf_layout_manager_->UpdateAutoHideState();
   Show();
diff --git a/ash/common/shelf/shelf_window_watcher_item_delegate.cc b/ash/common/shelf/shelf_window_watcher_item_delegate.cc
index 2bac8bb..88be8b7 100644
--- a/ash/common/shelf/shelf_window_watcher_item_delegate.cc
+++ b/ash/common/shelf/shelf_window_watcher_item_delegate.cc
@@ -7,9 +7,9 @@
 #include "ash/common/shelf/shelf_controller.h"
 #include "ash/common/shelf/shelf_model.h"
 #include "ash/common/wm/window_state.h"
-#include "ash/common/wm_shell.h"
 #include "ash/common/wm_window.h"
 #include "ash/public/cpp/window_properties.h"
+#include "ash/shell.h"
 #include "ash/wm/window_util.h"
 #include "ui/aura/window.h"
 #include "ui/events/event_constants.h"
@@ -19,7 +19,7 @@
 namespace {
 
 ShelfItemType GetShelfItemType(ShelfID id) {
-  ShelfModel* model = WmShell::Get()->shelf_controller()->model();
+  ShelfModel* model = Shell::Get()->shelf_controller()->model();
   ShelfItems::const_iterator item = model->ItemByID(id);
   return item == model->items().end() ? TYPE_UNDEFINED : item->type;
 }
diff --git a/ash/common/shelf/shelf_window_watcher_unittest.cc b/ash/common/shelf/shelf_window_watcher_unittest.cc
index 7e5ed90c..59f5ba1 100644
--- a/ash/common/shelf/shelf_window_watcher_unittest.cc
+++ b/ash/common/shelf/shelf_window_watcher_unittest.cc
@@ -29,7 +29,7 @@
 
   void SetUp() override {
     test::AshTestBase::SetUp();
-    model_ = WmShell::Get()->shelf_model();
+    model_ = Shell::Get()->shelf_model();
   }
 
   void TearDown() override {
@@ -38,7 +38,7 @@
   }
 
   static ShelfID CreateShelfItem(WmWindow* window) {
-    ShelfID id = WmShell::Get()->shelf_model()->next_id();
+    ShelfID id = Shell::Get()->shelf_model()->next_id();
     window->aura_window()->SetProperty(kShelfItemTypeKey,
                                        static_cast<int32_t>(TYPE_DIALOG));
     return id;
@@ -335,9 +335,9 @@
 // Ensures ShelfWindowWatcher supports windows opened prior to session start.
 using ShelfWindowWatcherSessionStartTest = test::NoSessionAshTestBase;
 TEST_F(ShelfWindowWatcherSessionStartTest, PreExistingWindow) {
-  ShelfModel* model = WmShell::Get()->shelf_model();
+  ShelfModel* model = Shell::Get()->shelf_model();
   ASSERT_FALSE(
-      WmShell::Get()->session_controller()->IsActiveUserSessionStarted());
+      Shell::Get()->session_controller()->IsActiveUserSessionStarted());
 
   // ShelfModel only has an APP_LIST item.
   EXPECT_EQ(1, model->item_count());
diff --git a/ash/common/shelf/wm_shelf.cc b/ash/common/shelf/wm_shelf.cc
index a6b695c..48e7e05 100644
--- a/ash/common/shelf/wm_shelf.cc
+++ b/ash/common/shelf/wm_shelf.cc
@@ -141,7 +141,7 @@
   DCHECK(!shelf_view_);
   shelf_view_ = shelf_widget_->CreateShelfView();
   shelf_locking_manager_.reset(new ShelfLockingManager(this));
-  WmShell::Get()->shelf_controller()->NotifyShelfCreated(this);
+  Shell::Get()->shelf_controller()->NotifyShelfCreated(this);
 }
 
 void WmShelf::ShutdownShelf() {
@@ -175,7 +175,7 @@
   // The ShelfWidget notifies the ShelfView of the alignment change.
   shelf_widget_->OnShelfAlignmentChanged();
   shelf_layout_manager_->LayoutShelf();
-  WmShell::Get()->shelf_controller()->NotifyShelfAlignmentChanged(this);
+  Shell::Get()->shelf_controller()->NotifyShelfAlignmentChanged(this);
   Shell::GetInstance()->NotifyShelfAlignmentChanged(
       GetWindow()->GetRootWindow());
 }
@@ -220,7 +220,7 @@
     return;
 
   auto_hide_behavior_ = auto_hide_behavior;
-  WmShell::Get()->shelf_controller()->NotifyShelfAutoHideBehaviorChanged(this);
+  Shell::Get()->shelf_controller()->NotifyShelfAutoHideBehaviorChanged(this);
   Shell::GetInstance()->NotifyShelfAutoHideBehaviorChanged(
       GetWindow()->GetRootWindow());
 }
@@ -272,7 +272,7 @@
 
 // static
 void WmShelf::LaunchShelfItem(int item_index) {
-  ShelfModel* shelf_model = WmShell::Get()->shelf_model();
+  ShelfModel* shelf_model = Shell::Get()->shelf_model();
   const ShelfItems& items = shelf_model->items();
   int item_count = shelf_model->item_count();
   int indexes_left = item_index >= 0 ? item_index : item_count;
@@ -298,7 +298,7 @@
 
 // static
 void WmShelf::ActivateShelfItem(int item_index) {
-  ShelfModel* shelf_model = WmShell::Get()->shelf_model();
+  ShelfModel* shelf_model = Shell::Get()->shelf_model();
   const ShelfItem& item = shelf_model->items()[item_index];
   mojom::ShelfItemDelegate* item_delegate =
       shelf_model->GetShelfItemDelegate(item.id);
diff --git a/ash/common/system/chromeos/media_security/multi_profile_media_tray_item.cc b/ash/common/system/chromeos/media_security/multi_profile_media_tray_item.cc
index 09f8bdd..d9e08fc 100644
--- a/ash/common/system/chromeos/media_security/multi_profile_media_tray_item.cc
+++ b/ash/common/system/chromeos/media_security/multi_profile_media_tray_item.cc
@@ -40,7 +40,7 @@
   // MediaCaptureObserver:
   void OnMediaCaptureChanged(
       const std::vector<mojom::MediaCaptureState>& capture_states) override {
-    SessionController* controller = WmShell::Get()->session_controller();
+    SessionController* controller = Shell::Get()->session_controller();
     // The user at 0 is the current desktop user.
     for (UserIndex index = 1; index < controller->NumberOfLoggedInUsers();
          ++index) {
diff --git a/ash/common/system/chromeos/media_security/multi_profile_media_tray_item_unittest.cc b/ash/common/system/chromeos/media_security/multi_profile_media_tray_item_unittest.cc
index ac00a30..1989f6a9 100644
--- a/ash/common/system/chromeos/media_security/multi_profile_media_tray_item_unittest.cc
+++ b/ash/common/system/chromeos/media_security/multi_profile_media_tray_item_unittest.cc
@@ -29,7 +29,7 @@
 
   void SetMediaCaptureState(mojom::MediaCaptureState state) {
     // Create the fake update.
-    SessionController* controller = WmShell::Get()->session_controller();
+    SessionController* controller = Shell::Get()->session_controller();
     std::vector<mojom::MediaCaptureState> v;
     for (int i = 0; i < controller->NumberOfLoggedInUsers(); ++i)
       v.push_back(state);
diff --git a/ash/common/system/chromeos/network/tray_vpn.cc b/ash/common/system/chromeos/network/tray_vpn.cc
index 4827992..38379de 100644
--- a/ash/common/system/chromeos/network/tray_vpn.cc
+++ b/ash/common/system/chromeos/network/tray_vpn.cc
@@ -17,6 +17,7 @@
 #include "ash/common/system/tray/tray_popup_item_style.h"
 #include "ash/common/wm_shell.h"
 #include "ash/resources/vector_icons/vector_icons.h"
+#include "ash/shell.h"
 #include "ash/strings/grit/ash_strings.h"
 #include "chromeos/network/network_state.h"
 #include "chromeos/network/network_state_handler.h"
@@ -161,7 +162,7 @@
     return NULL;
 
   const bool is_in_secondary_login_screen =
-      WmShell::Get()->session_controller()->IsInSecondaryLoginScreen();
+      Shell::Get()->session_controller()->IsInSecondaryLoginScreen();
 
   default_ = new tray::VpnDefaultView(this);
   default_->SetEnabled(status != LoginStatus::LOCKED &&
diff --git a/ash/common/system/chromeos/palette/palette_tray.cc b/ash/common/system/chromeos/palette/palette_tray.cc
index f2d8ca4..197e75a 100644
--- a/ash/common/system/chromeos/palette/palette_tray.cc
+++ b/ash/common/system/chromeos/palette/palette_tray.cc
@@ -63,7 +63,7 @@
 
 // Returns true if we are in a user session that can show the stylus tools.
 bool IsInUserSession() {
-  SessionController* session_controller = WmShell::Get()->session_controller();
+  SessionController* session_controller = Shell::Get()->session_controller();
   return !session_controller->IsUserSessionBlocked() &&
          session_controller->GetSessionState() ==
              session_manager::SessionState::ACTIVE &&
@@ -165,7 +165,7 @@
   tray_container()->AddChildView(icon_);
 
   Shell::GetInstance()->AddShellObserver(this);
-  WmShell::Get()->session_controller()->AddSessionStateObserver(this);
+  Shell::Get()->session_controller()->AddSessionStateObserver(this);
   ui::InputDeviceManager::GetInstance()->AddObserver(this);
 }
 
@@ -175,7 +175,7 @@
 
   ui::InputDeviceManager::GetInstance()->RemoveObserver(this);
   Shell::GetInstance()->RemoveShellObserver(this);
-  WmShell::Get()->session_controller()->RemoveSessionStateObserver(this);
+  Shell::Get()->session_controller()->RemoveSessionStateObserver(this);
 }
 
 bool PaletteTray::PerformAction(const ui::Event& event) {
diff --git a/ash/common/system/chromeos/settings/tray_settings.cc b/ash/common/system/chromeos/settings/tray_settings.cc
index 0d8a0aaef..9c10928 100644
--- a/ash/common/system/chromeos/settings/tray_settings.cc
+++ b/ash/common/system/chromeos/settings/tray_settings.cc
@@ -50,7 +50,7 @@
     bool power_view_right_align = false;
     if (login_status_ != LoginStatus::NOT_LOGGED_IN &&
         login_status_ != LoginStatus::LOCKED &&
-        !WmShell::Get()->session_controller()->IsInSecondaryLoginScreen()) {
+        !Shell::Get()->session_controller()->IsInSecondaryLoginScreen()) {
       ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
       views::ImageView* icon = TrayPopupUtils::CreateMainImageView();
 
@@ -84,7 +84,7 @@
   bool PerformAction(const ui::Event& event) override {
     if (login_status_ == LoginStatus::NOT_LOGGED_IN ||
         login_status_ == LoginStatus::LOCKED ||
-        WmShell::Get()->session_controller()->IsInSecondaryLoginScreen()) {
+        Shell::Get()->session_controller()->IsInSecondaryLoginScreen()) {
       return false;
     }
 
diff --git a/ash/common/system/date/date_default_view.cc b/ash/common/system/date/date_default_view.cc
index ddfd326..0ff3026 100644
--- a/ash/common/system/date/date_default_view.cc
+++ b/ash/common/system/date/date_default_view.cc
@@ -53,7 +53,7 @@
   view->SetContent(date_view_);
   AddChildView(view);
 
-  WmShell* shell = WmShell::Get();
+  Shell* shell = Shell::Get();
   const bool adding_user =
       shell->session_controller()->IsInSecondaryLoginScreen();
 
diff --git a/ash/common/system/overview/overview_button_tray.cc b/ash/common/system/overview/overview_button_tray.cc
index a323d40..d642efe8 100644
--- a/ash/common/system/overview/overview_button_tray.cc
+++ b/ash/common/system/overview/overview_button_tray.cc
@@ -34,12 +34,12 @@
   set_separator_visibility(false);
 
   Shell::GetInstance()->AddShellObserver(this);
-  WmShell::Get()->session_controller()->AddSessionStateObserver(this);
+  Shell::Get()->session_controller()->AddSessionStateObserver(this);
 }
 
 OverviewButtonTray::~OverviewButtonTray() {
   Shell::GetInstance()->RemoveShellObserver(this);
-  WmShell::Get()->session_controller()->RemoveSessionStateObserver(this);
+  Shell::Get()->session_controller()->RemoveSessionStateObserver(this);
 }
 
 void OverviewButtonTray::UpdateAfterLoginStatusChange(LoginStatus status) {
@@ -109,7 +109,7 @@
   // WindowSelectorController::CanSelect. The visibility of the button should
   // not change during transient times in which CanSelect is false. Such as when
   // a modal dialog is present.
-  SessionController* session_controller = WmShell::Get()->session_controller();
+  SessionController* session_controller = Shell::Get()->session_controller();
 
   Shell* shell = Shell::Get();
   SetVisible(
diff --git a/ash/common/system/tiles/tiles_default_view.cc b/ash/common/system/tiles/tiles_default_view.cc
index 5e74c87..5861a99 100644
--- a/ash/common/system/tiles/tiles_default_view.cc
+++ b/ash/common/system/tiles/tiles_default_view.cc
@@ -90,7 +90,7 @@
   lock_button_ =
       new SystemMenuButton(this, TrayPopupInkDropStyle::HOST_CENTERED,
                            kSystemMenuLockIcon, IDS_ASH_STATUS_TRAY_LOCK);
-  if (disable_buttons || !shell->session_controller()->CanLockScreen())
+  if (disable_buttons || !Shell::Get()->session_controller()->CanLockScreen())
     lock_button_->SetEnabled(false);
 
   AddChildView(lock_button_);
@@ -102,7 +102,7 @@
   AddChildView(power_button_);
   // This object is recreated every time the menu opens. Don't bother updating
   // the tooltip if the shutdown policy changes while the menu is open.
-  bool reboot = WmShell::Get()->shutdown_controller()->reboot_on_shutdown();
+  bool reboot = shell->shutdown_controller()->reboot_on_shutdown();
   power_button_->SetTooltipText(l10n_util::GetStringUTF16(
       reboot ? IDS_ASH_STATUS_TRAY_REBOOT : IDS_ASH_STATUS_TRAY_SHUTDOWN));
 }
diff --git a/ash/common/system/tray/system_tray.cc b/ash/common/system/tray/system_tray.cc
index fa71896..b7a849b 100644
--- a/ash/common/system/tray/system_tray.cc
+++ b/ash/common/system/tray/system_tray.cc
@@ -267,7 +267,7 @@
 
   // Create user items for each possible user.
   const int maximum_user_profiles =
-      WmShell::Get()->session_controller()->GetMaximumNumberOfLoggedInUsers();
+      Shell::Get()->session_controller()->GetMaximumNumberOfLoggedInUsers();
   for (int i = 0; i < maximum_user_profiles; i++)
     AddTrayItem(base::MakeUnique<TrayUser>(this, i));
 
diff --git a/ash/common/system/tray/tray_popup_utils.cc b/ash/common/system/tray/tray_popup_utils.cc
index d1227be..98e9fdb 100644
--- a/ash/common/system/tray/tray_popup_utils.cc
+++ b/ash/common/system/tray/tray_popup_utils.cc
@@ -14,8 +14,8 @@
 #include "ash/common/system/tray/size_range_layout.h"
 #include "ash/common/system/tray/tray_constants.h"
 #include "ash/common/system/tray/tray_popup_item_style.h"
-#include "ash/common/wm_shell.h"
 #include "ash/resources/vector_icons/vector_icons.h"
+#include "ash/shell.h"
 #include "base/memory/ptr_util.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/gfx/paint_vector_icon.h"
@@ -431,7 +431,7 @@
   // ShowSettings() method.
   return status != LoginStatus::NOT_LOGGED_IN &&
          status != LoginStatus::LOCKED &&
-         !WmShell::Get()->session_controller()->IsInSecondaryLoginScreen();
+         !Shell::Get()->session_controller()->IsInSecondaryLoginScreen();
 }
 
 }  // namespace ash
diff --git a/ash/common/system/user/login_status.cc b/ash/common/system/user/login_status.cc
index bc036f7..011aca19 100644
--- a/ash/common/system/user/login_status.cc
+++ b/ash/common/system/user/login_status.cc
@@ -5,7 +5,7 @@
 #include "ash/common/system/user/login_status.h"
 
 #include "ash/common/session/session_controller.h"
-#include "ash/common/wm_shell.h"
+#include "ash/shell.h"
 #include "ash/strings/grit/ash_strings.h"
 #include "base/strings/string_util.h"
 #include "base/strings/utf_string_conversions.h"
@@ -26,7 +26,7 @@
       break;
     default:
       message_id =
-          WmShell::Get()->session_controller()->NumberOfLoggedInUsers() > 1
+          Shell::Get()->session_controller()->NumberOfLoggedInUsers() > 1
               ? IDS_ASH_STATUS_TRAY_SIGN_OUT_ALL
               : IDS_ASH_STATUS_TRAY_SIGN_OUT;
       break;
diff --git a/ash/common/system/user/tray_user.cc b/ash/common/system/user/tray_user.cc
index bc481318..c9035d3 100644
--- a/ash/common/system/user/tray_user.cc
+++ b/ash/common/system/user/tray_user.cc
@@ -82,7 +82,7 @@
   if (status == LoginStatus::NOT_LOGGED_IN)
     return nullptr;
   const SessionController* const session_controller =
-      WmShell::Get()->session_controller();
+      Shell::Get()->session_controller();
 
   // If the screen is locked or a system modal dialog box is shown, show only
   // the currently active user.
@@ -224,7 +224,7 @@
 
 void TrayUser::OnUserAddedToSession() {
   const SessionController* const session_controller =
-      WmShell::Get()->session_controller();
+      Shell::Get()->session_controller();
   // Only create views for user items which are logged in.
   if (user_index_ >= session_controller->NumberOfLoggedInUsers())
     return;
@@ -238,7 +238,7 @@
 
 void TrayUser::UpdateAvatarImage(LoginStatus status) {
   const SessionController* const session_controller =
-      WmShell::Get()->session_controller();
+      Shell::Get()->session_controller();
   if (!avatar_ || user_index_ >= session_controller->NumberOfLoggedInUsers())
     return;
 
diff --git a/ash/common/system/user/tray_user_unittest.cc b/ash/common/system/user/tray_user_unittest.cc
index 74d0cae6..7b6c5476 100644
--- a/ash/common/system/user/tray_user_unittest.cc
+++ b/ash/common/system/user/tray_user_unittest.cc
@@ -55,9 +55,7 @@
 
   // Accessors to various system components.
   SystemTray* tray() { return tray_; }
-  SessionController* controller() {
-    return WmShell::Get()->session_controller();
-  }
+  SessionController* controller() { return Shell::Get()->session_controller(); }
   TrayUser* tray_user(int index) { return tray_user_[index]; }
 
  private:
diff --git a/ash/common/system/user/user_card_view.cc b/ash/common/system/user/user_card_view.cc
index dcc71b3c..bf56606b5 100644
--- a/ash/common/system/user/user_card_view.cc
+++ b/ash/common/system/user/user_card_view.cc
@@ -64,7 +64,7 @@
         gfx::CreateVectorIcon(kSystemMenuGuestIcon, kMenuIconColor);
     image_view->SetImage(icon, icon.size());
   } else {
-    SessionController* controller = WmShell::Get()->session_controller();
+    SessionController* controller = Shell::Get()->session_controller();
     // TODO(xiyuan); HiDpi avatar support. http://crbug.com/702689
     image_view->SetImage(gfx::ImageSkia::CreateFrom1xBitmap(
                              controller->GetUserSession(user_index)->avatar),
@@ -122,7 +122,7 @@
   // Note that since this is a public account it always has to be the primary
   // user.
   base::string16 display_name = base::UTF8ToUTF16(
-      WmShell::Get()->session_controller()->GetUserSession(0)->display_name);
+      Shell::Get()->session_controller()->GetUserSession(0)->display_name);
   base::RemoveChars(display_name, kDisplayNameMark, &display_name);
   display_name = kDisplayNameMark[0] + display_name + kDisplayNameMark[0];
   // Retrieve the domain managing the device and wrap it with markers.
@@ -396,7 +396,7 @@
 void UserCardView::AddUserContent(views::BoxLayout* layout,
                                   LoginStatus login_status) {
   AddChildView(CreateUserAvatarView(login_status, user_index_));
-  SessionController* controller = WmShell::Get()->session_controller();
+  SessionController* controller = Shell::Get()->session_controller();
   base::string16 user_name_string =
       login_status == LoginStatus::GUEST
           ? l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_GUEST_LABEL)
diff --git a/ash/common/system/user/user_view.cc b/ash/common/system/user/user_view.cc
index 3f20a7f..913ffd0 100644
--- a/ash/common/system/user/user_view.cc
+++ b/ash/common/system/user/user_view.cc
@@ -50,7 +50,7 @@
 // Switch to a user with the given |user_index|.
 void SwitchUser(UserIndex user_index) {
   // Do not switch users when the log screen is presented.
-  SessionController* controller = WmShell::Get()->session_controller();
+  SessionController* controller = Shell::Get()->session_controller();
   if (controller->IsUserSessionBlocked())
     return;
 
@@ -68,7 +68,7 @@
 
 bool IsMultiProfileSupportedAndUserActive() {
   return Shell::Get()->shell_delegate()->IsMultiProfilesEnabled() &&
-         !WmShell::Get()->session_controller()->IsUserSessionBlocked();
+         !Shell::Get()->session_controller()->IsUserSessionBlocked();
 }
 
 // Creates the view shown in the user switcher popup ("AddUserMenuOption").
@@ -352,7 +352,7 @@
   add_menu_option_->Init(params);
 
   const AddUserSessionPolicy add_user_policy =
-      WmShell::Get()->session_controller()->GetAddUserPolicy();
+      Shell::Get()->session_controller()->GetAddUserPolicy();
   add_user_enabled_ = add_user_policy == AddUserSessionPolicy::ALLOWED;
 
   // Position the widget on top of the user card view (which is still in the
diff --git a/ash/common/system/web_notification/web_notification_tray.cc b/ash/common/system/web_notification/web_notification_tray.cc
index 49c7b55..a299812 100644
--- a/ash/common/system/web_notification/web_notification_tray.cc
+++ b/ash/common/system/web_notification/web_notification_tray.cc
@@ -616,7 +616,7 @@
 bool WebNotificationTray::IsLoggedIn() const {
   return Shell::Get()->system_tray_delegate()->GetUserLoginStatus() !=
              LoginStatus::NOT_LOGGED_IN &&
-         !WmShell::Get()->session_controller()->IsInSecondaryLoginScreen();
+         !Shell::Get()->session_controller()->IsInSecondaryLoginScreen();
 }
 
 // Methods for testing
diff --git a/ash/common/test/test_session_controller_client.cc b/ash/common/test/test_session_controller_client.cc
index b4e86e2..a9e9f8b 100644
--- a/ash/common/test/test_session_controller_client.cc
+++ b/ash/common/test/test_session_controller_client.cc
@@ -9,7 +9,7 @@
 
 #include "ash/common/login_status.h"
 #include "ash/common/session/session_controller.h"
-#include "ash/common/wm_shell.h"
+#include "ash/shell.h"
 #include "base/logging.h"
 #include "base/strings/stringprintf.h"
 #include "components/session_manager/session_manager_types.h"
@@ -79,7 +79,7 @@
   controller_->SetSessionInfo(session_info_->Clone());
 
   // TODO(xiyuan): Remove after LoginStatus becomes part of SessionController.
-  WmShell::Get()->UpdateAfterLoginStatusChange(
+  Shell::Get()->UpdateAfterLoginStatusChange(
       state == session_manager::SessionState::ACTIVE
           ? LoginStatus::USER
           : LoginStatus::NOT_LOGGED_IN);
diff --git a/ash/common/test/test_shelf_delegate.cc b/ash/common/test/test_shelf_delegate.cc
index 7a6df08..d27d813f 100644
--- a/ash/common/test/test_shelf_delegate.cc
+++ b/ash/common/test/test_shelf_delegate.cc
@@ -75,7 +75,7 @@
     item.type = TYPE_APP_PANEL;
   else
     item.type = TYPE_APP;
-  ShelfModel* model = WmShell::Get()->shelf_model();
+  ShelfModel* model = Shell::Get()->shelf_model();
   ShelfID id = model->next_id();
   item.status = status;
   model->Add(item);
@@ -90,7 +90,7 @@
   ShelfID shelf_id = window->aura_window()->GetProperty(kShelfIDKey);
   if (shelf_id == 0)
     return;
-  ShelfModel* model = WmShell::Get()->shelf_model();
+  ShelfModel* model = Shell::Get()->shelf_model();
   int index = model->ItemIndexByID(shelf_id);
   DCHECK_NE(-1, index);
   model->RemoveItemAt(index);
diff --git a/ash/common/test/test_system_tray_delegate.cc b/ash/common/test/test_system_tray_delegate.cc
index c6d3226..24aefa4 100644
--- a/ash/common/test/test_system_tray_delegate.cc
+++ b/ash/common/test/test_system_tray_delegate.cc
@@ -9,6 +9,7 @@
 #include "ash/common/login_status.h"
 #include "ash/common/session/session_controller.h"
 #include "ash/common/wm_shell.h"
+#include "ash/shell.h"
 #include "base/time/time.h"
 
 namespace ash {
@@ -27,7 +28,7 @@
 
 void TestSystemTrayDelegate::SetLoginStatus(LoginStatus login_status) {
   login_status_ = login_status;
-  WmShell::Get()->UpdateAfterLoginStatusChange(login_status);
+  Shell::Get()->UpdateAfterLoginStatusChange(login_status);
 }
 
 void TestSystemTrayDelegate::SetSessionLengthLimitForTest(
@@ -57,7 +58,7 @@
 
   // At new user image screen manager->IsUserLoggedIn() would return true
   // but there's no browser session available yet so use SessionStarted().
-  SessionController* controller = WmShell::Get()->session_controller();
+  SessionController* controller = Shell::Get()->session_controller();
 
   if (!controller->IsActiveUserSessionStarted())
     return LoginStatus::NOT_LOGGED_IN;
diff --git a/ash/common/wallpaper/wallpaper_view.cc b/ash/common/wallpaper/wallpaper_view.cc
index 14345c0..843f95a 100644
--- a/ash/common/wallpaper/wallpaper_view.cc
+++ b/ash/common/wallpaper/wallpaper_view.cc
@@ -222,7 +222,7 @@
   // 4. From an empty background, guest user logged in.
   if (wallpaper_delegate->ShouldShowInitialAnimation() ||
       root_window_controller->animating_wallpaper_widget_controller() ||
-      WmShell::Get()->session_controller()->NumberOfLoggedInUsers()) {
+      Shell::Get()->session_controller()->NumberOfLoggedInUsers()) {
     wallpaper_window->SetVisibilityAnimationTransition(::wm::ANIMATE_SHOW);
     int duration_override = wallpaper_delegate->GetAnimationDurationOverride();
     if (duration_override) {
diff --git a/ash/common/wm/container_finder.cc b/ash/common/wm/container_finder.cc
index c52067a4..b9fe3167 100644
--- a/ash/common/wm/container_finder.cc
+++ b/ash/common/wm/container_finder.cc
@@ -39,7 +39,7 @@
   // all modal windows are placed into the normal modal container.
   // In case of missing transient parent (it could happen for alerts from
   // background pages) assume that the window belongs to user session.
-  if (!WmShell::Get()->session_controller()->IsUserSessionBlocked() ||
+  if (!Shell::Get()->session_controller()->IsUserSessionBlocked() ||
       !window->GetTransientParent()) {
     return root->GetChildByShellWindowId(kShellWindowId_SystemModalContainer);
   }
diff --git a/ash/common/wm/maximize_mode/maximize_mode_event_handler.cc b/ash/common/wm/maximize_mode/maximize_mode_event_handler.cc
index 7c3d36d..55838ef1 100644
--- a/ash/common/wm/maximize_mode/maximize_mode_event_handler.cc
+++ b/ash/common/wm/maximize_mode/maximize_mode_event_handler.cc
@@ -31,7 +31,7 @@
   if (event.type() != ui::ET_TOUCH_PRESSED)
     return false;
 
-  const SessionController* controller = WmShell::Get()->session_controller();
+  const SessionController* controller = Shell::Get()->session_controller();
 
   if (controller->IsScreenLocked() ||
       controller->GetSessionState() != session_manager::SessionState::ACTIVE) {
diff --git a/ash/common/wm/overview/window_selector_controller.cc b/ash/common/wm/overview/window_selector_controller.cc
index fa00733..6769442 100644
--- a/ash/common/wm/overview/window_selector_controller.cc
+++ b/ash/common/wm/overview/window_selector_controller.cc
@@ -33,10 +33,10 @@
 bool WindowSelectorController::CanSelect() {
   // Don't allow a window overview if the screen is locked or a modal dialog is
   // open or running in kiosk app session.
-  WmShell* wm_shell = WmShell::Get();
-  SessionController* session_controller = wm_shell->session_controller();
+  SessionController* session_controller = Shell::Get()->session_controller();
   SystemTrayDelegate* system_tray_delegate =
       Shell::Get()->system_tray_delegate();
+  WmShell* wm_shell = WmShell::Get();
   return session_controller->IsActiveUserSessionStarted() &&
          !session_controller->IsScreenLocked() &&
          !wm_shell->IsSystemModalWindowOpen() && !wm_shell->IsPinned() &&
diff --git a/ash/common/wm/system_modal_container_layout_manager.cc b/ash/common/wm/system_modal_container_layout_manager.cc
index 220a4d4..3c29217 100644
--- a/ash/common/wm/system_modal_container_layout_manager.cc
+++ b/ash/common/wm/system_modal_container_layout_manager.cc
@@ -11,6 +11,7 @@
 #include "ash/common/wm_shell.h"
 #include "ash/common/wm_window.h"
 #include "ash/public/cpp/shell_window_ids.h"
+#include "ash/shell.h"
 #include "base/memory/ptr_util.h"
 #include "base/stl_util.h"
 #include "ui/aura/client/aura_constants.h"
@@ -80,7 +81,7 @@
   if (!WmShell::Get()->IsRunningInMash()) {
     DCHECK(container_->GetShellWindowId() !=
                kShellWindowId_LockSystemModalContainer ||
-           WmShell::Get()->session_controller()->IsUserSessionBlocked());
+           Shell::Get()->session_controller()->IsUserSessionBlocked());
   }
   // Since this is for SystemModal, there is no good reason to add windows
   // other than MODAL_TYPE_NONE or MODAL_TYPE_SYSTEM. DCHECK to avoid simple
diff --git a/ash/common/wm/window_cycle_controller.cc b/ash/common/wm/window_cycle_controller.cc
index 7b6cfe5..b3cd51ca 100644
--- a/ash/common/wm/window_cycle_controller.cc
+++ b/ash/common/wm/window_cycle_controller.cc
@@ -39,7 +39,7 @@
 bool WindowCycleController::CanCycle() {
   // Prevent window cycling if the screen is locked or a modal dialog is open.
   WmShell* wm_shell = WmShell::Get();
-  return !wm_shell->session_controller()->IsScreenLocked() &&
+  return !Shell::Get()->session_controller()->IsScreenLocked() &&
          !wm_shell->IsSystemModalWindowOpen() && !wm_shell->IsPinned();
 }
 
diff --git a/ash/common/wm/workspace/workspace_layout_manager.cc b/ash/common/wm/workspace/workspace_layout_manager.cc
index 8ff93626..6ccf018 100644
--- a/ash/common/wm/workspace/workspace_layout_manager.cc
+++ b/ash/common/wm/workspace/workspace_layout_manager.cc
@@ -352,7 +352,7 @@
   // This would happen if the launcher was auto hidden before the login screen
   // was shown and then gets shown when the login screen gets presented.
   if (event->type() == wm::WM_EVENT_WORKAREA_BOUNDS_CHANGED &&
-      WmShell::Get()->session_controller()->IsScreenLocked())
+      Shell::Get()->session_controller()->IsScreenLocked())
     return;
 
   // If a user plugs an external display into a laptop running Aura the
diff --git a/ash/common/wm/workspace/workspace_layout_manager_unittest.cc b/ash/common/wm/workspace/workspace_layout_manager_unittest.cc
index 260edbf..9d4a3bc 100644
--- a/ash/common/wm/workspace/workspace_layout_manager_unittest.cc
+++ b/ash/common/wm/workspace/workspace_layout_manager_unittest.cc
@@ -927,7 +927,7 @@
             window_bounds.ToString());
 
   // The window size should not get touched while we are in lock screen.
-  WmShell::Get()->session_controller()->LockScreenAndFlushForTest();
+  Shell::Get()->session_controller()->LockScreenAndFlushForTest();
   ShelfLayoutManager* shelf_layout_manager = shelf->shelf_layout_manager();
   shelf_layout_manager->UpdateVisibilityState();
   EXPECT_EQ(window_bounds.ToString(), window->GetBounds().ToString());
diff --git a/ash/common/wm_shell.cc b/ash/common/wm_shell.cc
index 28108d71..a74f177 100644
--- a/ash/common/wm_shell.cc
+++ b/ash/common/wm_shell.cc
@@ -10,10 +10,6 @@
 #include "ash/common/session/session_controller.h"
 #include "ash/common/session/session_state_delegate.h"
 #include "ash/common/shelf/app_list_shelf_item_delegate.h"
-#include "ash/common/shelf/shelf_controller.h"
-#include "ash/common/shelf/shelf_delegate.h"
-#include "ash/common/shelf/shelf_model.h"
-#include "ash/common/shelf/shelf_window_watcher.h"
 #include "ash/common/shell_delegate.h"
 #include "ash/common/shutdown_controller.h"
 #include "ash/common/system/chromeos/network/vpn_list.h"
@@ -39,7 +35,6 @@
 WmShell::~WmShell() {
   DCHECK_EQ(this, instance_);
   instance_ = nullptr;
-  session_controller_->RemoveSessionStateObserver(this);
 }
 
 // static
@@ -48,27 +43,13 @@
 }
 
 void WmShell::Shutdown() {
-  // ShelfWindowWatcher has window observers and a pointer to the shelf model.
-  shelf_window_watcher_.reset();
-  // ShelfItemDelegate subclasses it owns have complex cleanup to run (e.g. ARC
-  // shelf items in Chrome) so explicitly shutdown early.
-  shelf_model()->DestroyItemDelegates();
-  // Must be destroyed before FocusClient.
-  shelf_delegate_.reset();
-
-  // Removes itself as an observer of |pref_store_|.
-  shelf_controller_.reset();
-}
-
-ShelfModel* WmShell::shelf_model() {
-  return shelf_controller_->model();
 }
 
 void WmShell::ShowContextMenu(const gfx::Point& location_in_screen,
                               ui::MenuSourceType source_type) {
   // Bail if there is no active user session or if the screen is locked.
-  if (session_controller()->NumberOfLoggedInUsers() < 1 ||
-      session_controller()->IsScreenLocked()) {
+  if (Shell::Get()->session_controller()->NumberOfLoggedInUsers() < 1 ||
+      Shell::Get()->session_controller()->IsScreenLocked()) {
     return;
   }
 
@@ -77,37 +58,6 @@
                                                    source_type);
 }
 
-void WmShell::CreateShelfView() {
-  // Must occur after SessionController creation and user login.
-  DCHECK(session_controller());
-  DCHECK_GT(session_controller()->NumberOfLoggedInUsers(), 0);
-  CreateShelfDelegate();
-
-  for (WmWindow* root_window : GetAllRootWindows())
-    root_window->GetRootWindowController()->CreateShelfView();
-}
-
-void WmShell::CreateShelfDelegate() {
-  // May be called multiple times as shelves are created and destroyed.
-  if (shelf_delegate_)
-    return;
-  // Must occur after SessionController creation and user login because
-  // Chrome's implementation of ShelfDelegate assumes it can get information
-  // about multi-profile login state.
-  DCHECK(session_controller());
-  DCHECK_GT(session_controller()->NumberOfLoggedInUsers(), 0);
-  shelf_delegate_.reset(
-      Shell::Get()->shell_delegate()->CreateShelfDelegate(shelf_model()));
-  shelf_window_watcher_.reset(new ShelfWindowWatcher(shelf_model()));
-}
-
-void WmShell::UpdateAfterLoginStatusChange(LoginStatus status) {
-  for (WmWindow* root_window : GetAllRootWindows()) {
-    root_window->GetRootWindowController()->UpdateAfterLoginStatusChange(
-        status);
-  }
-}
-
 void WmShell::OnLockStateEvent(LockStateObserver::EventType event) {
   for (auto& observer : lock_state_observers_)
     observer.OnLockStateEvent(event);
@@ -121,15 +71,8 @@
   lock_state_observers_.RemoveObserver(observer);
 }
 
-void WmShell::SetShelfDelegateForTesting(
-    std::unique_ptr<ShelfDelegate> test_delegate) {
-  shelf_delegate_ = std::move(test_delegate);
-}
-
 WmShell::WmShell()
-    : session_controller_(base::MakeUnique<SessionController>()),
-      shelf_controller_(base::MakeUnique<ShelfController>()),
-      shutdown_controller_(base::MakeUnique<ShutdownController>()),
+    : shutdown_controller_(base::MakeUnique<ShutdownController>()),
       system_tray_notifier_(base::MakeUnique<SystemTrayNotifier>()),
       vpn_list_(base::MakeUnique<VpnList>()),
       window_cycle_controller_(base::MakeUnique<WindowCycleController>()),
@@ -137,7 +80,6 @@
           base::MakeUnique<WindowSelectorController>()) {
   DCHECK(!instance_);
   instance_ = this;
-  session_controller_->AddSessionStateObserver(this);
 }
 
 RootWindowController* WmShell::GetPrimaryRootWindowController() {
@@ -200,18 +142,4 @@
   window_selector_controller_.reset();
 }
 
-void WmShell::SessionStateChanged(session_manager::SessionState state) {
-  // Create the shelf when a session becomes active. It's safe to do this
-  // multiple times (e.g. initial login vs. multiprofile add session).
-  if (state == session_manager::SessionState::ACTIVE)
-    CreateShelfView();
-
-  // Only trigger an update in mash because with classic ash chrome calls
-  // UpdateAfterLoginStatusChange() directly.
-  if (IsRunningInMash()) {
-    // TODO(jamescook): Should this call Shell::OnLoginStatusChanged() too?
-    UpdateAfterLoginStatusChange(session_controller_->GetLoginStatus());
-  }
-}
-
 }  // namespace ash
diff --git a/ash/common/wm_shell.h b/ash/common/wm_shell.h
index 0ce67ef..c5a7ad23 100644
--- a/ash/common/wm_shell.h
+++ b/ash/common/wm_shell.h
@@ -13,7 +13,6 @@
 #include "ash/ash_export.h"
 #include "ash/common/metrics/gesture_action_type.h"
 #include "ash/common/metrics/user_metrics_action.h"
-#include "ash/common/session/session_state_observer.h"
 #include "ash/common/wm/lock_state_observer.h"
 #include "base/observer_list.h"
 #include "ui/base/ui_base_types.h"
@@ -43,12 +42,7 @@
 class KeyboardUI;
 class RootWindowController;
 class ScopedDisableInternalMouseAndKeyboard;
-class SessionController;
 class SessionStateDelegate;
-class ShelfController;
-class ShelfDelegate;
-class ShelfModel;
-class ShelfWindowWatcher;
 struct ShellInitParams;
 class ShutdownController;
 class SystemTrayNotifier;
@@ -70,23 +64,15 @@
 }
 
 // Similar to ash::Shell. Eventually the two will be merged.
-class ASH_EXPORT WmShell : public SessionStateObserver {
+class ASH_EXPORT WmShell {
  public:
-  ~WmShell() override;
+  virtual ~WmShell();
 
   static WmShell* Get();
   static bool HasInstance() { return instance_ != nullptr; }
 
   virtual void Shutdown();
 
-  SessionController* session_controller() { return session_controller_.get(); }
-
-  ShelfController* shelf_controller() { return shelf_controller_.get(); }
-
-  ShelfDelegate* shelf_delegate() { return shelf_delegate_.get(); }
-
-  ShelfModel* shelf_model();
-
   ShutdownController* shutdown_controller() {
     return shutdown_controller_.get();
   }
@@ -223,16 +209,6 @@
 
   virtual std::unique_ptr<KeyEventWatcher> CreateKeyEventWatcher() = 0;
 
-  // Creates the ShelfView for each display and populates it with items.
-  // Called after the user session is active and profile is available.
-  void CreateShelfView();
-
-  void CreateShelfDelegate();
-
-  // Called when the login status changes.
-  // TODO(oshima): Investigate if we can merge this and |OnLoginStateChanged|.
-  void UpdateAfterLoginStatusChange(LoginStatus status);
-
   virtual SessionStateDelegate* GetSessionStateDelegate() = 0;
 
   virtual void AddDisplayObserver(WmDisplayObserver* observer) = 0;
@@ -253,8 +229,6 @@
   void AddLockStateObserver(LockStateObserver* observer);
   void RemoveLockStateObserver(LockStateObserver* observer);
 
-  void SetShelfDelegateForTesting(std::unique_ptr<ShelfDelegate> test_delegate);
-
   // True if any touch points are down.
   virtual bool IsTouchDown() = 0;
 
@@ -285,19 +259,12 @@
 
   void DeleteWindowSelectorController();
 
-  // SessionStateObserver:
-  void SessionStateChanged(session_manager::SessionState state) override;
-
  private:
   friend class AcceleratorControllerTest;
   friend class Shell;
 
   static WmShell* instance_;
 
-  std::unique_ptr<SessionController> session_controller_;
-  std::unique_ptr<ShelfController> shelf_controller_;
-  std::unique_ptr<ShelfDelegate> shelf_delegate_;
-  std::unique_ptr<ShelfWindowWatcher> shelf_window_watcher_;
   std::unique_ptr<ShutdownController> shutdown_controller_;
   std::unique_ptr<SystemTrayNotifier> system_tray_notifier_;
   std::unique_ptr<VpnList> vpn_list_;
diff --git a/ash/metrics/user_metrics_recorder.cc b/ash/metrics/user_metrics_recorder.cc
index 5dcbc08..3af9f7ea 100644
--- a/ash/metrics/user_metrics_recorder.cc
+++ b/ash/metrics/user_metrics_recorder.cc
@@ -169,13 +169,13 @@
 
 // Records the number of items in the shelf as an UMA statistic.
 void RecordShelfItemCounts() {
-  ShelfDelegate* shelf_delegate = WmShell::Get()->shelf_delegate();
+  ShelfDelegate* shelf_delegate = Shell::Get()->shelf_delegate();
   DCHECK(shelf_delegate);
 
   int pinned_item_count = 0;
   int unpinned_item_count = 0;
 
-  for (const ShelfItem& shelf_item : WmShell::Get()->shelf_model()->items()) {
+  for (const ShelfItem& shelf_item : Shell::Get()->shelf_model()->items()) {
     if (shelf_item.type != TYPE_APP_LIST) {
       // Internal ash apps do not have an app id and thus will always be counted
       // as unpinned.
diff --git a/ash/metrics/user_metrics_recorder_unittest.cc b/ash/metrics/user_metrics_recorder_unittest.cc
index 8351032..bdabfb09 100644
--- a/ash/metrics/user_metrics_recorder_unittest.cc
+++ b/ash/metrics/user_metrics_recorder_unittest.cc
@@ -12,6 +12,7 @@
 #include "ash/common/test/test_system_tray_delegate.h"
 #include "ash/common/wm_shell.h"
 #include "ash/common/wm_window.h"
+#include "ash/shell.h"
 #include "ash/test/ash_test_base.h"
 #include "ash/test/user_metrics_recorder_test_api.h"
 #include "base/test/histogram_tester.h"
@@ -196,7 +197,7 @@
   SetUserInActiveDesktopEnvironment(true);
 
   // Make sure the shelf contains the app list launcher button.
-  const ShelfItems& shelf_items = WmShell::Get()->shelf_model()->items();
+  const ShelfItems& shelf_items = Shell::Get()->shelf_model()->items();
   ASSERT_EQ(1u, shelf_items.size());
   ASSERT_EQ(TYPE_APP_LIST, shelf_items[0].type);
 
diff --git a/ash/mus/shelf_delegate_mus.cc b/ash/mus/shelf_delegate_mus.cc
index 3f0e6d8..90dd0f91 100644
--- a/ash/mus/shelf_delegate_mus.cc
+++ b/ash/mus/shelf_delegate_mus.cc
@@ -5,7 +5,7 @@
 #include "ash/mus/shelf_delegate_mus.h"
 
 #include "ash/common/shelf/shelf_controller.h"
-#include "ash/common/wm_shell.h"
+#include "ash/shell.h"
 #include "base/strings/string_util.h"
 
 namespace ash {
@@ -18,8 +18,8 @@
 // ShelfDelegate:
 
 ShelfID ShelfDelegateMus::GetShelfIDForAppID(const std::string& app_id) {
-  if (WmShell::Get()->shelf_controller()->app_id_to_shelf_id().count(app_id))
-    return WmShell::Get()->shelf_controller()->app_id_to_shelf_id().at(app_id);
+  if (Shell::Get()->shelf_controller()->app_id_to_shelf_id().count(app_id))
+    return Shell::Get()->shelf_controller()->app_id_to_shelf_id().at(app_id);
   return 0;
 }
 
@@ -30,12 +30,12 @@
 }
 
 bool ShelfDelegateMus::HasShelfIDToAppIDMapping(ShelfID id) const {
-  return WmShell::Get()->shelf_controller()->shelf_id_to_app_id().count(id) > 0;
+  return Shell::Get()->shelf_controller()->shelf_id_to_app_id().count(id) > 0;
 }
 
 const std::string& ShelfDelegateMus::GetAppIDForShelfID(ShelfID id) {
-  if (WmShell::Get()->shelf_controller()->shelf_id_to_app_id().count(id))
-    return WmShell::Get()->shelf_controller()->shelf_id_to_app_id().at(id);
+  if (Shell::Get()->shelf_controller()->shelf_id_to_app_id().count(id))
+    return Shell::Get()->shelf_controller()->shelf_id_to_app_id().at(id);
   return base::EmptyString();
 }
 
diff --git a/ash/mus/system_tray_delegate_mus.cc b/ash/mus/system_tray_delegate_mus.cc
index 056c7f08..95fe6bb 100644
--- a/ash/mus/system_tray_delegate_mus.cc
+++ b/ash/mus/system_tray_delegate_mus.cc
@@ -6,7 +6,7 @@
 
 #include "ash/common/session/session_controller.h"
 #include "ash/common/system/networking_config_delegate.h"
-#include "ash/common/wm_shell.h"
+#include "ash/shell.h"
 
 namespace ash {
 namespace {
@@ -37,7 +37,7 @@
 }
 
 LoginStatus SystemTrayDelegateMus::GetUserLoginStatus() const {
-  return WmShell::Get()->session_controller()->GetLoginStatus();
+  return Shell::Get()->session_controller()->GetLoginStatus();
 }
 
 NetworkingConfigDelegate* SystemTrayDelegateMus::GetNetworkingConfigDelegate()
diff --git a/ash/mus/test/wm_test_base.cc b/ash/mus/test/wm_test_base.cc
index 487a1caa..1054ec4 100644
--- a/ash/mus/test/wm_test_base.cc
+++ b/ash/mus/test/wm_test_base.cc
@@ -8,13 +8,13 @@
 #include <vector>
 
 #include "ash/common/session/session_controller.h"
-#include "ash/common/wm_shell.h"
 #include "ash/mus/test/wm_test_helper.h"
 #include "ash/mus/top_level_window_factory.h"
 #include "ash/mus/window_manager.h"
 #include "ash/mus/window_manager_application.h"
 #include "ash/public/cpp/session_types.h"
 #include "ash/public/interfaces/session_controller.mojom.h"
+#include "ash/shell.h"
 #include "ash/test/wm_window_test_api.h"
 #include "base/memory/ptr_util.h"
 #include "services/ui/public/cpp/property_type_converters.h"
@@ -180,7 +180,7 @@
 }
 
 void WmTestBase::SimulateUserLogin() {
-  SessionController* session_controller = WmShell::Get()->session_controller();
+  SessionController* session_controller = Shell::Get()->session_controller();
 
   // Simulate the first user logging in.
   mojom::UserSessionPtr session = mojom::UserSession::New();
diff --git a/ash/root_window_controller.cc b/ash/root_window_controller.cc
index df248331..6ba028a 100644
--- a/ash/root_window_controller.cc
+++ b/ash/root_window_controller.cc
@@ -399,7 +399,7 @@
     }
   } else {
     int modal_window_id =
-        WmShell::Get()->session_controller()->IsUserSessionBlocked()
+        Shell::Get()->session_controller()->IsUserSessionBlocked()
             ? kShellWindowId_LockSystemModalContainer
             : kShellWindowId_SystemModalContainer;
     modal_container = GetWmContainer(modal_window_id);
@@ -433,7 +433,7 @@
   aura::Window* blocking_container = nullptr;
 
   int modal_container_id = 0;
-  if (WmShell::Get()->session_controller()->IsUserSessionBlocked()) {
+  if (Shell::Get()->session_controller()->IsUserSessionBlocked()) {
     blocking_container =
         GetContainer(kShellWindowId_LockScreenContainersContainer);
     modal_container_id = kShellWindowId_LockSystemModalContainer;
@@ -778,7 +778,7 @@
     window_tree_host_->Show();
 
     // Create a shelf if a user is already logged in.
-    if (wm_shell->session_controller()->NumberOfLoggedInUsers())
+    if (shell->session_controller()->NumberOfLoggedInUsers())
       CreateShelfView();
 
     // Notify shell observers about new root window.
diff --git a/ash/root_window_controller_unittest.cc b/ash/root_window_controller_unittest.cc
index cdcfc0a..c6e08ca 100644
--- a/ash/root_window_controller_unittest.cc
+++ b/ash/root_window_controller_unittest.cc
@@ -397,7 +397,7 @@
             controller->GetSystemModalLayoutManager(
                 WmWindow::Get(session_modal_widget->GetNativeWindow())));
 
-  wm_shell->session_controller()->LockScreenAndFlushForTest();
+  Shell::Get()->session_controller()->LockScreenAndFlushForTest();
   EXPECT_EQ(LoginStatus::LOCKED,
             Shell::Get()->system_tray_delegate()->GetUserLoginStatus());
   EXPECT_EQ(
@@ -423,7 +423,7 @@
   UpdateDisplay("600x600");
 
   // Configure login screen environment.
-  SessionController* session_controller = WmShell::Get()->session_controller();
+  SessionController* session_controller = Shell::Get()->session_controller();
   SetUserLoggedIn(false);
   EXPECT_EQ(LoginStatus::NOT_LOGGED_IN,
             Shell::Get()->system_tray_delegate()->GetUserLoginStatus());
@@ -1004,7 +1004,7 @@
   // Mock a login user profile change to reinitialize the keyboard.
   mojom::SessionInfoPtr info = mojom::SessionInfo::New();
   info->state = session_manager::SessionState::ACTIVE;
-  WmShell::Get()->session_controller()->SetSessionInfo(std::move(info));
+  Shell::Get()->session_controller()->SetSessionInfo(std::move(info));
   EXPECT_EQ(display::Screen::GetScreen()->GetPrimaryDisplay().work_area(),
             before);
 }
diff --git a/ash/shelf/shelf_layout_manager_unittest.cc b/ash/shelf/shelf_layout_manager_unittest.cc
index e227b6db..0f67f728 100644
--- a/ash/shelf/shelf_layout_manager_unittest.cc
+++ b/ash/shelf/shelf_layout_manager_unittest.cc
@@ -293,14 +293,14 @@
   void LockScreen() {
     mojom::SessionInfoPtr info = mojom::SessionInfo::New();
     info->state = session_manager::SessionState::LOCKED;
-    ash::WmShell::Get()->session_controller()->SetSessionInfo(std::move(info));
+    ash::Shell::Get()->session_controller()->SetSessionInfo(std::move(info));
   }
 
   // Turn off the lock screen.
   void UnlockScreen() {
     mojom::SessionInfoPtr info = mojom::SessionInfo::New();
     info->state = session_manager::SessionState::ACTIVE;
-    ash::WmShell::Get()->session_controller()->SetSessionInfo(std::move(info));
+    ash::Shell::Get()->session_controller()->SetSessionInfo(std::move(info));
   }
 
   int64_t GetPrimaryDisplayId() {
@@ -845,7 +845,7 @@
 
   mojom::SessionInfoPtr info = mojom::SessionInfo::New();
   info->state = session_manager::SessionState::LOGIN_PRIMARY;
-  ash::WmShell::Get()->session_controller()->SetSessionInfo(std::move(info));
+  ash::Shell::Get()->session_controller()->SetSessionInfo(std::move(info));
 
   EXPECT_EQ(SHELF_VISIBLE, shelf->GetVisibilityState());
   EXPECT_EQ(SHELF_BACKGROUND_OVERLAP, GetShelfWidget()->GetBackgroundType());
diff --git a/ash/shelf/shelf_view_unittest.cc b/ash/shelf/shelf_view_unittest.cc
index 68d28cb..e6cf81d 100644
--- a/ash/shelf/shelf_view_unittest.cc
+++ b/ash/shelf/shelf_view_unittest.cc
@@ -33,6 +33,7 @@
 #include "ash/test/ash_test_helper.h"
 #include "ash/test/overflow_bubble_view_test_api.h"
 #include "ash/test/shelf_view_test_api.h"
+#include "ash/test/shell_test_api.h"
 #include "ash/test/test_shell_delegate.h"
 #include "base/i18n/rtl.h"
 #include "base/macros.h"
@@ -300,7 +301,7 @@
 
   void SetUp() override {
     AshTestBase::SetUp();
-    model_ = WmShell::Get()->shelf_model();
+    model_ = Shell::Get()->shelf_model();
     shelf_view_ = GetPrimaryShelf()->GetShelfViewForTesting();
 
     WebNotificationTray::DisableAnimationsForTest(true);
@@ -683,11 +684,10 @@
 
   void ReplaceShelfDelegate() {
     // Clear the value first to avoid TestShelfDelegate's singleton check.
-    WmShell::Get()->SetShelfDelegateForTesting(nullptr);
+    test::ShellTestApi().SetShelfDelegate(nullptr);
     shelf_delegate_ = new TestShelfDelegateForShelfView();
     test_api_->SetShelfDelegate(shelf_delegate_);
-    WmShell::Get()->SetShelfDelegateForTesting(
-        base::WrapUnique(shelf_delegate_));
+    test::ShellTestApi().SetShelfDelegate(base::WrapUnique(shelf_delegate_));
   }
 
   ShelfModel* model_;
diff --git a/ash/shell.cc b/ash/shell.cc
index 42fc691..b0802141 100644
--- a/ash/shell.cc
+++ b/ash/shell.cc
@@ -29,7 +29,12 @@
 #include "ash/common/media_controller.h"
 #include "ash/common/new_window_controller.h"
 #include "ash/common/palette_delegate.h"
+#include "ash/common/session/session_controller.h"
 #include "ash/common/session/session_state_delegate.h"
+#include "ash/common/shelf/shelf_controller.h"
+#include "ash/common/shelf/shelf_delegate.h"
+#include "ash/common/shelf/shelf_model.h"
+#include "ash/common/shelf/shelf_window_watcher.h"
 #include "ash/common/shelf/wm_shelf.h"
 #include "ash/common/shell_delegate.h"
 #include "ash/common/shell_observer.h"
@@ -371,6 +376,10 @@
       resolution_notification_controller_->DoesNotificationTimeout());
 }
 
+ShelfModel* Shell::shelf_model() {
+  return shelf_controller_->model();
+}
+
 aura::client::ActivationClient* Shell::activation_client() {
   return focus_controller_.get();
 }
@@ -407,6 +416,16 @@
   return new FirstRunHelperImpl;
 }
 
+void Shell::CreateShelfView() {
+  // Must occur after SessionController creation and user login.
+  DCHECK(session_controller());
+  DCHECK_GT(session_controller()->NumberOfLoggedInUsers(), 0);
+  CreateShelfDelegate();
+
+  for (WmWindow* root_window : wm_shell_->GetAllRootWindows())
+    root_window->GetRootWindowController()->CreateShelfView();
+}
+
 void Shell::SetLargeCursorSizeInDip(int large_cursor_size_in_dip) {
   window_tree_host_manager_->cursor_window_controller()
       ->SetLargeCursorSizeInDip(large_cursor_size_in_dip);
@@ -456,6 +475,13 @@
   return app_list_->GetTargetVisibility();
 }
 
+void Shell::UpdateAfterLoginStatusChange(LoginStatus status) {
+  for (WmWindow* root_window : wm_shell_->GetAllRootWindows()) {
+    root_window->GetRootWindowController()->UpdateAfterLoginStatusChange(
+        status);
+  }
+}
+
 void Shell::NotifyMaximizeModeStarted() {
   for (auto& observer : shell_observers_)
     observer.OnMaximizeModeStarted();
@@ -530,6 +556,8 @@
           base::MakeUnique<LocaleNotificationController>()),
       media_controller_(base::MakeUnique<MediaController>()),
       new_window_controller_(base::MakeUnique<NewWindowController>()),
+      session_controller_(base::MakeUnique<SessionController>()),
+      shelf_controller_(base::MakeUnique<ShelfController>()),
       shell_delegate_(std::move(shell_delegate)),
       system_tray_controller_(base::MakeUnique<SystemTrayController>()),
       app_list_(base::MakeUnique<app_list::AppList>()),
@@ -550,6 +578,8 @@
   }
 
   PowerStatus::Initialize();
+
+  session_controller_->AddSessionStateObserver(this);
 }
 
 Shell::~Shell() {
@@ -688,7 +718,21 @@
   // Balances the Install() in Initialize().
   views::FocusManagerFactory::Install(nullptr);
 
+  // ShelfWindowWatcher has window observers and a pointer to the shelf model.
+  shelf_window_watcher_.reset();
+
+  // ShelfItemDelegate subclasses it owns have complex cleanup to run (e.g. ARC
+  // shelf items in Chrome) so explicitly shutdown early.
+  shelf_model()->DestroyItemDelegates();
+
+  // Must be destroyed before FocusController.
+  shelf_delegate_.reset();
+
+  // Removes itself as an observer of |pref_store_|.
+  shelf_controller_.reset();
+
   wm_shell_->Shutdown();
+
   // Depends on |focus_controller_|, so must be destroyed before.
   window_tree_host_manager_.reset();
   focus_controller_->RemoveObserver(this);
@@ -714,6 +758,7 @@
 
   // Needs to happen right before |instance_| is reset.
   wm_shell_.reset();
+  session_controller_->RemoveSessionStateObserver(this);
   wallpaper_delegate_.reset();
   pref_store_ = nullptr;
   shell_delegate_.reset();
@@ -1110,6 +1155,19 @@
   }
 }
 
+void Shell::CreateShelfDelegate() {
+  // May be called multiple times as shelves are created and destroyed.
+  if (shelf_delegate_)
+    return;
+  // Must occur after SessionController creation and user login because
+  // Chrome's implementation of ShelfDelegate assumes it can get information
+  // about multi-profile login state.
+  DCHECK(session_controller());
+  DCHECK_GT(session_controller()->NumberOfLoggedInUsers(), 0);
+  shelf_delegate_.reset(shell_delegate_->CreateShelfDelegate(shelf_model()));
+  shelf_window_watcher_ = base::MakeUnique<ShelfWindowWatcher>(shelf_model());
+}
+
 bool Shell::CanWindowReceiveEvents(aura::Window* window) {
   RootWindowControllerList controllers = GetAllRootWindowControllers();
   for (RootWindowController* controller : controllers) {
@@ -1148,4 +1206,24 @@
     root_window_for_new_windows_ = gained_active_wm->GetRootWindow();
 }
 
+void Shell::SessionStateChanged(session_manager::SessionState state) {
+  // Create the shelf when a session becomes active. It's safe to do this
+  // multiple times (e.g. initial login vs. multiprofile add session).
+  if (state == session_manager::SessionState::ACTIVE) {
+    CreateShelfView();
+
+    if (!wm_shell_->IsRunningInMash()) {
+      // Recreate the keyboard after initial login and after multiprofile login.
+      CreateKeyboard();
+    }
+  }
+
+  // Only trigger an update in mash because with classic ash chrome calls
+  // UpdateAfterLoginStatusChange() directly.
+  if (wm_shell_->IsRunningInMash()) {
+    // TODO(jamescook): Should this call Shell::OnLoginStatusChanged() too?
+    UpdateAfterLoginStatusChange(session_controller_->GetLoginStatus());
+  }
+}
+
 }  // namespace ash
diff --git a/ash/shell.h b/ash/shell.h
index 45fb327..343af630 100644
--- a/ash/shell.h
+++ b/ash/shell.h
@@ -10,6 +10,7 @@
 #include <vector>
 
 #include "ash/ash_export.h"
+#include "ash/common/session/session_state_observer.h"
 #include "ash/metrics/user_metrics_recorder.h"
 #include "ash/public/cpp/shelf_types.h"
 #include "ash/wm/cursor_manager_chromeos.h"
@@ -138,7 +139,12 @@
 class ScreenshotController;
 class ScreenPinningController;
 class ScreenPositionController;
+class SessionController;
 class SessionStateDelegate;
+class ShelfController;
+class ShelfDelegate;
+class ShelfModel;
+class ShelfWindowWatcher;
 class ShellDelegate;
 struct ShellInitParams;
 class ShellObserver;
@@ -178,7 +184,8 @@
 //
 // Upon creation, the Shell sets itself as the RootWindow's delegate, which
 // takes ownership of the Shell.
-class ASH_EXPORT Shell : public SystemModalContainerEventFilterDelegate,
+class ASH_EXPORT Shell : public SessionStateObserver,
+                         public SystemModalContainerEventFilterDelegate,
                          public ui::EventTarget,
                          public aura::client::ActivationChangeObserver {
  public:
@@ -326,6 +333,10 @@
   NewWindowController* new_window_controller() {
     return new_window_controller_.get();
   }
+  SessionController* session_controller() { return session_controller_.get(); }
+  ShelfController* shelf_controller() { return shelf_controller_.get(); }
+  ShelfDelegate* shelf_delegate() { return shelf_delegate_.get(); }
+  ShelfModel* shelf_model();
   SystemTrayController* system_tray_controller() {
     return system_tray_controller_.get();
   }
@@ -483,6 +494,10 @@
   // returned object.
   ash::FirstRunHelper* CreateFirstRunHelper();
 
+  // Creates the ShelfView for each display and populates it with items.
+  // Called after the user session is active and profile is available.
+  void CreateShelfView();
+
   void SetLargeCursorSizeInDip(int large_cursor_size_in_dip);
 
   // Toggles cursor compositing on/off. Native cursor is disabled when cursor
@@ -518,6 +533,10 @@
   // Returns app list target visibility.
   bool GetAppListTargetVisibility() const;
 
+  // Called when the login status changes.
+  // TODO(oshima): Investigate if we can merge this and |OnLoginStateChanged|.
+  void UpdateAfterLoginStatusChange(LoginStatus status);
+
   // Notifies observers that maximize mode has started, windows might still
   // animate.
   void NotifyMaximizeModeStarted();
@@ -584,6 +603,8 @@
   void SetSystemTrayDelegate(std::unique_ptr<SystemTrayDelegate> delegate);
   void DeleteSystemTrayDelegate();
 
+  void CreateShelfDelegate();
+
   // Destroys all child windows including widgets across all roots.
   void CloseAllRootWindowChildWindows();
 
@@ -601,6 +622,9 @@
                          aura::Window* gained_active,
                          aura::Window* lost_active) override;
 
+  // SessionStateObserver:
+  void SessionStateChanged(session_manager::SessionState state) override;
+
   static Shell* instance_;
 
   // Only valid in mash, for classic ash this is null.
@@ -640,6 +664,10 @@
   std::unique_ptr<NewWindowController> new_window_controller_;
   std::unique_ptr<PaletteDelegate> palette_delegate_;
   std::unique_ptr<ResizeShadowController> resize_shadow_controller_;
+  std::unique_ptr<SessionController> session_controller_;
+  std::unique_ptr<ShelfController> shelf_controller_;
+  std::unique_ptr<ShelfDelegate> shelf_delegate_;
+  std::unique_ptr<ShelfWindowWatcher> shelf_window_watcher_;
   std::unique_ptr<ShellDelegate> shell_delegate_;
   std::unique_ptr<SystemTrayController> system_tray_controller_;
   std::unique_ptr<SystemTrayDelegate> system_tray_delegate_;
diff --git a/ash/shell/app_list.cc b/ash/shell/app_list.cc
index 58ca087..983a6a0 100644
--- a/ash/shell/app_list.cc
+++ b/ash/shell/app_list.cc
@@ -112,7 +112,7 @@
         break;
       }
       case LOCK_SCREEN: {
-        WmShell::Get()->session_controller()->LockScreen();
+        Shell::Get()->session_controller()->LockScreen();
         break;
       }
       case WIDGETS_WINDOW: {
diff --git a/ash/shell/content/client/shell_browser_main_parts.cc b/ash/shell/content/client/shell_browser_main_parts.cc
index a5183b50..b61ace57 100644
--- a/ash/shell/content/client/shell_browser_main_parts.cc
+++ b/ash/shell/content/client/shell_browser_main_parts.cc
@@ -137,11 +137,11 @@
   // creating shelf.
   example_session_controller_client_ =
       base::MakeUnique<ExampleSessionControllerClient>(
-          WmShell::Get()->session_controller());
+          Shell::Get()->session_controller());
   example_session_controller_client_->Initialize();
 
-  ash::WmShell::Get()->CreateShelfView();
-  ash::WmShell::Get()->UpdateAfterLoginStatusChange(LoginStatus::USER);
+  ash::Shell::Get()->CreateShelfView();
+  ash::Shell::Get()->UpdateAfterLoginStatusChange(LoginStatus::USER);
 
   window_watcher_.reset(new ash::shell::WindowWatcher);
   display::Screen::GetScreen()->AddObserver(window_watcher_.get());
diff --git a/ash/shell/window_type_launcher.cc b/ash/shell/window_type_launcher.cc
index 5bc6d668..1078aed 100644
--- a/ash/shell/window_type_launcher.cc
+++ b/ash/shell/window_type_launcher.cc
@@ -269,7 +269,7 @@
   } else if (sender == bubble_button_) {
     CreatePointyBubble(sender);
   } else if (sender == lock_button_) {
-    WmShell::Get()->session_controller()->LockScreen();
+    Shell::Get()->session_controller()->LockScreen();
   } else if (sender == widgets_button_) {
     CreateWidgetsWindow();
   } else if (sender == system_modal_button_) {
diff --git a/ash/shell/window_watcher.cc b/ash/shell/window_watcher.cc
index 57f98c0c..196997c 100644
--- a/ash/shell/window_watcher.cc
+++ b/ash/shell/window_watcher.cc
@@ -97,7 +97,7 @@
     return;
 
   static int image_count = 0;
-  ShelfModel* model = WmShell::Get()->shelf_model();
+  ShelfModel* model = Shell::Get()->shelf_model();
   ShelfItem item;
   item.type = new_window->type() == ui::wm::WINDOW_TYPE_PANEL
                   ? ash::TYPE_APP_PANEL
@@ -124,7 +124,7 @@
   for (IDToWindow::iterator i = id_to_window_.begin(); i != id_to_window_.end();
        ++i) {
     if (i->second == window) {
-      ShelfModel* model = WmShell::Get()->shelf_model();
+      ShelfModel* model = Shell::Get()->shelf_model();
       int index = model->ItemIndexByID(i->first);
       DCHECK_NE(-1, index);
       model->RemoveItemAt(index);
diff --git a/ash/shell_unittest.cc b/ash/shell_unittest.cc
index a505fee3..76e8528 100644
--- a/ash/shell_unittest.cc
+++ b/ash/shell_unittest.cc
@@ -164,7 +164,7 @@
     // Create a LockScreen window.
     views::Widget::InitParams widget_params(
         views::Widget::InitParams::TYPE_WINDOW);
-    SessionController* controller = WmShell::Get()->session_controller();
+    SessionController* controller = Shell::Get()->session_controller();
     controller->LockScreenAndFlushForTest();
     views::Widget* lock_widget = CreateTestWindow(widget_params);
     Shell::GetContainer(Shell::GetPrimaryRootWindow(),
@@ -278,7 +278,7 @@
   EXPECT_TRUE(
       GetDefaultContainer()->Contains(widget->GetNativeWindow()->parent()));
 
-  WmShell::Get()->session_controller()->LockScreenAndFlushForTest();
+  Shell::Get()->session_controller()->LockScreenAndFlushForTest();
   // Create a LockScreen window.
   views::Widget* lock_widget = CreateTestWindow(widget_params);
   Shell::GetContainer(Shell::GetPrimaryRootWindow(),
@@ -335,7 +335,7 @@
 }
 
 TEST_F(ShellTest, IsScreenLocked) {
-  SessionController* controller = WmShell::Get()->session_controller();
+  SessionController* controller = Shell::Get()->session_controller();
   controller->LockScreenAndFlushForTest();
   EXPECT_TRUE(controller->IsScreenLocked());
   GetSessionControllerClient()->UnlockScreen();
diff --git a/ash/system/chromeos/power/power_event_observer.cc b/ash/system/chromeos/power/power_event_observer.cc
index 3441bc1..88454e0 100644
--- a/ash/system/chromeos/power/power_event_observer.cc
+++ b/ash/system/chromeos/power/power_event_observer.cc
@@ -76,7 +76,7 @@
 }
 
 void PowerEventObserver::SuspendImminent() {
-  SessionController* controller = WmShell::Get()->session_controller();
+  SessionController* controller = Shell::Get()->session_controller();
 
   // This class is responsible for disabling all rendering requests at suspend
   // time and then enabling them at resume time.  When the
diff --git a/ash/system/chromeos/power/tablet_power_button_controller.cc b/ash/system/chromeos/power/tablet_power_button_controller.cc
index fb8f60a..29c586a9 100644
--- a/ash/system/chromeos/power/tablet_power_button_controller.cc
+++ b/ash/system/chromeos/power/tablet_power_button_controller.cc
@@ -252,7 +252,7 @@
 }
 
 void TabletPowerButtonController::LockScreenIfRequired() {
-  SessionController* session_controller = WmShell::Get()->session_controller();
+  SessionController* session_controller = Shell::Get()->session_controller();
   if (session_controller->ShouldLockScreenAutomatically() &&
       session_controller->CanLockScreen() &&
       !session_controller->IsUserSessionBlocked() &&
diff --git a/ash/system/chromeos/power/tablet_power_button_controller_unittest.cc b/ash/system/chromeos/power/tablet_power_button_controller_unittest.cc
index a1bdc72..ae059342 100644
--- a/ash/system/chromeos/power/tablet_power_button_controller_unittest.cc
+++ b/ash/system/chromeos/power/tablet_power_button_controller_unittest.cc
@@ -112,7 +112,7 @@
     // LockScreen is an async mojo call. Spin message loop to ensure it is
     // delivered.
     SessionController* const session_controller =
-        WmShell::Get()->session_controller();
+        Shell::Get()->session_controller();
     session_controller->FlushMojoForTest();
     return session_controller->IsScreenLocked();
   }
diff --git a/ash/system/chromeos/power/video_activity_notifier.cc b/ash/system/chromeos/power/video_activity_notifier.cc
index 030e768..09360e6 100644
--- a/ash/system/chromeos/power/video_activity_notifier.cc
+++ b/ash/system/chromeos/power/video_activity_notifier.cc
@@ -23,8 +23,7 @@
 VideoActivityNotifier::VideoActivityNotifier(VideoDetector* detector)
     : detector_(detector),
       video_state_(detector->state()),
-      screen_is_locked_(
-          WmShell::Get()->session_controller()->IsScreenLocked()) {
+      screen_is_locked_(Shell::Get()->session_controller()->IsScreenLocked()) {
   detector_->AddObserver(this);
   Shell::GetInstance()->AddShellObserver(this);
 
diff --git a/ash/system/overview/overview_button_tray_unittest.cc b/ash/system/overview/overview_button_tray_unittest.cc
index 0fb2763..51ab690 100644
--- a/ash/system/overview/overview_button_tray_unittest.cc
+++ b/ash/system/overview/overview_button_tray_unittest.cc
@@ -74,7 +74,7 @@
 
 void OverviewButtonTrayTest::NotifySessionStateChanged() {
   GetTray()->SessionStateChanged(
-      WmShell::Get()->session_controller()->GetSessionState());
+      Shell::Get()->session_controller()->GetSessionState());
 }
 
 // Ensures that creation doesn't cause any crashes and adds the image icon.
@@ -173,11 +173,11 @@
   Shell::Get()->maximize_mode_controller()->EnableMaximizeModeWindowManager(
       true);
   SetUserLoggedIn(false);
-  WmShell::Get()->UpdateAfterLoginStatusChange(LoginStatus::NOT_LOGGED_IN);
+  Shell::Get()->UpdateAfterLoginStatusChange(LoginStatus::NOT_LOGGED_IN);
   EXPECT_FALSE(GetTray()->visible());
   SetUserLoggedIn(true);
   SetSessionStarted(true);
-  WmShell::Get()->UpdateAfterLoginStatusChange(LoginStatus::USER);
+  Shell::Get()->UpdateAfterLoginStatusChange(LoginStatus::USER);
   EXPECT_TRUE(GetTray()->visible());
   SetUserAddingScreenRunning(true);
   NotifySessionStateChanged();
diff --git a/ash/test/ash_test_base.cc b/ash/test/ash_test_base.cc
index d9e13ce82..d98b4a6 100644
--- a/ash/test/ash_test_base.cc
+++ b/ash/test/ash_test_base.cc
@@ -339,7 +339,7 @@
   switch (block_reason) {
     case BLOCKED_BY_LOCK_SCREEN:
       SetSessionStarted(true);
-      WmShell::Get()->session_controller()->LockScreenAndFlushForTest();
+      Shell::Get()->session_controller()->LockScreenAndFlushForTest();
       Shell::GetInstance()->OnLockStateChanged(true);
       break;
     case BLOCKED_BY_LOGIN_SCREEN:
diff --git a/ash/test/ash_test_helper.cc b/ash/test/ash_test_helper.cc
index 00eb87dd..3bb9925 100644
--- a/ash/test/ash_test_helper.cc
+++ b/ash/test/ash_test_helper.cc
@@ -142,7 +142,7 @@
       std::unique_ptr<aura::InputStateLookup>());
 
   session_controller_client_.reset(
-      new TestSessionControllerClient(WmShell::Get()->session_controller()));
+      new TestSessionControllerClient(Shell::Get()->session_controller()));
   session_controller_client_->InitializeAndBind();
 
   Shell* shell = Shell::GetInstance();
diff --git a/ash/test/shell_test_api.cc b/ash/test/shell_test_api.cc
index eea718c..c81507e 100644
--- a/ash/test/shell_test_api.cc
+++ b/ash/test/shell_test_api.cc
@@ -3,8 +3,10 @@
 // found in the LICENSE file.
 
 #include "ash/test/shell_test_api.h"
+
 #include "ash/common/palette_delegate.h"
 #include "ash/common/session/session_state_delegate.h"
+#include "ash/common/shelf/shelf_delegate.h"
 #include "ash/root_window_controller.h"
 #include "ash/shell.h"
 
@@ -45,5 +47,10 @@
   shell_->session_state_delegate_.reset(session_state_delegate);
 }
 
+void ShellTestApi::SetShelfDelegate(
+    std::unique_ptr<ShelfDelegate> test_delegate) {
+  shell_->shelf_delegate_ = std::move(test_delegate);
+}
+
 }  // namespace test
 }  // namespace ash
diff --git a/ash/test/shell_test_api.h b/ash/test/shell_test_api.h
index f1eb5d4..0d1364c 100644
--- a/ash/test/shell_test_api.h
+++ b/ash/test/shell_test_api.h
@@ -16,6 +16,7 @@
 class PaletteDelegate;
 class SessionStateDelegate;
 class ScreenPositionController;
+class ShelfDelegate;
 class Shell;
 class SystemGestureEventFilter;
 class WorkspaceController;
@@ -37,6 +38,7 @@
 
   void SetPaletteDelegate(std::unique_ptr<PaletteDelegate> palette_delegate);
   void SetSessionStateDelegate(SessionStateDelegate* session_state_delegate);
+  void SetShelfDelegate(std::unique_ptr<ShelfDelegate> test_delegate);
 
  private:
   Shell* shell_;  // not owned
diff --git a/ash/wm/ash_focus_rules_unittest.cc b/ash/wm/ash_focus_rules_unittest.cc
index 8e847fe..0909d33b 100644
--- a/ash/wm/ash_focus_rules_unittest.cc
+++ b/ash/wm/ash_focus_rules_unittest.cc
@@ -94,7 +94,7 @@
     AshTestBase::SetUp();
     ash_test_helper()->set_test_session_controller_client(
         base::MakeUnique<LockScreenSessionControllerClient>(
-            WmShell::Get()->session_controller()));
+            Shell::Get()->session_controller()));
   }
 
   void TearDown() override { AshTestBase::TearDown(); }
@@ -163,7 +163,7 @@
 
   BlockUserSession(BLOCKED_BY_LOCK_SCREEN);
 
-  EXPECT_TRUE(WmShell::Get()->session_controller()->IsScreenLocked());
+  EXPECT_TRUE(Shell::Get()->session_controller()->IsScreenLocked());
   EXPECT_FALSE(normal_window->HasFocus());
   EXPECT_FALSE(always_on_top_window->HasFocus());
   EXPECT_FALSE(normal_window_state->IsMinimized());
@@ -173,7 +173,7 @@
 
   UnblockUserSession();
 
-  EXPECT_FALSE(WmShell::Get()->session_controller()->IsScreenLocked());
+  EXPECT_FALSE(Shell::Get()->session_controller()->IsScreenLocked());
   EXPECT_FALSE(normal_window_state->IsMinimized());
   EXPECT_FALSE(always_on_top_window_state->IsMinimized());
   EXPECT_TRUE(normal_window_state->CanActivate());
@@ -186,7 +186,7 @@
 // view doesn't get focused if the widget shows behind the lock screen.
 TEST_F(LockScreenAshFocusRulesTest, PreventFocusChangeWithLockScreenPresent) {
   BlockUserSession(BLOCKED_BY_LOCK_SCREEN);
-  EXPECT_TRUE(WmShell::Get()->session_controller()->IsScreenLocked());
+  EXPECT_TRUE(Shell::Get()->session_controller()->IsScreenLocked());
 
   views::test::TestInitialFocusWidgetDelegate delegate(CurrentContext());
   EXPECT_FALSE(delegate.view()->HasFocus());
@@ -195,7 +195,7 @@
   EXPECT_FALSE(delegate.view()->HasFocus());
 
   UnblockUserSession();
-  EXPECT_FALSE(WmShell::Get()->session_controller()->IsScreenLocked());
+  EXPECT_FALSE(Shell::Get()->session_controller()->IsScreenLocked());
   EXPECT_TRUE(delegate.GetWidget()->IsActive());
   EXPECT_TRUE(delegate.view()->HasFocus());
 }
diff --git a/ash/wm/event_client_impl.cc b/ash/wm/event_client_impl.cc
index fb18625..69de5326 100644
--- a/ash/wm/event_client_impl.cc
+++ b/ash/wm/event_client_impl.cc
@@ -23,7 +23,7 @@
   // remove this.
   const aura::Window* root_window = window ? window->GetRootWindow() : NULL;
   if (!root_window ||
-      !WmShell::Get()->session_controller()->IsUserSessionBlocked()) {
+      !Shell::Get()->session_controller()->IsUserSessionBlocked()) {
     return true;
   }
 
diff --git a/ash/wm/lock_state_controller_unittest.cc b/ash/wm/lock_state_controller_unittest.cc
index a1484cf1..5c18242 100644
--- a/ash/wm/lock_state_controller_unittest.cc
+++ b/ash/wm/lock_state_controller_unittest.cc
@@ -277,13 +277,13 @@
   void ExpectUnlockedState() {
     SCOPED_TRACE("Failure in ExpectUnlockedState");
     EXPECT_EQ(0u, test_animator_->GetAnimationCount());
-    EXPECT_FALSE(WmShell::Get()->session_controller()->IsScreenLocked());
+    EXPECT_FALSE(Shell::Get()->session_controller()->IsScreenLocked());
   }
 
   void ExpectLockedState() {
     SCOPED_TRACE("Failure in ExpectLockedState");
     EXPECT_EQ(0u, test_animator_->GetAnimationCount());
-    EXPECT_TRUE(WmShell::Get()->session_controller()->IsScreenLocked());
+    EXPECT_TRUE(Shell::Get()->session_controller()->IsScreenLocked());
   }
 
   void HideWallpaper() { test_animator_->HideWallpaper(); }
@@ -314,7 +314,7 @@
 
   void SystemLocks() {
     lock_state_controller_->OnLockStateChanged(true);
-    WmShell::Get()->session_controller()->LockScreenAndFlushForTest();
+    Shell::Get()->session_controller()->LockScreenAndFlushForTest();
   }
 
   void SuccessfulAuthentication(bool* call_flag) {
diff --git a/ash/wm/panels/panel_layout_manager_unittest.cc b/ash/wm/panels/panel_layout_manager_unittest.cc
index 92ea98f8..f20307a 100644
--- a/ash/wm/panels/panel_layout_manager_unittest.cc
+++ b/ash/wm/panels/panel_layout_manager_unittest.cc
@@ -220,7 +220,7 @@
     test::ShelfViewTestAPI test_api(shelf_view);
     test_api.SetAnimationDuration(1);
     test_api.RunMessageLoopUntilAnimationsDone();
-    int index = WmShell::Get()->shelf_model()->ItemIndexByID(
+    int index = Shell::Get()->shelf_model()->ItemIndexByID(
         window->GetProperty(kShelfIDKey));
     gfx::Rect bounds = test_api.GetButton(index)->GetBoundsInScreen();
 
diff --git a/ash/wm/panels/panel_window_resizer_unittest.cc b/ash/wm/panels/panel_window_resizer_unittest.cc
index 3fd6e9e..438eae4 100644
--- a/ash/wm/panels/panel_window_resizer_unittest.cc
+++ b/ash/wm/panels/panel_window_resizer_unittest.cc
@@ -41,7 +41,7 @@
   void SetUp() override {
     AshTestBase::SetUp();
     UpdateDisplay("600x400");
-    model_ = WmShell::Get()->shelf_model();
+    model_ = Shell::Get()->shelf_model();
   }
 
   void TearDown() override { AshTestBase::TearDown(); }
diff --git a/ash/wm/power_button_controller.cc b/ash/wm/power_button_controller.cc
index c8cd426c..8031ac50 100644
--- a/ash/wm/power_button_controller.cc
+++ b/ash/wm/power_button_controller.cc
@@ -104,7 +104,7 @@
   }
 
   const SessionController* const session_controller =
-      WmShell::Get()->session_controller();
+      Shell::Get()->session_controller();
   if (has_legacy_power_button_) {
     // If power button releases won't get reported correctly because we're not
     // running on official hardware, just lock the screen or shut down
@@ -145,7 +145,7 @@
   lock_button_down_ = down;
 
   const SessionController* const session_controller =
-      WmShell::Get()->session_controller();
+      Shell::Get()->session_controller();
   if (!session_controller->CanLockScreen() ||
       session_controller->IsScreenLocked() ||
       lock_state_controller_->LockRequested() ||
diff --git a/ash/wm/window_cycle_controller_unittest.cc b/ash/wm/window_cycle_controller_unittest.cc
index 7726d7b..29e30f00 100644
--- a/ash/wm/window_cycle_controller_unittest.cc
+++ b/ash/wm/window_cycle_controller_unittest.cc
@@ -227,7 +227,7 @@
   wm::ActivateWindow(window0.get());
 
   // When the screen is locked, cycling window does not take effect.
-  WmShell::Get()->session_controller()->LockScreenAndFlushForTest();
+  Shell::Get()->session_controller()->LockScreenAndFlushForTest();
   EXPECT_TRUE(wm::IsActiveWindow(window0.get()));
   controller->HandleCycleWindow(WindowCycleController::FORWARD);
   EXPECT_FALSE(controller->IsCycling());
diff --git a/ash/wm/workspace_controller_unittest.cc b/ash/wm/workspace_controller_unittest.cc
index 885b893..9011354e 100644
--- a/ash/wm/workspace_controller_unittest.cc
+++ b/ash/wm/workspace_controller_unittest.cc
@@ -500,7 +500,7 @@
   // AshTestHelper.
   mojom::SessionInfoPtr info = mojom::SessionInfo::New();
   info->state = session_manager::SessionState::ACTIVE;
-  ash::WmShell::Get()->session_controller()->SetSessionInfo(std::move(info));
+  ash::Shell::Get()->session_controller()->SetSessionInfo(std::move(info));
 
   std::unique_ptr<Window> w1(CreateTestWindow());
   w1->Show();
diff --git a/base/BUILD.gn b/base/BUILD.gn
index 3398073..e2c748d 100644
--- a/base/BUILD.gn
+++ b/base/BUILD.gn
@@ -2154,6 +2154,7 @@
     "trace_event/java_heap_dump_provider_android_unittest.cc",
     "trace_event/memory_allocator_dump_unittest.cc",
     "trace_event/memory_dump_manager_unittest.cc",
+    "trace_event/memory_dump_scheduler_unittest.cc",
     "trace_event/memory_usage_estimator_unittest.cc",
     "trace_event/process_memory_dump_unittest.cc",
     "trace_event/trace_category_unittest.cc",
diff --git a/base/message_loop/message_loop.h b/base/message_loop/message_loop.h
index 8417ce49c..2ef4537 100644
--- a/base/message_loop/message_loop.h
+++ b/base/message_loop/message_loop.h
@@ -320,6 +320,8 @@
   // Runs the specified PendingTask.
   void RunTask(PendingTask* pending_task);
 
+  bool nesting_allowed() const { return allow_nesting_; }
+
   // Disallow nesting. After this is called, running a nested RunLoop or calling
   // Add/RemoveNestingObserver() on this MessageLoop will crash.
   void DisallowNesting() { allow_nesting_ = false; }
diff --git a/base/trace_event/memory_dump_scheduler.h b/base/trace_event/memory_dump_scheduler.h
index fd21fce8..8c48078 100644
--- a/base/trace_event/memory_dump_scheduler.h
+++ b/base/trace_event/memory_dump_scheduler.h
@@ -50,10 +50,11 @@
 
  private:
   friend class MemoryDumpManagerTest;
+  friend class MemoryDumpSchedulerPollingTest;
   FRIEND_TEST_ALL_PREFIXES(MemoryDumpManagerTest, TestPollingOnDumpThread);
 
   // Helper class to schdule periodic memory dumps.
-  struct PeriodicTriggerState {
+  struct BASE_EXPORT PeriodicTriggerState {
     PeriodicTriggerState();
     ~PeriodicTriggerState();
 
@@ -71,7 +72,7 @@
     DISALLOW_COPY_AND_ASSIGN(PeriodicTriggerState);
   };
 
-  struct PollingTriggerState {
+  struct BASE_EXPORT PollingTriggerState {
     enum State {
       CONFIGURED,  // Polling trigger was added.
       ENABLED,     // Polling is running.
diff --git a/base/trace_event/memory_dump_scheduler_unittest.cc b/base/trace_event/memory_dump_scheduler_unittest.cc
new file mode 100644
index 0000000..05344d5
--- /dev/null
+++ b/base/trace_event/memory_dump_scheduler_unittest.cc
@@ -0,0 +1,95 @@
+// 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 "base/trace_event/memory_dump_scheduler.h"
+
+#include <memory>
+
+#include "base/single_thread_task_runner.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+namespace trace_event {
+
+class MemoryDumpSchedulerPollingTest : public testing::Test {
+ public:
+  static const uint32_t kMinPollsToDump = 5;
+
+  MemoryDumpSchedulerPollingTest()
+      : testing::Test(),
+        num_samples_tracked_(
+            MemoryDumpScheduler::PollingTriggerState::kMaxNumMemorySamples) {}
+
+  void SetUp() override {
+    MemoryDumpScheduler::SetPollingIntervalForTesting(1);
+    uint32_t kMinPollsToDump = 5;
+    mds_.reset(new MemoryDumpScheduler(nullptr, nullptr));
+    mds_->AddTrigger(MemoryDumpType::PEAK_MEMORY_USAGE,
+                     MemoryDumpLevelOfDetail::LIGHT, kMinPollsToDump);
+    mds_->polling_state_.ResetTotals();
+    mds_->polling_state_.current_state =
+        MemoryDumpScheduler::PollingTriggerState::ENABLED;
+  }
+
+  void TearDown() override {
+    mds_->polling_state_.current_state =
+        MemoryDumpScheduler::PollingTriggerState::DISABLED;
+    mds_.reset();
+  }
+
+ protected:
+  bool ShouldTriggerDump(uint64_t total) {
+    return mds_->ShouldTriggerDump(total);
+  }
+
+  uint32_t num_samples_tracked_;
+  std::unique_ptr<MemoryDumpScheduler> mds_;
+};
+
+TEST_F(MemoryDumpSchedulerPollingTest, PeakDetection) {
+  for (uint32_t i = 0; i < num_samples_tracked_ * 6; ++i) {
+    // Memory is increased in steps and dumps must be triggered at every step.
+    uint64_t total = (2 + (i / (2 * num_samples_tracked_))) * 1024 * 1204;
+    bool did_trigger = ShouldTriggerDump(total);
+    // Dumps must be triggered only at specific iterations.
+    bool should_have_triggered = i == 0;
+    should_have_triggered |=
+        (i > num_samples_tracked_) && (i % (2 * num_samples_tracked_) == 1);
+    if (should_have_triggered) {
+      ASSERT_TRUE(did_trigger) << "Dump wasn't triggered at " << i;
+    } else {
+      ASSERT_FALSE(did_trigger) << "Unexpected dump at " << i;
+    }
+  }
+}
+
+TEST_F(MemoryDumpSchedulerPollingTest, SlowGrowthDetection) {
+  for (uint32_t i = 0; i < 15; ++i) {
+    // Record 1GiB of increase in each call. Dumps are triggered with 1% w.r.t
+    // system's total memory.
+    uint64_t total = static_cast<uint64_t>(i + 1) * 1024 * 1024 * 1024;
+    bool did_trigger = ShouldTriggerDump(total);
+    bool should_have_triggered = i % kMinPollsToDump == 0;
+    if (should_have_triggered) {
+      ASSERT_TRUE(did_trigger) << "Dump wasn't triggered at " << i;
+    } else {
+      ASSERT_FALSE(did_trigger) << "Unexpected dump at " << i;
+    }
+  }
+}
+
+TEST_F(MemoryDumpSchedulerPollingTest, NotifyDumpTriggered) {
+  for (uint32_t i = 0; i < num_samples_tracked_ * 6; ++i) {
+    uint64_t total = (2 + (i / (2 * num_samples_tracked_))) * 1024 * 1204;
+    if (i % num_samples_tracked_ == 0)
+      mds_->NotifyDumpTriggered();
+    bool did_trigger = ShouldTriggerDump(total);
+    // Dumps should never be triggered since NotifyDumpTriggered() is called
+    // frequently.
+    ASSERT_FALSE(did_trigger && i);
+  }
+}
+
+}  // namespace trace_event
+}  // namespace base
diff --git a/cc/output/overlay_candidate.cc b/cc/output/overlay_candidate.cc
index 698d1ed..b4210532 100644
--- a/cc/output/overlay_candidate.cc
+++ b/cc/output/overlay_candidate.cc
@@ -25,7 +25,7 @@
 const gfx::BufferFormat kOverlayFormats[] = {
     gfx::BufferFormat::RGBX_8888, gfx::BufferFormat::RGBA_8888,
     gfx::BufferFormat::BGRX_8888, gfx::BufferFormat::BGRA_8888,
-    gfx::BufferFormat::BGR_565};
+    gfx::BufferFormat::BGR_565,   gfx::BufferFormat::YUV_420_BIPLANAR};
 
 enum Axis { NONE, AXIS_POS_X, AXIS_NEG_X, AXIS_POS_Y, AXIS_NEG_Y };
 
diff --git a/cc/paint/paint_canvas.h b/cc/paint/paint_canvas.h
index 670fd22..733273d 100644
--- a/cc/paint/paint_canvas.h
+++ b/cc/paint/paint_canvas.h
@@ -20,6 +20,8 @@
 
 class CC_PAINT_EXPORT PaintCanvas {
  public:
+  virtual ~PaintCanvas() {}
+
   virtual SkMetaData& getMetaData() = 0;
   virtual SkImageInfo imageInfo() const = 0;
 
diff --git a/cc/paint/skia_paint_canvas.h b/cc/paint/skia_paint_canvas.h
index b85badfd..0a0974b 100644
--- a/cc/paint/skia_paint_canvas.h
+++ b/cc/paint/skia_paint_canvas.h
@@ -28,7 +28,7 @@
   explicit SkiaPaintCanvas(SkCanvas* canvas);
   explicit SkiaPaintCanvas(const SkBitmap& bitmap);
   explicit SkiaPaintCanvas(const SkBitmap& bitmap, const SkSurfaceProps& props);
-  ~SkiaPaintCanvas();
+  ~SkiaPaintCanvas() override;
 
   SkMetaData& getMetaData() override;
   SkImageInfo imageInfo() const override;
diff --git a/chrome/browser/chrome_content_browser_manifest_overlay.json b/chrome/browser/chrome_content_browser_manifest_overlay.json
index 9c0145b..3c770a8 100644
--- a/chrome/browser/chrome_content_browser_manifest_overlay.json
+++ b/chrome/browser/chrome_content_browser_manifest_overlay.json
@@ -40,6 +40,7 @@
           "blink::mojom::ShareService",
           "bluetooth::mojom::AdapterFactory",
           "chrome::mojom::PrerenderCanceler",
+          "chrome::mojom::OpenSearchDocumentDescriptionHandler",
           "device::usb::ChooserService",
           "device::usb::DeviceManager",
           "contextual_search::mojom::ContextualSearchJsApiService",
diff --git a/chrome/browser/chromeos/arc/arc_play_store_enabled_preference_handler.cc b/chrome/browser/chromeos/arc/arc_play_store_enabled_preference_handler.cc
index b137d3e..a4a82d5 100644
--- a/chrome/browser/chromeos/arc/arc_play_store_enabled_preference_handler.cc
+++ b/chrome/browser/chromeos/arc/arc_play_store_enabled_preference_handler.cc
@@ -5,7 +5,7 @@
 #include "chrome/browser/chromeos/arc/arc_play_store_enabled_preference_handler.h"
 
 #include "ash/common/shelf/shelf_delegate.h"
-#include "ash/common/wm_shell.h"
+#include "ash/shell.h"
 #include "base/bind.h"
 #include "base/command_line.h"
 #include "base/logging.h"
@@ -93,8 +93,8 @@
       // Remove the pinned Play Store icon launcher in Shelf.
       // This is only for non-Managed cases. In managed cases, it is expected
       // to be "disabled" rather than "removed", so keep it here.
-      auto* shelf_delegate = ash::WmShell::HasInstance()
-                                 ? ash::WmShell::Get()->shelf_delegate()
+      auto* shelf_delegate = ash::Shell::HasInstance()
+                                 ? ash::Shell::Get()->shelf_delegate()
                                  : nullptr;
       if (shelf_delegate)
         shelf_delegate->UnpinAppWithID(ArcSupportHost::kHostAppId);
diff --git a/chrome/browser/extensions/bookmark_app_helper.cc b/chrome/browser/extensions/bookmark_app_helper.cc
index 7a73d7867..58bfbb4 100644
--- a/chrome/browser/extensions/bookmark_app_helper.cc
+++ b/chrome/browser/extensions/bookmark_app_helper.cc
@@ -77,7 +77,7 @@
 
 #if defined(USE_ASH)
 #include "ash/common/shelf/shelf_delegate.h"  // nogncheck
-#include "ash/common/wm_shell.h"  // nogncheck
+#include "ash/shell.h"                        // nogncheck
 #endif
 
 namespace {
@@ -732,7 +732,7 @@
   web_app::CreateShortcuts(web_app::SHORTCUT_CREATION_BY_USER,
                            creation_locations, current_profile, extension);
 #else
-  ash::ShelfDelegate* shelf_delegate = ash::WmShell::Get()->shelf_delegate();
+  ash::ShelfDelegate* shelf_delegate = ash::Shell::Get()->shelf_delegate();
   DCHECK(shelf_delegate);
   shelf_delegate->PinAppWithID(extension->id());
 #endif  // !defined(USE_ASH)
diff --git a/chrome/browser/installable/installable_logging.cc b/chrome/browser/installable/installable_logging.cc
index 6f0cdc4..926dd2a 100644
--- a/chrome/browser/installable/installable_logging.cc
+++ b/chrome/browser/installable/installable_logging.cc
@@ -64,7 +64,7 @@
     "a URL in the web manifest contains a username, password, or port";
 static const char kInIncognitoMessage[] =
     "the page is loaded in an incognito window";
-
+static const char kNotOfflineCapable[] = "the page does not work offline";
 }  // namespace
 
 void LogErrorToConsole(content::WebContents* web_contents,
@@ -151,6 +151,9 @@
     case IN_INCOGNITO:
       pattern = kInIncognitoMessage;
       break;
+    case NOT_OFFLINE_CAPABLE:
+      pattern = kNotOfflineCapable;
+      break;
   }
 
   if (!pattern)
diff --git a/chrome/browser/installable/installable_logging.h b/chrome/browser/installable/installable_logging.h
index 369afc5..24236272 100644
--- a/chrome/browser/installable/installable_logging.h
+++ b/chrome/browser/installable/installable_logging.h
@@ -46,6 +46,7 @@
   FAILED_TO_CREATE_BANNER,
   URL_NOT_SUPPORTED_FOR_WEBAPK,
   IN_INCOGNITO,
+  NOT_OFFLINE_CAPABLE,
   MAX_ERROR_CODE,
 };
 
diff --git a/chrome/browser/installable/installable_manager.cc b/chrome/browser/installable/installable_manager.cc
index 4c4b343..b0bf449 100644
--- a/chrome/browser/installable/installable_manager.cc
+++ b/chrome/browser/installable/installable_manager.cc
@@ -412,15 +412,23 @@
                  weak_factory_.GetWeakPtr()));
 }
 
-void InstallableManager::OnDidCheckHasServiceWorker(bool has_service_worker) {
+void InstallableManager::OnDidCheckHasServiceWorker(
+    content::ServiceWorkerCapability capability) {
   if (!GetWebContents())
     return;
 
-  if (has_service_worker) {
-    installable_->installable = true;
-  } else {
-    installable_->installable = false;
-    installable_->error = NO_MATCHING_SERVICE_WORKER;
+  switch (capability) {
+    case content::ServiceWorkerCapability::SERVICE_WORKER_WITH_FETCH_HANDLER:
+      installable_->installable = true;
+      break;
+    case content::ServiceWorkerCapability::SERVICE_WORKER_NO_FETCH_HANDLER:
+      installable_->installable = false;
+      installable_->error = NOT_OFFLINE_CAPABLE;
+      break;
+    case content::ServiceWorkerCapability::NO_SERVICE_WORKER:
+      installable_->installable = false;
+      installable_->error = NO_MATCHING_SERVICE_WORKER;
+      break;
   }
 
   installable_->fetched = true;
diff --git a/chrome/browser/installable/installable_manager.h b/chrome/browser/installable/installable_manager.h
index 3f50814..5d56abd 100644
--- a/chrome/browser/installable/installable_manager.h
+++ b/chrome/browser/installable/installable_manager.h
@@ -16,6 +16,7 @@
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
 #include "chrome/browser/installable/installable_logging.h"
+#include "content/public/browser/service_worker_context.h"
 #include "content/public/browser/web_contents_observer.h"
 #include "content/public/browser/web_contents_user_data.h"
 #include "content/public/common/manifest.h"
@@ -91,9 +92,9 @@
   // using it.
   const SkBitmap* badge_icon;
 
-  // true if the site has a service worker and a viable web app manifest. If
-  // check_installable was true and the site isn't installable, the reason will
-  // be in error_code.
+  // true if the site has a service worker with a fetch handler and a viable web
+  // app manifest. If check_installable was true and the site isn't installable,
+  // the reason will be in error_code.
   const bool is_installable;
 };
 
@@ -195,7 +196,7 @@
   void CheckInstallable();
   bool IsManifestValidForWebApp(const content::Manifest& manifest);
   void CheckServiceWorker();
-  void OnDidCheckHasServiceWorker(bool has_service_worker);
+  void OnDidCheckHasServiceWorker(content::ServiceWorkerCapability capability);
 
   void CheckAndFetchBestIcon(const IconParams& params);
   void OnIconFetched(
diff --git a/chrome/browser/installable/installable_manager_browsertest.cc b/chrome/browser/installable/installable_manager_browsertest.cc
index fa43b1a..f809328 100644
--- a/chrome/browser/installable/installable_manager_browsertest.cc
+++ b/chrome/browser/installable/installable_manager_browsertest.cc
@@ -620,6 +620,30 @@
   }
 }
 
+IN_PROC_BROWSER_TEST_F(InstallableManagerBrowserTest,
+                       CheckPageWithNoServiceWorkerFetchHandler) {
+  base::RunLoop run_loop;
+  std::unique_ptr<CallbackTester> tester(
+      new CallbackTester(run_loop.QuitClosure()));
+
+  NavigateAndRunInstallableManager(
+      tester.get(), GetWebAppParams(),
+      "/banners/no_sw_fetch_handler_test_page.html");
+
+  RunInstallableManager(tester.get(), GetWebAppParams());
+  run_loop.Run();
+
+  EXPECT_FALSE(tester->manifest().IsEmpty());
+  EXPECT_FALSE(tester->manifest_url().is_empty());
+
+  EXPECT_TRUE(tester->primary_icon_url().is_empty());
+  EXPECT_EQ(nullptr, tester->primary_icon());
+  EXPECT_TRUE(tester->badge_icon_url().is_empty());
+  EXPECT_EQ(nullptr, tester->badge_icon());
+  EXPECT_FALSE(tester->is_installable());
+  EXPECT_EQ(NOT_OFFLINE_CAPABLE, tester->error_code());
+}
+
 IN_PROC_BROWSER_TEST_F(InstallableManagerBrowserTest, CheckDataUrlIcon) {
   // Verify that InstallableManager can handle data URL icons.
   base::RunLoop run_loop;
diff --git a/chrome/browser/resources/settings/settings_shared_css.html b/chrome/browser/resources/settings/settings_shared_css.html
index 508259a..a2672e1 100644
--- a/chrome/browser/resources/settings/settings_shared_css.html
+++ b/chrome/browser/resources/settings/settings_shared_css.html
@@ -40,7 +40,7 @@
       paper-button {
         height: 32px;
         margin: 0;
-        border-radius: 4px;
+        border-radius: 2px;
       }
 
       paper-button[toggles][active] {
diff --git a/chrome/browser/ui/ash/app_list/app_list_controller_ash.cc b/chrome/browser/ui/ash/app_list/app_list_controller_ash.cc
index aa17dce..bf7c8e3 100644
--- a/chrome/browser/ui/ash/app_list/app_list_controller_ash.cc
+++ b/chrome/browser/ui/ash/app_list/app_list_controller_ash.cc
@@ -39,21 +39,21 @@
 }
 
 bool AppListControllerDelegateAsh::IsAppPinned(const std::string& app_id) {
-  return ash::WmShell::Get()->shelf_delegate()->IsAppPinned(app_id);
+  return ash::Shell::Get()->shelf_delegate()->IsAppPinned(app_id);
 }
 
 bool AppListControllerDelegateAsh::IsAppOpen(const std::string& app_id) const {
   ash::ShelfID id =
-      ash::WmShell::Get()->shelf_delegate()->GetShelfIDForAppID(app_id);
+      ash::Shell::Get()->shelf_delegate()->GetShelfIDForAppID(app_id);
   return id && ChromeLauncherController::instance()->IsOpen(id);
 }
 
 void AppListControllerDelegateAsh::PinApp(const std::string& app_id) {
-  ash::WmShell::Get()->shelf_delegate()->PinAppWithID(app_id);
+  ash::Shell::Get()->shelf_delegate()->PinAppWithID(app_id);
 }
 
 void AppListControllerDelegateAsh::UnpinApp(const std::string& app_id) {
-  ash::WmShell::Get()->shelf_delegate()->UnpinAppWithID(app_id);
+  ash::Shell::Get()->shelf_delegate()->UnpinAppWithID(app_id);
 }
 
 AppListControllerDelegate::Pinnable AppListControllerDelegateAsh::GetPinnable(
diff --git a/chrome/browser/ui/ash/launcher/arc_app_launcher_browsertest.cc b/chrome/browser/ui/ash/launcher/arc_app_launcher_browsertest.cc
index ffe1363..663fd4b2 100644
--- a/chrome/browser/ui/ash/launcher/arc_app_launcher_browsertest.cc
+++ b/chrome/browser/ui/ash/launcher/arc_app_launcher_browsertest.cc
@@ -3,7 +3,7 @@
 // found in the LICENSE file.
 
 #include "ash/common/shelf/shelf_delegate.h"
-#include "ash/common/wm_shell.h"
+#include "ash/shell.h"
 #include "ash/wm/window_util.h"
 #include "base/macros.h"
 #include "base/run_loop.h"
@@ -108,7 +108,7 @@
 }
 
 ash::ShelfDelegate* shelf_delegate() {
-  return ash::WmShell::Get()->shelf_delegate();
+  return ash::Shell::Get()->shelf_delegate();
 }
 
 class AppAnimatedWaiter {
diff --git a/chrome/browser/ui/ash/launcher/arc_app_window_launcher_controller.cc b/chrome/browser/ui/ash/launcher/arc_app_window_launcher_controller.cc
index ecdc1eb5..cd189031 100644
--- a/chrome/browser/ui/ash/launcher/arc_app_window_launcher_controller.cc
+++ b/chrome/browser/ui/ash/launcher/arc_app_window_launcher_controller.cc
@@ -565,6 +565,9 @@
 
 AppWindowLauncherItemController*
 ArcAppWindowLauncherController::ControllerForWindow(aura::Window* window) {
+  if (!window)
+    return nullptr;
+
   AppWindow* app_window = GetAppWindowForTask(active_task_id_);
   if (app_window &&
       app_window->widget() == views::Widget::GetWidgetForNativeWindow(window)) {
@@ -587,6 +590,8 @@
     aura::client::ActivationChangeObserver::ActivationReason reason,
     aura::Window* gained_active,
     aura::Window* lost_active) {
+  AppWindowLauncherController::OnWindowActivated(reason, gained_active,
+                                                 lost_active);
   OnTaskSetActive(active_task_id_);
 }
 
diff --git a/chrome/browser/ui/ash/launcher/arc_app_window_launcher_item_controller.cc b/chrome/browser/ui/ash/launcher/arc_app_window_launcher_item_controller.cc
index d94ec286..2ecf46e4 100644
--- a/chrome/browser/ui/ash/launcher/arc_app_window_launcher_item_controller.cc
+++ b/chrome/browser/ui/ash/launcher/arc_app_window_launcher_item_controller.cc
@@ -55,6 +55,11 @@
   callback.Run(ash::SHELF_ACTION_NEW_WINDOW_CREATED, base::nullopt);
 }
 
+void ArcAppWindowLauncherItemController::ExecuteCommand(uint32_t command_id,
+                                                        int32_t event_flags) {
+  ActivateIndexedApp(command_id);
+}
+
 MenuItemList ArcAppWindowLauncherItemController::GetAppMenuItems(
     int event_flags) {
   MenuItemList items;
diff --git a/chrome/browser/ui/ash/launcher/arc_app_window_launcher_item_controller.h b/chrome/browser/ui/ash/launcher/arc_app_window_launcher_item_controller.h
index c2cf5a1..62abc7a 100644
--- a/chrome/browser/ui/ash/launcher/arc_app_window_launcher_item_controller.h
+++ b/chrome/browser/ui/ash/launcher/arc_app_window_launcher_item_controller.h
@@ -28,6 +28,7 @@
                     ash::ShelfLaunchSource source,
                     const ItemSelectedCallback& callback) override;
   MenuItemList GetAppMenuItems(int event_flags) override;
+  void ExecuteCommand(uint32_t command_id, int32_t event_flags) override;
 
   void AddTaskId(int task_id);
   void RemoveTaskId(int task_id);
diff --git a/chrome/browser/ui/ash/launcher/browser_shortcut_launcher_item_controller.cc b/chrome/browser/ui/ash/launcher/browser_shortcut_launcher_item_controller.cc
index dd36cdf7..810a42a 100644
--- a/chrome/browser/ui/ash/launcher/browser_shortcut_launcher_item_controller.cc
+++ b/chrome/browser/ui/ash/launcher/browser_shortcut_launcher_item_controller.cc
@@ -9,9 +9,9 @@
 
 #include "ash/common/shelf/shelf_delegate.h"
 #include "ash/common/shelf/shelf_model.h"
-#include "ash/common/wm_shell.h"
 #include "ash/common/wm_window.h"
 #include "ash/resources/grit/ash_resources.h"
+#include "ash/shell.h"
 #include "ash/wm/window_properties.h"
 #include "ash/wm/window_util.h"
 #include "base/memory/ptr_util.h"
@@ -358,7 +358,7 @@
     return false;
 
   // v1 App popup windows with a valid app id have their own icon.
-  ash::ShelfDelegate* delegate = ash::WmShell::Get()->shelf_delegate();
+  ash::ShelfDelegate* delegate = ash::Shell::Get()->shelf_delegate();
   if (browser->is_app() && browser->is_type_popup() && delegate &&
       delegate->GetShelfIDForAppID(web_app::GetExtensionIdFromApplicationName(
           browser->app_name())) > 0) {
diff --git a/chrome/browser/ui/ash/launcher/chrome_launcher_controller_impl_browsertest.cc b/chrome/browser/ui/ash/launcher/chrome_launcher_controller_impl_browsertest.cc
index 9d6c8ea..af53acf 100644
--- a/chrome/browser/ui/ash/launcher/chrome_launcher_controller_impl_browsertest.cc
+++ b/chrome/browser/ui/ash/launcher/chrome_launcher_controller_impl_browsertest.cc
@@ -233,7 +233,7 @@
     return extensions::PlatformAppBrowserTest::RunTestOnMainThreadLoop();
   }
 
-  ash::ShelfModel* shelf_model() { return ash::WmShell::Get()->shelf_model(); }
+  ash::ShelfModel* shelf_model() { return ash::Shell::Get()->shelf_model(); }
 
   ash::ShelfID CreateAppShortcutLauncherItem(
       const ash::AppLaunchId& app_launch_id) {
@@ -287,7 +287,7 @@
 
     shelf_ =
         ash::WmShelf::ForWindow(ash::WmShell::Get()->GetPrimaryRootWindow());
-    model_ = ash::WmShell::Get()->shelf_model();
+    model_ = ash::Shell::Get()->shelf_model();
     controller_ = GetChromeLauncherControllerImpl();
     ASSERT_TRUE(controller_);
     return ExtensionBrowserTest::RunTestOnMainThreadLoop();
@@ -1562,7 +1562,7 @@
 
 IN_PROC_BROWSER_TEST_F(LauncherPlatformAppBrowserTest,
                        ShowInShelfWindowsWithWindowKeySet) {
-  ash::ShelfModel* shelf_model = ash::WmShell::Get()->shelf_model();
+  ash::ShelfModel* shelf_model = ash::Shell::Get()->shelf_model();
 
   // Add a window with shelf True, close it
   int item_count = shelf_model->item_count();
@@ -2277,7 +2277,7 @@
 IN_PROC_BROWSER_TEST_F(ShelfAppBrowserTest, SettingsWindow) {
   chrome::SettingsWindowManager* settings_manager =
       chrome::SettingsWindowManager::GetInstance();
-  ash::ShelfModel* shelf_model = ash::WmShell::Get()->shelf_model();
+  ash::ShelfModel* shelf_model = ash::Shell::Get()->shelf_model();
 
   // Get the number of items in the shelf and browser menu.
   int item_count = shelf_model->item_count();
diff --git a/chrome/browser/ui/ash/launcher/chrome_launcher_controller_impl_unittest.cc b/chrome/browser/ui/ash/launcher/chrome_launcher_controller_impl_unittest.cc
index 270a1ee0..0d746cb 100644
--- a/chrome/browser/ui/ash/launcher/chrome_launcher_controller_impl_unittest.cc
+++ b/chrome/browser/ui/ash/launcher/chrome_launcher_controller_impl_unittest.cc
@@ -26,6 +26,7 @@
 #include "ash/public/cpp/shelf_item.h"
 #include "ash/shell.h"
 #include "ash/test/ash_test_helper.h"
+#include "ash/test/shell_test_api.h"
 #include "ash/test/test_shell_delegate.h"
 #include "ash/wm/window_util.h"
 #include "base/command_line.h"
@@ -111,6 +112,7 @@
 #include "ui/display/display.h"
 #include "ui/display/display_switches.h"
 #include "ui/display/screen.h"
+#include "ui/events/base_event_utils.h"
 #include "ui/events/event_constants.h"
 #include "ui/views/widget/widget.h"
 
@@ -322,6 +324,18 @@
   DISALLOW_COPY_AND_ASSIGN(ProxyShelfDelegate);
 };
 
+// A callback that does nothing after shelf item selection handling.
+void NoopCallback(ash::ShelfAction action, base::Optional<MenuItemList>) {}
+
+// Simulates selection of the shelf item.
+void SelectItem(ash::mojom::ShelfItemDelegate* delegate) {
+  std::unique_ptr<ui::Event> event = base::MakeUnique<ui::MouseEvent>(
+      ui::ET_MOUSE_PRESSED, gfx::Point(), gfx::Point(), ui::EventTimeForNow(),
+      ui::EF_NONE, 0);
+  delegate->ItemSelected(std::move(event), display::kInvalidDisplayId,
+                         ash::LAUNCH_FROM_UNKNOWN, base::Bind(&NoopCallback));
+}
+
 }  // namespace
 
 class ChromeLauncherControllerImplTest : public BrowserWithTestWindowTest {
@@ -345,7 +359,7 @@
       ASSERT_TRUE(profile_manager_->SetUp());
     }
 
-    model_ = ash::WmShell::Get()->shelf_controller()->model();
+    model_ = ash::Shell::Get()->shelf_controller()->model();
     model_observer_.reset(new TestShelfModelObserver);
     model_->AddObserver(model_observer_.get());
 
@@ -551,7 +565,7 @@
   // This needs to be called after InitLaunchController(), or its family.
   // It is not supported to recreate the instance.
   void SetShelfDelegate() {
-    ash::WmShell::Get()->SetShelfDelegateForTesting(
+    ash::test::ShellTestApi().SetShelfDelegate(
         base::MakeUnique<ProxyShelfDelegate>(launcher_controller_.get()));
   }
 
@@ -3773,6 +3787,65 @@
                    "AppList, Chrome");
 }
 
+// Test the application menu of a shelf item with multiple ARC windows.
+TEST_P(ChromeLauncherControllerImplWithArcTest, ShelfItemWithMultipleWindows) {
+  InitLauncherControllerWithBrowser();
+
+  arc::mojom::AppInfo appinfo =
+      CreateAppInfo("Test1", "test", "com.example.app", OrientationLock::NONE);
+  AddArcAppAndShortcut(appinfo);
+
+  // Widgets will be deleted by the system.
+  NotifyOnTaskCreated(appinfo, 1 /* task_id */);
+  views::Widget* window1 = CreateArcWindow("org.chromium.arc.1");
+  ASSERT_TRUE(window1);
+  EXPECT_TRUE(window1->IsActive());
+
+  NotifyOnTaskCreated(appinfo, 2 /* task_id */);
+  views::Widget* window2 = CreateArcWindow("org.chromium.arc.2");
+  ASSERT_TRUE(window2);
+
+  EXPECT_FALSE(window1->IsActive());
+  EXPECT_TRUE(window2->IsActive());
+
+  const std::string app_id = ArcAppTest::GetAppId(appinfo);
+
+  const ash::ShelfID shelf_id =
+      launcher_controller_->GetShelfIDForAppID(app_id);
+  LauncherItemController* item_controller =
+      launcher_controller_->GetLauncherItemController(shelf_id);
+  ASSERT_TRUE(item_controller);
+
+  // Selecting the item will show its application menu. It does not change the
+  // active window.
+  SelectItem(item_controller);
+  EXPECT_FALSE(window1->IsActive());
+  EXPECT_TRUE(window2->IsActive());
+
+  // Command ids are just app window indices. Note, apps are registered in
+  // opposite order. Last created goes in front.
+  MenuItemList items = item_controller->GetAppMenuItems(0);
+  ASSERT_EQ(items.size(), 2U);
+  EXPECT_EQ(items[0]->command_id, 0U);
+  EXPECT_EQ(items[1]->command_id, 1U);
+
+  // Execute command to activate first window.
+  item_controller->ExecuteCommand(items[1]->command_id, 0);
+  EXPECT_TRUE(window1->IsActive());
+  EXPECT_FALSE(window2->IsActive());
+
+  // Selecting the item will show its application menu. It does not change the
+  // active window.
+  SelectItem(item_controller);
+  EXPECT_TRUE(window1->IsActive());
+  EXPECT_FALSE(window2->IsActive());
+
+  // Execute command to activate second window.
+  item_controller->ExecuteCommand(items[0]->command_id, 0);
+  EXPECT_FALSE(window1->IsActive());
+  EXPECT_TRUE(window2->IsActive());
+}
+
 namespace {
 
 class ChromeLauncherControllerOrientationTest
@@ -4297,7 +4370,7 @@
   prefs->SetString(prefs::kShelfAutoHideBehaviorLocal, "Always");
   prefs->SetString(prefs::kShelfAutoHideBehavior, "Always");
 
-  ash::ShelfModel* model = ash::WmShell::Get()->shelf_controller()->model();
+  ash::ShelfModel* model = ash::Shell::Get()->shelf_controller()->model();
   TestChromeLauncherControllerImpl test_launcher_controller(profile(), model);
   test_launcher_controller.Init();
 
@@ -4318,7 +4391,7 @@
 
 // Tests that the shelf controller's changes are not wastefully echoed back.
 TEST_F(ChromeLauncherControllerImplPrefTest, DoNotEchoShelfControllerChanges) {
-  ash::ShelfModel* model = ash::WmShell::Get()->shelf_controller()->model();
+  ash::ShelfModel* model = ash::Shell::Get()->shelf_controller()->model();
   TestChromeLauncherControllerImpl test_launcher_controller(profile(), model);
   test_launcher_controller.Init();
 
diff --git a/chrome/browser/ui/ash/launcher/extension_app_window_launcher_controller.cc b/chrome/browser/ui/ash/launcher/extension_app_window_launcher_controller.cc
index 2fbcc38..7aac2465 100644
--- a/chrome/browser/ui/ash/launcher/extension_app_window_launcher_controller.cc
+++ b/chrome/browser/ui/ash/launcher/extension_app_window_launcher_controller.cc
@@ -5,8 +5,8 @@
 #include "chrome/browser/ui/ash/launcher/extension_app_window_launcher_controller.h"
 
 #include "ash/common/shelf/shelf_delegate.h"
-#include "ash/common/wm_shell.h"
 #include "ash/common/wm_window.h"
+#include "ash/shell.h"
 #include "ash/wm/window_properties.h"
 #include "ash/wm/window_util.h"
 #include "base/stl_util.h"
@@ -173,7 +173,7 @@
     // If there is already a shelf id mapped to this AppLaunchId (e.g. pinned),
     // use that shelf item.
     shelf_id =
-        ash::WmShell::Get()->shelf_delegate()->GetShelfIDForAppIDAndLaunchID(
+        ash::Shell::Get()->shelf_delegate()->GetShelfIDForAppIDAndLaunchID(
             app_id, launch_id);
 
     if (shelf_id == 0) {
diff --git a/chrome/browser/ui/ash/launcher/extension_launcher_context_menu.cc b/chrome/browser/ui/ash/launcher/extension_launcher_context_menu.cc
index 8dd21a8..f077719 100644
--- a/chrome/browser/ui/ash/launcher/extension_launcher_context_menu.cc
+++ b/chrome/browser/ui/ash/launcher/extension_launcher_context_menu.cc
@@ -4,7 +4,7 @@
 
 #include "chrome/browser/ui/ash/launcher/extension_launcher_context_menu.h"
 
-#include "ash/common/wm_shell.h"
+#include "ash/shell.h"
 #include "base/bind.h"
 #include "chrome/browser/extensions/context_menu_matcher.h"
 #include "chrome/browser/extensions/extension_util.h"
@@ -83,8 +83,8 @@
       AddItemWithStringId(MENU_NEW_INCOGNITO_WINDOW,
                           IDS_APP_LIST_NEW_INCOGNITO_WINDOW);
     }
-    if (!BrowserShortcutLauncherItemController(
-             controller(), ash::WmShell::Get()->shelf_model())
+    if (!BrowserShortcutLauncherItemController(controller(),
+                                               ash::Shell::Get()->shelf_model())
              .IsListOfActiveBrowserEmpty()) {
       AddItem(MENU_CLOSE,
               l10n_util::GetStringUTF16(IDS_LAUNCHER_CONTEXT_MENU_CLOSE));
diff --git a/chrome/browser/ui/ash/launcher/launcher_context_menu.cc b/chrome/browser/ui/ash/launcher/launcher_context_menu.cc
index 4da52d7..5852c23 100644
--- a/chrome/browser/ui/ash/launcher/launcher_context_menu.cc
+++ b/chrome/browser/ui/ash/launcher/launcher_context_menu.cc
@@ -116,7 +116,7 @@
     case MENU_CLOSE:
       if (item_.type == ash::TYPE_DIALOG) {
         ash::mojom::ShelfItemDelegate* item_delegate =
-            ash::WmShell::Get()->shelf_model()->GetShelfItemDelegate(item_.id);
+            ash::Shell::Get()->shelf_model()->GetShelfItemDelegate(item_.id);
         DCHECK(item_delegate);
         item_delegate->Close();
       } else {
diff --git a/chrome/browser/ui/ash/launcher/launcher_context_menu_unittest.cc b/chrome/browser/ui/ash/launcher/launcher_context_menu_unittest.cc
index 1b3388e..3976c66 100644
--- a/chrome/browser/ui/ash/launcher/launcher_context_menu_unittest.cc
+++ b/chrome/browser/ui/ash/launcher/launcher_context_menu_unittest.cc
@@ -47,7 +47,7 @@
     session_manager_ = base::MakeUnique<session_manager::SessionManager>();
     ash::test::AshTestBase::SetUp();
     controller_.reset(new ChromeLauncherControllerImpl(
-        profile(), ash::WmShell::Get()->shelf_model()));
+        profile(), ash::Shell::Get()->shelf_model()));
     controller_->Init();
   }
 
diff --git a/chrome/browser/ui/ash/system_tray_delegate_chromeos.cc b/chrome/browser/ui/ash/system_tray_delegate_chromeos.cc
index ccc9cd23..787ef3d 100644
--- a/chrome/browser/ui/ash/system_tray_delegate_chromeos.cc
+++ b/chrome/browser/ui/ash/system_tray_delegate_chromeos.cc
@@ -462,7 +462,7 @@
   // Returned user_profile might be NULL on restoring Users on browser start.
   // At some point profile is not yet fully initiated.
   if (session_started_ && user_profile && user_profile_ == user_profile)
-    ash::WmShell::Get()->UpdateAfterLoginStatusChange(GetUserLoginStatus());
+    ash::Shell::Get()->UpdateAfterLoginStatusChange(GetUserLoginStatus());
 }
 
 ash::SystemTrayNotifier* SystemTrayDelegateChromeOS::GetSystemTrayNotifier() {
@@ -619,11 +619,11 @@
 
 // Overridden from SessionManagerClient::Observer.
 void SystemTrayDelegateChromeOS::ScreenIsLocked() {
-  ash::WmShell::Get()->UpdateAfterLoginStatusChange(GetUserLoginStatus());
+  ash::Shell::Get()->UpdateAfterLoginStatusChange(GetUserLoginStatus());
 }
 
 void SystemTrayDelegateChromeOS::ScreenIsUnlocked() {
-  ash::WmShell::Get()->UpdateAfterLoginStatusChange(GetUserLoginStatus());
+  ash::Shell::Get()->UpdateAfterLoginStatusChange(GetUserLoginStatus());
 }
 
 // content::NotificationObserver implementation.
@@ -649,7 +649,7 @@
     }
     case chrome::NOTIFICATION_SESSION_STARTED: {
       session_started_ = true;
-      ash::WmShell::Get()->UpdateAfterLoginStatusChange(GetUserLoginStatus());
+      ash::Shell::Get()->UpdateAfterLoginStatusChange(GetUserLoginStatus());
       SetProfile(ProfileManager::GetActiveUserProfile());
       break;
     }
diff --git a/chrome/browser/ui/search_engines/search_engine_tab_helper.cc b/chrome/browser/ui/search_engines/search_engine_tab_helper.cc
index e9c8fd0..7c26123 100644
--- a/chrome/browser/ui/search_engines/search_engine_tab_helper.cc
+++ b/chrome/browser/ui/search_engines/search_engine_tab_helper.cc
@@ -72,8 +72,7 @@
 void AssociateURLFetcherWithWebContents(content::WebContents* web_contents,
                                         net::URLFetcher* url_fetcher) {
   content::AssociateURLFetcherWithRenderFrame(
-      url_fetcher,
-      url::Origin(web_contents->GetURL()),
+      url_fetcher, url::Origin(web_contents->GetURL()),
       web_contents->GetRenderProcessHost()->GetID(),
       web_contents->GetMainFrame()->GetRoutingID());
 }
@@ -88,34 +87,24 @@
   GenerateKeywordIfNecessary(handle);
 }
 
-bool SearchEngineTabHelper::OnMessageReceived(const IPC::Message& message) {
-  bool handled = true;
-  IPC_BEGIN_MESSAGE_MAP(SearchEngineTabHelper, message)
-    IPC_MESSAGE_HANDLER(ChromeViewHostMsg_PageHasOSDD, OnPageHasOSDD)
-    IPC_MESSAGE_UNHANDLED(handled = false)
-  IPC_END_MESSAGE_MAP()
-
-  return handled;
-}
-
-bool SearchEngineTabHelper::OnMessageReceived(
-    const IPC::Message& message,
-    content::RenderFrameHost* render_frame_host) {
-  return OnMessageReceived(message);
-}
-
 SearchEngineTabHelper::SearchEngineTabHelper(WebContents* web_contents)
-    : content::WebContentsObserver(web_contents) {
+    : content::WebContentsObserver(web_contents),
+      osdd_handler_bindings_(web_contents, this) {
   DCHECK(web_contents);
 }
 
-void SearchEngineTabHelper::OnPageHasOSDD(
+void SearchEngineTabHelper::PageHasOpenSearchDescriptionDocument(
     const GURL& page_url,
     const GURL& osdd_url) {
   // Checks to see if we should generate a keyword based on the OSDD, and if
   // necessary uses TemplateURLFetcher to download the OSDD and create a
   // keyword.
 
+  // Only accept messages from the main frame.
+  if (osdd_handler_bindings_.GetCurrentTargetFrame() !=
+      web_contents()->GetMainFrame())
+    return;
+
   // Make sure that the page is the current page and other basic checks.
   // When |page_url| has file: scheme, this method doesn't work because of
   // http://b/issue?id=863583. For that reason, this doesn't check and allow
diff --git a/chrome/browser/ui/search_engines/search_engine_tab_helper.h b/chrome/browser/ui/search_engines/search_engine_tab_helper.h
index 2df1a4a..a5ca787 100644
--- a/chrome/browser/ui/search_engines/search_engine_tab_helper.h
+++ b/chrome/browser/ui/search_engines/search_engine_tab_helper.h
@@ -6,9 +6,10 @@
 #define CHROME_BROWSER_UI_SEARCH_ENGINES_SEARCH_ENGINE_TAB_HELPER_H_
 
 #include "base/macros.h"
-#include "base/memory/weak_ptr.h"
 #include "chrome/browser/ui/find_bar/find_bar_controller.h"
 #include "chrome/browser/ui/find_bar/find_notification_details.h"
+#include "chrome/common/open_search_description_document_handler.mojom.h"
+#include "content/public/browser/web_contents_binding_set.h"
 #include "content/public/browser/web_contents_observer.h"
 #include "content/public/browser/web_contents_user_data.h"
 
@@ -16,27 +17,29 @@
 // functionality.
 class SearchEngineTabHelper
     : public content::WebContentsObserver,
-      public content::WebContentsUserData<SearchEngineTabHelper> {
+      public content::WebContentsUserData<SearchEngineTabHelper>,
+      public chrome::mojom::OpenSearchDescriptionDocumentHandler {
  public:
   ~SearchEngineTabHelper() override;
 
   // content::WebContentsObserver overrides.
   void DidFinishNavigation(content::NavigationHandle* handle) override;
 
-  bool OnMessageReceived(const IPC::Message& message) override;
-  bool OnMessageReceived(const IPC::Message& message,
-                         content::RenderFrameHost* rfh) override;
-
  private:
   explicit SearchEngineTabHelper(content::WebContents* web_contents);
   friend class content::WebContentsUserData<SearchEngineTabHelper>;
 
-  // Handles when a page specifies an OSDD (OpenSearch Description Document).
-  void OnPageHasOSDD(const GURL& page_url, const GURL& osdd_url);
+  // chrome::mojom::OpenSearchDescriptionDocumentHandler overrides.
+  void PageHasOpenSearchDescriptionDocument(const GURL& page_url,
+                                            const GURL& osdd_url) override;
 
   // If params has a searchable form, this tries to create a new keyword.
   void GenerateKeywordIfNecessary(content::NavigationHandle* handle);
 
+  content::WebContentsFrameBindingSet<
+      chrome::mojom::OpenSearchDescriptionDocumentHandler>
+      osdd_handler_bindings_;
+
   DISALLOW_COPY_AND_ASSIGN(SearchEngineTabHelper);
 };
 
diff --git a/chrome/browser/ui/views/apps/app_info_dialog/app_info_footer_panel.cc b/chrome/browser/ui/views/apps/app_info_dialog/app_info_footer_panel.cc
index 2a1b343..b06e177 100644
--- a/chrome/browser/ui/views/apps/app_info_dialog/app_info_footer_panel.cc
+++ b/chrome/browser/ui/views/apps/app_info_dialog/app_info_footer_panel.cc
@@ -22,7 +22,7 @@
 
 #if defined(USE_ASH)
 #include "ash/common/shelf/shelf_delegate.h"  // nogncheck
-#include "ash/common/wm_shell.h"  // nogncheck
+#include "ash/shell.h"                        // nogncheck
 #include "chrome/browser/ui/ash/launcher/chrome_launcher_controller_util.h"  // nogncheck
 #endif
 
@@ -90,7 +90,7 @@
 #if defined(USE_ASH)
   if (pin_to_shelf_button_ && unpin_from_shelf_button_) {
     bool is_pinned =
-        !ash::WmShell::Get()->shelf_delegate()->IsAppPinned(app_->id());
+        !ash::Shell::Get()->shelf_delegate()->IsAppPinned(app_->id());
     pin_to_shelf_button_->SetVisible(is_pinned);
     unpin_from_shelf_button_->SetVisible(!is_pinned);
 
@@ -152,7 +152,7 @@
 #if defined(USE_ASH)
 void AppInfoFooterPanel::SetPinnedToShelf(bool value) {
   DCHECK(CanSetPinnedToShelf());
-  ash::ShelfDelegate* shelf_delegate = ash::WmShell::Get()->shelf_delegate();
+  ash::ShelfDelegate* shelf_delegate = ash::Shell::Get()->shelf_delegate();
   DCHECK(shelf_delegate);
   if (value)
     shelf_delegate->PinAppWithID(app_->id());
@@ -165,7 +165,7 @@
 
 bool AppInfoFooterPanel::CanSetPinnedToShelf() const {
   // Non-Ash platforms don't have a shelf.
-  if (!ash::WmShell::HasInstance())
+  if (!ash::Shell::HasInstance())
     return false;
 
   // The Chrome app can't be unpinned, and extensions can't be pinned.
diff --git a/chrome/common/BUILD.gn b/chrome/common/BUILD.gn
index 896c0e2..f3a106a 100644
--- a/chrome/common/BUILD.gn
+++ b/chrome/common/BUILD.gn
@@ -696,6 +696,7 @@
     "insecure_content_renderer.mojom",
     "net_benchmarking.mojom",
     "network_diagnostics.mojom",
+    "open_search_description_document_handler.mojom",
     "prerender.mojom",
     "renderer_configuration.mojom",
     "resource_usage_reporter.mojom",
diff --git a/chrome/common/open_search_description_document_handler.mojom b/chrome/common/open_search_description_document_handler.mojom
new file mode 100644
index 0000000..0608937
--- /dev/null
+++ b/chrome/common/open_search_description_document_handler.mojom
@@ -0,0 +1,14 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+module chrome.mojom;
+
+import "url/mojo/url.mojom";
+
+interface OpenSearchDescriptionDocumentHandler {
+  // Notification that the page has an OpenSearch description document
+  // associated with it.
+  PageHasOpenSearchDescriptionDocument(url.mojom.Url page_url,
+                                       url.mojom.Url osdd_url);
+};
diff --git a/chrome/common/render_messages.h b/chrome/common/render_messages.h
index fc9f617..8b9a99b 100644
--- a/chrome/common/render_messages.h
+++ b/chrome/common/render_messages.h
@@ -306,12 +306,6 @@
 IPC_MESSAGE_ROUTED1(ChromeViewHostMsg_CouldNotLoadPlugin,
                     base::FilePath /* plugin_path */)
 
-// Notification that the page has an OpenSearch description document
-// associated with it.
-IPC_MESSAGE_ROUTED2(ChromeViewHostMsg_PageHasOSDD,
-                    GURL /* page_url */,
-                    GURL /* osdd_url */)
-
 // Notifies when a plugin couldn't be loaded because it's outdated.
 IPC_MESSAGE_ROUTED2(ChromeViewHostMsg_BlockedOutdatedPlugin,
                     int /* placeholder ID */,
diff --git a/chrome/renderer/chrome_render_frame_observer.cc b/chrome/renderer/chrome_render_frame_observer.cc
index e13fa81..cb86e0b 100644
--- a/chrome/renderer/chrome_render_frame_observer.cc
+++ b/chrome/renderer/chrome_render_frame_observer.cc
@@ -19,11 +19,13 @@
 #include "chrome/common/chrome_isolated_world_ids.h"
 #include "chrome/common/chrome_switches.h"
 #include "chrome/common/crash_keys.h"
+#include "chrome/common/open_search_description_document_handler.mojom.h"
 #include "chrome/common/prerender_messages.h"
 #include "chrome/common/render_messages.h"
 #include "chrome/renderer/prerender/prerender_helper.h"
 #include "chrome/renderer/safe_browsing/phishing_classifier_delegate.h"
 #include "components/translate/content/renderer/translate_helper.h"
+#include "content/public/common/associated_interface_provider.h"
 #include "content/public/renderer/render_frame.h"
 #include "content/public/renderer/render_view.h"
 #include "extensions/common/constants.h"
@@ -195,8 +197,9 @@
   if (!context_node.isNull() && context_node.isElementNode()) {
     blink::WebImage image = context_node.to<WebElement>().imageContents();
     original_size = image.size();
-    thumbnail =
-        Downscale(image, thumbnail_min_area_pixels, thumbnail_max_size_pixels);
+    thumbnail = Downscale(image,
+                          thumbnail_min_area_pixels,
+                          thumbnail_max_size_pixels);
   }
 
   SkBitmap bitmap;
@@ -249,8 +252,12 @@
 
   GURL osdd_url = frame->document().openSearchDescriptionURL();
   if (!osdd_url.is_empty()) {
-    Send(new ChromeViewHostMsg_PageHasOSDD(
-        routing_id(), frame->document().url(), osdd_url));
+    chrome::mojom::OpenSearchDescriptionDocumentHandlerAssociatedPtr
+        osdd_handler;
+    render_frame()->GetRemoteAssociatedInterfaces()->GetInterface(
+        &osdd_handler);
+    osdd_handler->PageHasOpenSearchDescriptionDocument(
+        frame->document().url(), osdd_url);
   }
 }
 
diff --git a/chrome/test/base/view_event_test_platform_part_chromeos.cc b/chrome/test/base/view_event_test_platform_part_chromeos.cc
index 2483213..fd29a10 100644
--- a/chrome/test/base/view_event_test_platform_part_chromeos.cc
+++ b/chrome/test/base/view_event_test_platform_part_chromeos.cc
@@ -72,7 +72,7 @@
       switches::kHostWindowBounds, "0+0-1280x800");
   ash::Shell::CreateInstance(init_params);
   ash::test::TestSessionControllerClient session_controller_client(
-      ash::WmShell::Get()->session_controller());
+      ash::Shell::Get()->session_controller());
   session_controller_client.CreatePredefinedUserSessions(1);
   GetContext()->GetHost()->Show();
 }
diff --git a/chrome/test/data/banners/no_sw_fetch_handler_test_page.html b/chrome/test/data/banners/no_sw_fetch_handler_test_page.html
new file mode 100644
index 0000000..e1003799
--- /dev/null
+++ b/chrome/test/data/banners/no_sw_fetch_handler_test_page.html
@@ -0,0 +1,11 @@
+<html>
+  <head>
+    <link rel="manifest" href="manifest.json" />
+    <script>
+      navigator.serviceWorker.register('service_worker_no_fetch_handler.js');
+    </script>
+  </head>
+  <body>
+    Page with a service worker missing a fetch handler.
+  </body>
+</html>
diff --git a/chrome/test/data/banners/service_worker.js b/chrome/test/data/banners/service_worker.js
index 1047484e..9752d67 100644
--- a/chrome/test/data/banners/service_worker.js
+++ b/chrome/test/data/banners/service_worker.js
@@ -2,4 +2,5 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-self.addEventListener('activate', function(event) {});
\ No newline at end of file
+// A no-op fetch handler.
+self.addEventListener('fetch', e => {});
\ No newline at end of file
diff --git a/chrome/test/data/banners/service_worker_no_fetch_handler.js b/chrome/test/data/banners/service_worker_no_fetch_handler.js
new file mode 100644
index 0000000..ec70d2b
--- /dev/null
+++ b/chrome/test/data/banners/service_worker_no_fetch_handler.js
@@ -0,0 +1,5 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Intentionally empty.
\ No newline at end of file
diff --git a/components/exo/display.cc b/components/exo/display.cc
index 1fbab0f..6d1e9ca 100644
--- a/components/exo/display.cc
+++ b/components/exo/display.cc
@@ -45,7 +45,7 @@
 
 const gfx::BufferFormat kOverlayFormatsForDrmAtomic[] = {
     gfx::BufferFormat::RGBX_8888, gfx::BufferFormat::RGBA_8888,
-    gfx::BufferFormat::BGR_565};
+    gfx::BufferFormat::BGR_565, gfx::BufferFormat::YUV_420_BIPLANAR};
 #endif
 
 }  // namespace
diff --git a/components/printing/renderer/print_web_view_helper.cc b/components/printing/renderer/print_web_view_helper.cc
index f31074f2..4900973 100644
--- a/components/printing/renderer/print_web_view_helper.cc
+++ b/components/printing/renderer/print_web_view_helper.cc
@@ -680,7 +680,7 @@
       blink::WebLocalFrame* parent,
       blink::WebTreeScopeType scope,
       const blink::WebString& name,
-      const blink::WebString& unique_name,
+      const blink::WebString& fallback_name,
       blink::WebSandboxFlags sandbox_flags,
       const blink::WebFrameOwnerProperties& frame_owner_properties) override;
 
@@ -839,7 +839,7 @@
     blink::WebLocalFrame* parent,
     blink::WebTreeScopeType scope,
     const blink::WebString& name,
-    const blink::WebString& unique_name,
+    const blink::WebString& fallback_name,
     blink::WebSandboxFlags sandbox_flags,
     const blink::WebFrameOwnerProperties& frame_owner_properties) {
   blink::WebLocalFrame* frame =
diff --git a/components/security_state/content/BUILD.gn b/components/security_state/content/BUILD.gn
index 1c046a9..7d3f503 100644
--- a/components/security_state/content/BUILD.gn
+++ b/components/security_state/content/BUILD.gn
@@ -37,6 +37,7 @@
     "//content/public/browser",
     "//content/test:test_support",
     "//net",
+    "//net:test_support",
     "//testing/gtest",
   ]
 }
diff --git a/components/security_state/content/content_utils.cc b/components/security_state/content/content_utils.cc
index ae007c3..8fd95ea 100644
--- a/components/security_state/content/content_utils.cc
+++ b/components/security_state/content/content_utils.cc
@@ -226,6 +226,14 @@
             !!security_info.certificate));
   }
 
+  if (security_info.cert_missing_subject_alt_name) {
+    security_style_explanations->broken_explanations.push_back(
+        content::SecurityStyleExplanation(
+            l10n_util::GetStringUTF8(IDS_SUBJECT_ALT_NAME_MISSING),
+            l10n_util::GetStringUTF8(IDS_SUBJECT_ALT_NAME_MISSING_DESCRIPTION),
+            !!security_info.certificate));
+  }
+
   // Record the presence of mixed content (HTTP subresources on an HTTPS
   // page).
   security_style_explanations->ran_mixed_content =
diff --git a/components/security_state/content/content_utils_unittest.cc b/components/security_state/content/content_utils_unittest.cc
index 41395a8e..9e53bae 100644
--- a/components/security_state/content/content_utils_unittest.cc
+++ b/components/security_state/content/content_utils_unittest.cc
@@ -13,6 +13,8 @@
 #include "net/cert/cert_status_flags.h"
 #include "net/ssl/ssl_cipher_suite_names.h"
 #include "net/ssl/ssl_connection_status_flags.h"
+#include "net/test/cert_test_util.h"
+#include "net/test/test_data_directory.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace {
@@ -232,4 +234,28 @@
   EXPECT_EQ(1u, explanations.unauthenticated_explanations.size());
 }
 
+// Tests that an explanation is provided if a certificate is missing a
+// subjectAltName extension containing a domain name or IP address.
+TEST(SecurityStateContentUtilsTest, SubjectAltNameWarning) {
+  security_state::SecurityInfo security_info;
+  security_info.cert_status = 0;
+  security_info.scheme_is_cryptographic = true;
+
+  security_info.certificate = net::ImportCertFromFile(
+      net::GetTestCertsDirectory(), "salesforce_com_test.pem");
+  ASSERT_TRUE(security_info.certificate);
+
+  content::SecurityStyleExplanations explanations;
+  security_info.cert_missing_subject_alt_name = true;
+  GetSecurityStyle(security_info, &explanations);
+  // Verify that an explanation was shown for a missing subjectAltName.
+  EXPECT_EQ(1u, explanations.broken_explanations.size());
+
+  explanations.broken_explanations.clear();
+  security_info.cert_missing_subject_alt_name = false;
+  GetSecurityStyle(security_info, &explanations);
+  // Verify that no explanation is shown if the subjectAltName is present.
+  EXPECT_EQ(0u, explanations.broken_explanations.size());
+}
+
 }  // namespace
diff --git a/components/security_state/core/security_state.cc b/components/security_state/core/security_state.cc
index 577d93b..00368ec 100644
--- a/components/security_state/core/security_state.cc
+++ b/components/security_state/core/security_state.cc
@@ -222,6 +222,11 @@
       visible_security_state.displayed_password_field_on_http;
   security_info->displayed_credit_card_field_on_http =
       visible_security_state.displayed_credit_card_field_on_http;
+  if (visible_security_state.certificate) {
+    security_info->cert_missing_subject_alt_name =
+        !visible_security_state.certificate->GetSubjectAltName(nullptr,
+                                                               nullptr);
+  }
 
   security_info->security_level = GetSecurityLevelForRequest(
       visible_security_state, used_policy_installed_certificate,
@@ -249,7 +254,8 @@
       obsolete_ssl_status(net::OBSOLETE_SSL_NONE),
       pkp_bypassed(false),
       displayed_password_field_on_http(false),
-      displayed_credit_card_field_on_http(false) {}
+      displayed_credit_card_field_on_http(false),
+      cert_missing_subject_alt_name(false) {}
 
 SecurityInfo::~SecurityInfo() {}
 
diff --git a/components/security_state/core/security_state.h b/components/security_state/core/security_state.h
index 7de0fa74..480d25ae 100644
--- a/components/security_state/core/security_state.h
+++ b/components/security_state/core/security_state.h
@@ -142,6 +142,10 @@
 
   // True if the page displayed credit card field on an HTTP page.
   bool displayed_credit_card_field_on_http;
+
+  // True if the server's certificate does not contain a
+  // subjectAltName extension with a domain name or IP address.
+  bool cert_missing_subject_alt_name;
 };
 
 // Contains the security state relevant to computing the SecurityInfo
diff --git a/components/security_state/core/security_state_unittest.cc b/components/security_state/core/security_state_unittest.cc
index 9b9ddad..5a9bbc7 100644
--- a/components/security_state/core/security_state_unittest.cc
+++ b/components/security_state/core/security_state_unittest.cc
@@ -51,6 +51,9 @@
         displayed_credit_card_field_on_http_(false) {}
   virtual ~TestSecurityStateHelper() {}
 
+  void SetCertificate(scoped_refptr<net::X509Certificate> cert) {
+    cert_ = std::move(cert);
+  }
   void set_connection_status(int connection_status) {
     connection_status_ = connection_status;
   }
@@ -107,7 +110,7 @@
 
  private:
   GURL url_;
-  const scoped_refptr<net::X509Certificate> cert_;
+  scoped_refptr<net::X509Certificate> cert_;
   int connection_status_;
   net::CertStatus cert_status_;
   bool displayed_mixed_content_;
@@ -366,4 +369,24 @@
   histograms.ExpectUniqueSample(kHistogramName, 2 /* HTTP_SHOW_WARNING */, 2);
 }
 
+TEST(SecurityStateTest, DetectSubjectAltName) {
+  TestSecurityStateHelper helper;
+
+  // Ensure subjectAltName is detected as present when the cert includes it.
+  SecurityInfo san_security_info;
+  helper.GetSecurityInfo(&san_security_info);
+  EXPECT_FALSE(san_security_info.cert_missing_subject_alt_name);
+
+  // Ensure subjectAltName is detected as missing when the cert doesn't
+  // include it.
+  scoped_refptr<net::X509Certificate> cert = net::ImportCertFromFile(
+      net::GetTestCertsDirectory(), "salesforce_com_test.pem");
+  ASSERT_TRUE(cert);
+  helper.SetCertificate(std::move(cert));
+
+  SecurityInfo no_san_security_info;
+  helper.GetSecurityInfo(&no_san_security_info);
+  EXPECT_TRUE(no_san_security_info.cert_missing_subject_alt_name);
+}
+
 }  // namespace security_state
diff --git a/components/security_state_strings.grdp b/components/security_state_strings.grdp
index a193d68..d3580a9 100644
--- a/components/security_state_strings.grdp
+++ b/components/security_state_strings.grdp
@@ -13,6 +13,12 @@
   <message name="IDS_SHA1_DESCRIPTION" desc="Description of a security problem where the site's certificate chain contains a SHA1 signature." translateable="false">
     The certificate chain for this site contains a certificate signed using SHA-1.
   </message>
+  <message name="IDS_SUBJECT_ALT_NAME_MISSING" desc="Summary phrase for a security problem where the site's certificate is missing a subjectAltName extension." translateable="false">
+    Subject Alternative Name Missing
+  </message>
+  <message name="IDS_SUBJECT_ALT_NAME_MISSING_DESCRIPTION" desc="Description of a security problem where the site's certificate is missing a subjectAltName extension." translateable="false">
+    The certificate for this site does not contain a Subject Alternative Name extension containing a domain name or IP address.
+  </message>
   <message name="IDS_CERTIFICATE_CHAIN_ERROR" desc="Summary phrase for a security problem with the site's certificate." translateable="false">
     Certificate Error
   </message>
diff --git a/content/browser/loader/reload_cache_control_browsertest.cc b/content/browser/loader/reload_cache_control_browsertest.cc
index 411f7658..f2208d0 100644
--- a/content/browser/loader/reload_cache_control_browsertest.cc
+++ b/content/browser/loader/reload_cache_control_browsertest.cc
@@ -250,11 +250,6 @@
     EXPECT_EQ(kNoCacheControl, request_log_[3].cache_control);
   }
 
-  // TODO(crbug.com/671545): This test does not work correctly if browser-side
-  // navigation is enabled.
-  if (IsBrowserSideNavigationEnabled())
-    return;
-
   // The second navigation is the same page navigation. This should be handled
   // as a reload, revalidating the main resource, but following cache protocols
   // for others.
diff --git a/content/browser/service_worker/service_worker_context_core.cc b/content/browser/service_worker/service_worker_context_core.cc
index a0bbc66..53997c63 100644
--- a/content/browser/service_worker/service_worker_context_core.cc
+++ b/content/browser/service_worker/service_worker_context_core.cc
@@ -32,6 +32,7 @@
 #include "content/browser/service_worker/service_worker_register_job.h"
 #include "content/browser/service_worker/service_worker_registration.h"
 #include "content/browser/service_worker/service_worker_storage.h"
+#include "content/browser/service_worker/service_worker_version.h"
 #include "content/common/service_worker/service_worker_utils.h"
 #include "content/public/browser/browser_thread.h"
 #include "ipc/ipc_message.h"
@@ -43,6 +44,26 @@
 namespace content {
 namespace {
 
+void CheckFetchHandlerOfInstalledServiceWorker(
+    const ServiceWorkerContext::CheckHasServiceWorkerCallback callback,
+    scoped_refptr<ServiceWorkerRegistration> registration) {
+  // Waiting Service Worker is a newer version, prefer that if available.
+  ServiceWorkerVersion* preferred_version =
+      registration->waiting_version() ? registration->waiting_version()
+                                      : registration->active_version();
+
+  DCHECK(preferred_version);
+
+  ServiceWorkerVersion::FetchHandlerExistence existence =
+      preferred_version->fetch_handler_existence();
+
+  DCHECK_NE(existence, ServiceWorkerVersion::FetchHandlerExistence::UNKNOWN);
+
+  callback.Run(existence == ServiceWorkerVersion::FetchHandlerExistence::EXISTS
+                   ? ServiceWorkerCapability::SERVICE_WORKER_WITH_FETCH_HANDLER
+                   : ServiceWorkerCapability::SERVICE_WORKER_NO_FETCH_HANDLER);
+}
+
 void SuccessCollectorCallback(const base::Closure& done_closure,
                               bool* overall_success,
                               ServiceWorkerStatusCode status) {
@@ -856,17 +877,17 @@
     ServiceWorkerStatusCode status,
     scoped_refptr<ServiceWorkerRegistration> registration) {
   if (status != SERVICE_WORKER_OK) {
-    callback.Run(false);
+    callback.Run(ServiceWorkerCapability::NO_SERVICE_WORKER);
     return;
   }
 
   if (!ServiceWorkerUtils::ScopeMatches(registration->pattern(), other_url)) {
-    callback.Run(false);
+    callback.Run(ServiceWorkerCapability::NO_SERVICE_WORKER);
     return;
   }
 
   if (registration->is_uninstalling() || registration->is_uninstalled()) {
-    callback.Run(false);
+    callback.Run(ServiceWorkerCapability::NO_SERVICE_WORKER);
     return;
   }
 
@@ -878,14 +899,18 @@
     return;
   }
 
-  callback.Run(true);
+  CheckFetchHandlerOfInstalledServiceWorker(callback, registration);
 }
 
 void ServiceWorkerContextCore::OnRegistrationFinishedForCheckHasServiceWorker(
     const ServiceWorkerContext::CheckHasServiceWorkerCallback callback,
     scoped_refptr<ServiceWorkerRegistration> registration) {
-  callback.Run(registration->active_version() ||
-               registration->waiting_version());
+  if (!registration->active_version() && !registration->waiting_version()) {
+    callback.Run(ServiceWorkerCapability::NO_SERVICE_WORKER);
+    return;
+  }
+
+  CheckFetchHandlerOfInstalledServiceWorker(callback, registration);
 }
 
 }  // namespace content
diff --git a/content/browser/service_worker/service_worker_context_wrapper.cc b/content/browser/service_worker/service_worker_context_wrapper.cc
index 9f3c72b8..62068edd0 100644
--- a/content/browser/service_worker/service_worker_context_wrapper.cc
+++ b/content/browser/service_worker/service_worker_context_wrapper.cc
@@ -481,11 +481,11 @@
 
 void ServiceWorkerContextWrapper::DidCheckHasServiceWorker(
     const CheckHasServiceWorkerCallback& callback,
-    bool has_service_worker) {
+    ServiceWorkerCapability capability) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
 
   BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
-                          base::Bind(callback, has_service_worker));
+                          base::Bind(callback, capability));
 }
 
 void ServiceWorkerContextWrapper::StopAllServiceWorkersForOrigin(
@@ -570,8 +570,9 @@
     return;
   }
   if (!context_core_) {
-    BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
-                            base::Bind(callback, false));
+    BrowserThread::PostTask(
+        BrowserThread::UI, FROM_HERE,
+        base::Bind(callback, ServiceWorkerCapability::NO_SERVICE_WORKER));
     return;
   }
   context()->CheckHasServiceWorker(
diff --git a/content/browser/service_worker/service_worker_context_wrapper.h b/content/browser/service_worker/service_worker_context_wrapper.h
index 04001b9c..33fa91c 100644
--- a/content/browser/service_worker/service_worker_context_wrapper.h
+++ b/content/browser/service_worker/service_worker_context_wrapper.h
@@ -270,7 +270,7 @@
       const std::vector<ServiceWorkerRegistrationInfo>& registrations);
 
   void DidCheckHasServiceWorker(const CheckHasServiceWorkerCallback& callback,
-                                bool has_service_worker);
+                                content::ServiceWorkerCapability status);
 
   void DidFindRegistrationForUpdate(
       ServiceWorkerStatusCode status,
diff --git a/content/browser/site_per_process_browsertest.cc b/content/browser/site_per_process_browsertest.cc
index 8067488dd..470e220 100644
--- a/content/browser/site_per_process_browsertest.cc
+++ b/content/browser/site_per_process_browsertest.cc
@@ -9,6 +9,7 @@
 
 #include <algorithm>
 #include <map>
+#include <set>
 #include <vector>
 
 #include "base/bind.h"
@@ -27,8 +28,11 @@
 #include "base/threading/thread_task_runner_handle.h"
 #include "build/build_config.h"
 #include "content/browser/frame_host/cross_process_frame_connector.h"
+#include "content/browser/frame_host/frame_navigation_entry.h"
 #include "content/browser/frame_host/frame_tree.h"
 #include "content/browser/frame_host/interstitial_page_impl.h"
+#include "content/browser/frame_host/navigation_controller_impl.h"
+#include "content/browser/frame_host/navigation_entry_impl.h"
 #include "content/browser/frame_host/navigator.h"
 #include "content/browser/frame_host/render_frame_proxy_host.h"
 #include "content/browser/frame_host/render_widget_host_view_child_frame.h"
@@ -101,6 +105,8 @@
 #include "content/browser/renderer_host/render_widget_host_view_android.h"
 #endif
 
+using ::testing::SizeIs;
+
 namespace content {
 
 namespace {
@@ -9906,4 +9912,46 @@
             root->child_at(0)->current_frame_host()->GetSiteInstance());
 }
 
+IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest,
+                       FrameSwapPreservesUniqueName) {
+  GURL main_url(embedded_test_server()->GetURL(
+      "a.com", "/cross_site_iframe_factory.html?a(a)"));
+  ASSERT_TRUE(NavigateToURL(shell(), main_url));
+
+  // Navigate the subframe cross-site…
+  {
+    GURL url(embedded_test_server()->GetURL("b.com", "/title1.html"));
+    EXPECT_TRUE(NavigateIframeToURL(shell()->web_contents(), "child-0", url));
+  }
+  // and then same-site…
+  {
+    GURL url(embedded_test_server()->GetURL("a.com", "/title1.html"));
+    EXPECT_TRUE(NavigateIframeToURL(shell()->web_contents(), "child-0", url));
+  }
+  // and cross-site once more.
+  {
+    GURL url(embedded_test_server()->GetURL("b.com", "/title1.html"));
+    EXPECT_TRUE(NavigateIframeToURL(shell()->web_contents(), "child-0", url));
+  }
+
+  // Inspect the navigation entries and make sure that the navigation target
+  // remained constant across frame swaps.
+  const auto& controller = static_cast<const NavigationControllerImpl&>(
+      shell()->web_contents()->GetController());
+  EXPECT_EQ(4, controller.GetEntryCount());
+
+  std::set<std::string> names;
+  for (int i = 0; i < controller.GetEntryCount(); ++i) {
+    NavigationEntryImpl::TreeNode* root =
+        controller.GetEntryAtIndex(i)->root_node();
+    ASSERT_EQ(1U, root->children.size());
+    names.insert(root->children[0]->frame_entry->frame_unique_name());
+  }
+
+  // More than one entry in the set means that the subframe frame navigation
+  // entries didn't have a consistent unique name. This will break history
+  // navigations =(
+  EXPECT_THAT(names, SizeIs(1)) << "Mismatched names for subframe!";
+}
+
 }  // namespace content
diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc
index 12a9dda..093f1f22 100644
--- a/content/browser/web_contents/web_contents_impl.cc
+++ b/content/browser/web_contents/web_contents_impl.cc
@@ -1025,6 +1025,14 @@
                     weak_factory_.GetWeakPtr(), interface_name);
 }
 
+WebContentsBindingSet* WebContentsImpl::GetBindingSet(
+    const std::string& interface_name) {
+  auto it = binding_sets_.find(interface_name);
+  if (it == binding_sets_.end())
+    return nullptr;
+  return it->second;
+}
+
 void WebContentsImpl::UpdateDeviceScaleFactor(double device_scale_factor) {
   SendPageMessage(
       new PageMsg_SetDeviceScaleFactor(MSG_ROUTING_NONE, device_scale_factor));
diff --git a/content/browser/web_contents/web_contents_impl.h b/content/browser/web_contents/web_contents_impl.h
index f320c21..ab1ed32 100644
--- a/content/browser/web_contents/web_contents_impl.h
+++ b/content/browser/web_contents/web_contents_impl.h
@@ -262,17 +262,10 @@
   base::Closure AddBindingSet(const std::string& interface_name,
                               WebContentsBindingSet* binding_set);
 
-  // Overrides the incoming Channel-associated interface request handler for
-  // interfaces registered with this WebContents via a WebContentsBindingSet.
-  // |binder| will live until either the binder is overridden again or the
-  // WebContents is destroyed.
-  template <typename Interface>
-  void OverrideBinderForTesting(
-      std::unique_ptr<WebContentsBindingSet::Binder> binder) {
-    auto it = binding_sets_.find(Interface::Name_);
-    DCHECK(it != binding_sets_.end());
-    it->second->SetBinderForTesting(std::move(binder));
-  }
+  // Accesses a WebContentsBindingSet for a specific interface on this
+  // WebContents. Returns null of there is no registered binder for the
+  // interface.
+  WebContentsBindingSet* GetBindingSet(const std::string& interface_name);
 
   // WebContents ------------------------------------------------------
   WebContentsDelegate* GetDelegate() override;
diff --git a/content/browser/web_contents_binding_set_browsertest.cc b/content/browser/web_contents_binding_set_browsertest.cc
index fcaedec..970a9831 100644
--- a/content/browser/web_contents_binding_set_browsertest.cc
+++ b/content/browser/web_contents_binding_set_browsertest.cc
@@ -45,24 +45,26 @@
 
   // Ensure that we can add a WebContentsFrameBindingSet and then override its
   // request handler.
-  auto* web_contents = static_cast<WebContentsImpl*>(shell()->web_contents());
+  auto* web_contents = shell()->web_contents();
   WebContentsFrameBindingSet<mojom::BrowserAssociatedInterfaceTestDriver>
       frame_bindings(web_contents, nullptr);
 
   // Now override the binder for this interface. It quits |run_loop| whenever
   // an incoming interface request is received.
   base::RunLoop run_loop;
-  web_contents
-      ->OverrideBinderForTesting<mojom::BrowserAssociatedInterfaceTestDriver>(
+  WebContentsBindingSet::GetForWebContents<
+      mojom::BrowserAssociatedInterfaceTestDriver>(web_contents)
+      ->SetBinderForTesting(
           base::MakeUnique<TestInterfaceBinder>(run_loop.QuitClosure()));
 
   // Simulate an inbound request for the test interface. This should get routed
   // to the overriding binder and allow the test to complete.
   mojom::BrowserAssociatedInterfaceTestDriverAssociatedPtr override_client;
-  web_contents->OnAssociatedInterfaceRequest(
-      web_contents->GetMainFrame(),
-      mojom::BrowserAssociatedInterfaceTestDriver::Name_,
-      mojo::MakeIsolatedRequest(&override_client).PassHandle());
+  static_cast<WebContentsImpl*>(web_contents)
+      ->OnAssociatedInterfaceRequest(
+          web_contents->GetMainFrame(),
+          mojom::BrowserAssociatedInterfaceTestDriver::Name_,
+          mojo::MakeIsolatedRequest(&override_client).PassHandle());
   run_loop.Run();
 }
 
diff --git a/content/public/browser/service_worker_context.h b/content/public/browser/service_worker_context.h
index c095f9d..90c3d93 100644
--- a/content/public/browser/service_worker_context.h
+++ b/content/public/browser/service_worker_context.h
@@ -18,6 +18,12 @@
 
 namespace content {
 
+enum class ServiceWorkerCapability {
+  NO_SERVICE_WORKER,
+  SERVICE_WORKER_NO_FETCH_HANDLER,
+  SERVICE_WORKER_WITH_FETCH_HANDLER,
+};
+
 // Represents the per-StoragePartition ServiceWorker data.
 class ServiceWorkerContext {
  public:
@@ -31,7 +37,7 @@
       const std::vector<ServiceWorkerUsageInfo>& usage_info)>;
 
   using CheckHasServiceWorkerCallback =
-      base::Callback<void(bool has_service_worker)>;
+      base::Callback<void(ServiceWorkerCapability capability)>;
 
   using CountExternalRequestsCallback =
       base::Callback<void(size_t external_request_count)>;
@@ -102,10 +108,14 @@
   virtual void DeleteForOrigin(const GURL& origin_url,
                                const ResultCallback& callback) = 0;
 
-  // Returns true if a Service Worker registration exists that matches |url|,
-  // and if |other_url| falls inside the scope of the same registration. Note
-  // this still returns true even if there is a Service Worker registration
-  // which has a longer match for |other_url|.
+  // Returns ServiceWorkerCapability describing existence and properties of a
+  // Service Worker registration matching |url|. Found service worker
+  // registration must also encompass the |other_url|, otherwise it will be
+  // considered non existent by this method. Note that the longest matching
+  // registration for |url| is described, which is not necessarily the longest
+  // matching registration for |other_url|. In case the service worker is being
+  // installed as of calling this method, it will wait for the installation to
+  // finish before coming back with the result.
   //
   // This function can be called from any thread, but the callback will always
   // be called on the UI thread.
diff --git a/content/public/browser/web_contents_binding_set.cc b/content/public/browser/web_contents_binding_set.cc
index 39e9711..0ef198a 100644
--- a/content/public/browser/web_contents_binding_set.cc
+++ b/content/public/browser/web_contents_binding_set.cc
@@ -28,6 +28,14 @@
   remove_callback_.Run();
 }
 
+// static
+WebContentsBindingSet* WebContentsBindingSet::GetForWebContents(
+    WebContents* web_contents,
+    const char* interface_name) {
+  return static_cast<WebContentsImpl*>(web_contents)
+      ->GetBindingSet(interface_name);
+}
+
 void WebContentsBindingSet::CloseAllBindings() {
   binder_for_testing_.reset();
   binder_.reset();
diff --git a/content/public/browser/web_contents_binding_set.h b/content/public/browser/web_contents_binding_set.h
index b8d5ff2..6c89f23 100644
--- a/content/public/browser/web_contents_binding_set.h
+++ b/content/public/browser/web_contents_binding_set.h
@@ -39,6 +39,11 @@
     binder_for_testing_ = std::move(binder);
   }
 
+  template <typename Interface>
+  static WebContentsBindingSet* GetForWebContents(WebContents* web_contents) {
+    return GetForWebContents(web_contents, Interface::Name_);
+  }
+
  protected:
   WebContentsBindingSet(WebContents* web_contents,
                         const std::string& interface_name,
@@ -48,6 +53,9 @@
  private:
   friend class WebContentsImpl;
 
+  static WebContentsBindingSet* GetForWebContents(WebContents* web_contents,
+                                                  const char* interface_name);
+
   void CloseAllBindings();
   void OnRequestForFrame(RenderFrameHost* render_frame_host,
                          mojo::ScopedInterfaceEndpointHandle handle);
diff --git a/content/public/test/test_runner_support.h b/content/public/test/test_runner_support.h
new file mode 100644
index 0000000..12c32d74
--- /dev/null
+++ b/content/public/test/test_runner_support.h
@@ -0,0 +1,21 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_PUBLIC_TEST_TEST_RUNNER_SUPPORT_H_
+#define CONTENT_PUBLIC_TEST_TEST_RUNNER_SUPPORT_H_
+
+#include <string>
+
+namespace blink {
+class WebLocalFrame;
+}
+
+namespace content {
+
+// Returns the unique name associated with the given frame.
+std::string GetUniqueNameForFrame(blink::WebLocalFrame* frame);
+
+}  // namespace content
+
+#endif  // CONTENT_PUBLIC_TEST_TEST_RUNNER_SUPPORT_H_
diff --git a/content/renderer/BUILD.gn b/content/renderer/BUILD.gn
index 2badf76..137d6bc 100644
--- a/content/renderer/BUILD.gn
+++ b/content/renderer/BUILD.gn
@@ -377,6 +377,8 @@
     "theme_helper_mac.mm",
     "top_level_blame_context.cc",
     "top_level_blame_context.h",
+    "unique_name_helper.cc",
+    "unique_name_helper.h",
     "web_frame_utils.cc",
     "web_frame_utils.h",
     "web_ui_extension.cc",
diff --git a/content/renderer/history_entry.cc b/content/renderer/history_entry.cc
index e74299a..90f7525b 100644
--- a/content/renderer/history_entry.cc
+++ b/content/renderer/history_entry.cc
@@ -174,7 +174,7 @@
     RenderFrameImpl* frame) {
   if (!frame->GetWebFrame()->parent())
     return root_history_node();
-  return unique_names_to_items_[frame->GetWebFrame()->uniqueName().utf8()];
+  return unique_names_to_items_[frame->unique_name()];
 }
 
 WebHistoryItem HistoryEntry::GetItemForFrame(RenderFrameImpl* frame) {
diff --git a/content/renderer/render_frame_impl.cc b/content/renderer/render_frame_impl.cc
index b1dfbf1..906788e4 100644
--- a/content/renderer/render_frame_impl.cc
+++ b/content/renderer/render_frame_impl.cc
@@ -994,9 +994,10 @@
     render_frame =
         RenderFrameImpl::Create(parent_proxy->render_view(), routing_id);
     render_frame->InitializeBlameContext(FromRoutingID(parent_routing_id));
+    render_frame->unique_name_helper_.set_propagated_name(
+        replicated_state.unique_name);
     web_frame = parent_web_frame->createLocalChild(
         replicated_state.scope, WebString::fromUTF8(replicated_state.name),
-        WebString::fromUTF8(replicated_state.unique_name),
         replicated_state.sandbox_flags, render_frame,
         render_frame->blink_interface_provider_.get(),
         render_frame->blink_interface_registry_.get(),
@@ -1095,6 +1096,7 @@
 RenderFrameImpl::RenderFrameImpl(const CreateParams& params)
     : frame_(NULL),
       is_main_frame_(true),
+      unique_name_helper_(this),
       in_browser_initiated_detach_(false),
       in_frame_tree_(false),
       render_view_(params.render_view),
@@ -3045,7 +3047,7 @@
     blink::WebLocalFrame* parent,
     blink::WebTreeScopeType scope,
     const blink::WebString& name,
-    const blink::WebString& unique_name,
+    const blink::WebString& fallback_name,
     blink::WebSandboxFlags sandbox_flags,
     const blink::WebFrameOwnerProperties& frame_owner_properties) {
   // Synchronously notify the browser of a child frame creation to get the
@@ -3055,7 +3057,23 @@
   params.parent_routing_id = routing_id_;
   params.scope = scope;
   params.frame_name = name.utf8();
-  params.frame_unique_name = unique_name.utf8();
+  // The unique name generation logic was moved out of Blink, so for historical
+  // reasons, unique name generation needs to take something called the
+  // |fallback_name| into account. Normally, unique names are generated based on
+  // the browing context name. For new frames, the initial browsing context name
+  // comes from the name attribute of the browsing context container element.
+  //
+  // However, when the browsing context name is null, Blink instead uses the
+  // "fallback name" to derive the unique name. The exact contents of the
+  // "fallback name" are unspecified, but may contain the value of the
+  // 'subresource attribute' of the browsing context container element.
+  //
+  // Note that Blink can't be changed to just pass |fallback_name| as |name| in
+  // the case |name| is empty: |fallback_name| should never affect the actual
+  // browsing context name, only unique name generation.
+  params.frame_unique_name = UniqueNameHelper::GenerateNameForNewChildFrame(
+      parent,
+      params.frame_name.empty() ? fallback_name.utf8() : params.frame_name);
   params.sandbox_flags = sandbox_flags;
   params.frame_owner_properties =
       ConvertWebFrameOwnerPropertiesToFrameOwnerProperties(
@@ -3080,6 +3098,8 @@
   // Create the RenderFrame and WebLocalFrame, linking the two.
   RenderFrameImpl* child_render_frame =
       RenderFrameImpl::Create(render_view_, child_routing_id);
+  child_render_frame->unique_name_helper_.set_propagated_name(
+      params.frame_unique_name);
   child_render_frame->InitializeBlameContext(this);
   blink::WebLocalFrame* web_frame = WebLocalFrame::create(
       scope, child_render_frame,
@@ -3188,10 +3208,14 @@
     observer.WillCommitProvisionalLoad();
 }
 
-void RenderFrameImpl::didChangeName(const blink::WebString& name,
-                                    const blink::WebString& unique_name) {
-  Send(new FrameHostMsg_DidChangeName(
-      routing_id_, name.utf8(), unique_name.utf8()));
+void RenderFrameImpl::didChangeName(const blink::WebString& name) {
+  if (current_history_item_.isNull()) {
+    // Once a navigation has committed, the unique name must no longer change to
+    // avoid breaking back/forward navigations: https://crbug.com/607205
+    unique_name_helper_.UpdateName(name.utf8());
+  }
+  Send(new FrameHostMsg_DidChangeName(routing_id_, name.utf8(),
+                                      unique_name_helper_.value()));
 
   if (!committed_first_load_)
     name_changed_before_first_commit_ = true;
@@ -3626,6 +3650,9 @@
           "SessionRestore.SubFrameUniqueNameChangedBeforeFirstCommit",
           name_changed_before_first_commit_);
     }
+    // TODO(dcheng): This signal is likely calculated incorrectly, and will be
+    // removed in a followup CL (as we've decided to try to preserve backwards
+    // compatibility as much as possible for the time being).
     committed_first_load_ = true;
   }
 
@@ -3681,9 +3708,12 @@
   // before updating the current history item.
   SendUpdateState();
 
-  // Update the current history item for this frame (both in default Chrome and
-  // subframe FrameNavigationEntry modes).
+  // Update the current history item for this frame.
   current_history_item_ = item;
+  // Note: don't reference |item| after this point, as its value may not match
+  // |current_history_item_|.
+  current_history_item_.setTarget(
+      blink::WebString::fromUTF8(unique_name_helper_.value()));
 
   InternalDocumentStateData* internal_data =
       InternalDocumentStateData::FromDocumentState(document_state);
@@ -3748,7 +3778,7 @@
   // new navigation.
   navigation_state->set_request_committed(true);
 
-  SendDidCommitProvisionalLoad(frame, commit_type, item);
+  SendDidCommitProvisionalLoad(frame, commit_type);
 
   // Check whether we have new encoding name.
   UpdateEncoding(frame, frame->view()->pageEncoding().utf8());
@@ -4829,8 +4859,7 @@
 // Tell the embedding application that the URL of the active page has changed.
 void RenderFrameImpl::SendDidCommitProvisionalLoad(
     blink::WebFrame* frame,
-    blink::WebHistoryCommitType commit_type,
-    const blink::WebHistoryItem& item) {
+    blink::WebHistoryCommitType commit_type) {
   DCHECK_EQ(frame_, frame);
   WebDataSource* ds = frame->dataSource();
   DCHECK(ds);
@@ -4903,17 +4932,18 @@
   // that committed entry has it at all times.  Send a single HistoryItem for
   // this frame, rather than the whole tree.  It will be stored in the
   // corresponding FrameNavigationEntry.
-  params.page_state = SingleHistoryItemToPageState(item);
+  params.page_state = SingleHistoryItemToPageState(current_history_item_);
 
   params.content_source_id = GetRenderWidget()->GetContentSourceId();
 
   params.method = request.httpMethod().latin1();
   if (params.method == "POST")
-    params.post_id = ExtractPostId(item);
+    params.post_id = ExtractPostId(current_history_item_);
 
-  params.frame_unique_name = item.target().utf8();
-  params.item_sequence_number = item.itemSequenceNumber();
-  params.document_sequence_number = item.documentSequenceNumber();
+  params.frame_unique_name = current_history_item_.target().utf8();
+  params.item_sequence_number = current_history_item_.itemSequenceNumber();
+  params.document_sequence_number =
+      current_history_item_.documentSequenceNumber();
 
   // If the page contained a client redirect (meta refresh, document.loc...),
   // set the referrer appropriately.
@@ -5062,6 +5092,10 @@
   RenderFrameProxy* proxy = RenderFrameProxy::FromRoutingID(proxy_routing_id_);
   CHECK(proxy);
 
+  unique_name_helper_.set_propagated_name(proxy->unique_name());
+
+  // Note: Calling swap() will detach and delete |proxy|, so do not reference it
+  // after this.
   int proxy_routing_id = proxy_routing_id_;
   if (!proxy->web_frame()->swap(frame_))
     return false;
@@ -5346,13 +5380,13 @@
     // just staying at the initial about:blank document.
     bool should_ask_browser = false;
     RenderFrameImpl* parent = RenderFrameImpl::FromWebFrame(frame_->parent());
-    const auto& iter = parent->history_subframe_unique_names_.find(
-        frame_->uniqueName().utf8());
+    auto iter = parent->history_subframe_unique_names_.find(
+        unique_name_helper_.value());
     if (iter != parent->history_subframe_unique_names_.end()) {
       bool history_item_is_about_blank = iter->second;
       should_ask_browser =
           !history_item_is_about_blank || url != url::kAboutBlankURL;
-      parent->history_subframe_unique_names_.erase(frame_->uniqueName().utf8());
+      parent->history_subframe_unique_names_.erase(iter);
     }
 
     if (should_ask_browser) {
diff --git a/content/renderer/render_frame_impl.h b/content/renderer/render_frame_impl.h
index 36116314..3ddeaae 100644
--- a/content/renderer/render_frame_impl.h
+++ b/content/renderer/render_frame_impl.h
@@ -44,6 +44,7 @@
 #include "content/renderer/frame_blame_context.h"
 #include "content/renderer/mojo/blink_interface_provider_impl.h"
 #include "content/renderer/renderer_webcookiejar_impl.h"
+#include "content/renderer/unique_name_helper.h"
 #include "ipc/ipc_message.h"
 #include "ipc/ipc_platform_file.h"
 #include "media/blink/webmediaplayer_delegate.h"
@@ -261,6 +262,9 @@
   // Draw commands have been issued by RenderWidgetCompositor.
   void DidCommitAndDrawCompositorFrame();
 
+  // Returns the unique name of the RenderFrame.
+  const std::string& unique_name() const { return unique_name_helper_.value(); }
+
   // TODO(jam): this is a temporary getter until all the code is transitioned
   // to using RenderFrame instead of RenderView.
   RenderViewImpl* render_view() { return render_view_; }
@@ -507,15 +511,14 @@
       blink::WebLocalFrame* parent,
       blink::WebTreeScopeType scope,
       const blink::WebString& name,
-      const blink::WebString& unique_name,
+      const blink::WebString& fallback_name,
       blink::WebSandboxFlags sandbox_flags,
       const blink::WebFrameOwnerProperties& frame_owner_properties) override;
   void didChangeOpener(blink::WebFrame* frame) override;
   void frameDetached(blink::WebLocalFrame* frame, DetachType type) override;
   void frameFocused() override;
   void willCommitProvisionalLoad() override;
-  void didChangeName(const blink::WebString& name,
-                     const blink::WebString& unique_name) override;
+  void didChangeName(const blink::WebString& name) override;
   void didEnforceInsecureRequestPolicy(
       blink::WebInsecureRequestPolicy policy) override;
   void didUpdateToUniqueOrigin(
@@ -810,8 +813,7 @@
 
   // Builds and sends DidCommitProvisionalLoad to the host.
   void SendDidCommitProvisionalLoad(blink::WebFrame* frame,
-                                    blink::WebHistoryCommitType commit_type,
-                                    const blink::WebHistoryItem& item);
+                                    blink::WebHistoryCommitType commit_type);
 
   // Swaps the current frame into the frame tree, replacing the
   // RenderFrameProxy it is associated with.  Return value indicates whether
@@ -1129,6 +1131,8 @@
   // |frame_| has been invalidated.
   bool is_main_frame_;
 
+  UniqueNameHelper unique_name_helper_;
+
   // When a frame is detached in response to a message from the browser process,
   // this RenderFrame should not be sending notifications back to it. This
   // boolean is used to indicate this case.
diff --git a/content/renderer/render_frame_proxy.cc b/content/renderer/render_frame_proxy.cc
index 006858b6..d6a89ce 100644
--- a/content/renderer/render_frame_proxy.cc
+++ b/content/renderer/render_frame_proxy.cc
@@ -61,6 +61,7 @@
   CHECK_NE(routing_id, MSG_ROUTING_NONE);
 
   std::unique_ptr<RenderFrameProxy> proxy(new RenderFrameProxy(routing_id));
+  proxy->unique_name_ = frame_to_replace->unique_name();
 
   // When a RenderFrame is replaced by a RenderProxy, the WebRemoteFrame should
   // always come from WebRemoteFrame::create and a call to WebFrame::swap must
@@ -126,8 +127,8 @@
     web_frame = parent->web_frame()->createRemoteChild(
         replicated_state.scope,
         blink::WebString::fromUTF8(replicated_state.name),
-        blink::WebString::fromUTF8(replicated_state.unique_name),
         replicated_state.sandbox_flags, proxy.get(), opener);
+    proxy->unique_name_ = replicated_state.unique_name;
     render_view = parent->render_view();
     render_widget = parent->render_widget();
   }
@@ -220,8 +221,7 @@
   DCHECK(web_frame_);
   web_frame_->setReplicatedOrigin(state.origin);
   web_frame_->setReplicatedSandboxFlags(state.sandbox_flags);
-  web_frame_->setReplicatedName(blink::WebString::fromUTF8(state.name),
-                                blink::WebString::fromUTF8(state.unique_name));
+  web_frame_->setReplicatedName(blink::WebString::fromUTF8(state.name));
   web_frame_->setReplicatedInsecureRequestPolicy(state.insecure_request_policy);
   web_frame_->setReplicatedPotentiallyTrustworthyUniqueOrigin(
       state.has_potentially_trustworthy_unique_origin);
@@ -347,8 +347,8 @@
 
 void RenderFrameProxy::OnDidUpdateName(const std::string& name,
                                        const std::string& unique_name) {
-  web_frame_->setReplicatedName(blink::WebString::fromUTF8(name),
-                                blink::WebString::fromUTF8(unique_name));
+  web_frame_->setReplicatedName(blink::WebString::fromUTF8(name));
+  unique_name_ = unique_name;
 }
 
 void RenderFrameProxy::OnAddContentSecurityPolicy(
diff --git a/content/renderer/render_frame_proxy.h b/content/renderer/render_frame_proxy.h
index aee4d65..f58a0cbd 100644
--- a/content/renderer/render_frame_proxy.h
+++ b/content/renderer/render_frame_proxy.h
@@ -117,6 +117,7 @@
   int routing_id() { return routing_id_; }
   RenderViewImpl* render_view() { return render_view_; }
   blink::WebRemoteFrame* web_frame() { return web_frame_; }
+  const std::string& unique_name() const { return unique_name_; }
 
   void set_provisional_frame_routing_id(int routing_id) {
     provisional_frame_routing_id_ = routing_id;
@@ -189,6 +190,7 @@
 
   // Stores the WebRemoteFrame we are associated with.
   blink::WebRemoteFrame* web_frame_;
+  std::string unique_name_;
   scoped_refptr<ChildFrameCompositingHelper> compositing_helper_;
 
   RenderViewImpl* render_view_;
diff --git a/content/renderer/render_view_browsertest.cc b/content/renderer/render_view_browsertest.cc
index befcd2d..640ca7b 100644
--- a/content/renderer/render_view_browsertest.cc
+++ b/content/renderer/render_view_browsertest.cc
@@ -190,7 +190,7 @@
   // can't recover result.scope - no way to get WebTreeScopeType via public
   // blink API...
   result.name = frame->assignedName().utf8();
-  result.unique_name = frame->uniqueName().utf8();
+  result.unique_name = test_render_frame->unique_name();
   result.sandbox_flags = frame->effectiveSandboxFlags();
   // result.should_enforce_strict_mixed_content_checking is calculated in the
   // browser...
diff --git a/content/renderer/unique_name_helper.cc b/content/renderer/unique_name_helper.cc
new file mode 100644
index 0000000..2d173d5
--- /dev/null
+++ b/content/renderer/unique_name_helper.cc
@@ -0,0 +1,187 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/renderer/unique_name_helper.h"
+
+#include <vector>
+
+#include "base/containers/adapters.h"
+#include "base/logging.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_piece.h"
+#include "content/renderer/render_frame_impl.h"
+#include "content/renderer/render_frame_proxy.h"
+#include "third_party/WebKit/public/web/WebLocalFrame.h"
+
+namespace content {
+
+namespace {
+
+const std::string& UniqueNameForFrame(blink::WebFrame* frame) {
+  return frame->isWebLocalFrame()
+             ? RenderFrameImpl::FromWebFrame(frame)->unique_name()
+             : RenderFrameProxy::FromWebFrame(frame)->unique_name();
+}
+
+bool UniqueNameExists(blink::WebFrame* top, const std::string& candidate) {
+  // This method is currently O(N), where N = number of frames in the tree.
+
+  // Before recalculating or checking unique name, we set m_uniqueName
+  // to an empty string (so the soon-to-be-removed name does not count
+  // as a collision).  This means that uniqueNameExists would return
+  // false positives when called with an empty |name|.
+  DCHECK(!candidate.empty());
+
+  for (blink::WebFrame* frame = top; frame; frame = frame->traverseNext()) {
+    if (UniqueNameForFrame(frame) == candidate)
+      return true;
+  }
+
+  return false;
+}
+
+std::string GenerateCandidate(blink::WebFrame* parent, blink::WebFrame* child) {
+  constexpr char kFramePathPrefix[] = "<!--framePath ";
+  constexpr int kFramePathPrefixLength = 14;
+  constexpr int kFramePathSuffixLength = 3;
+
+  std::string new_name(kFramePathPrefix);
+
+  // Find the nearest parent that has a frame with a path in it.
+  std::vector<blink::WebFrame*> chain;
+  for (blink::WebFrame* frame = parent; frame; frame = frame->parent()) {
+    base::StringPiece name = UniqueNameForFrame(frame);
+    if (name.starts_with(kFramePathPrefix) && name.ends_with("-->") &&
+        kFramePathPrefixLength + kFramePathSuffixLength < name.size()) {
+      name.substr(kFramePathPrefixLength,
+                  name.size() - kFramePathPrefixLength - kFramePathSuffixLength)
+          .AppendToString(&new_name);
+      break;
+    }
+    chain.push_back(frame);
+  }
+
+  for (auto* frame : base::Reversed(chain)) {
+    new_name += '/';
+    new_name += UniqueNameForFrame(frame);
+  }
+
+  int child_count = 0;
+  for (blink::WebFrame* frame = parent->firstChild(); frame;
+       frame = frame->nextSibling()) {
+    ++child_count;
+  }
+
+  // When generating a candidate, a null |child| means that this is the
+  // candidate name for a child frame that's not yet attached.
+  new_name += "/<!--frame";
+  new_name += base::IntToString(child_count - (child ? 1 : 0));
+  new_name += "-->-->";
+
+  // NOTE: This name might not be unique - see http://crbug.com/588800.
+  return new_name;
+}
+
+std::string GenerateFramePosition(blink::WebFrame* parent,
+                                  blink::WebFrame* child) {
+  // This method is currently O(N), where N = number of frames in the tree.
+
+  std::string position_string("<!--framePosition");
+
+  while (parent) {
+    int position_in_parent = 0;
+    blink::WebFrame* sibling = parent->firstChild();
+    while (sibling != child) {
+      sibling = sibling->nextSibling();
+      ++position_in_parent;
+    }
+
+    position_string += '-';
+    position_string += base::IntToString(position_in_parent);
+
+    child = parent;
+    parent = parent->parent();
+  }
+
+  // NOTE: The generated string is not guaranteed to be unique, but should
+  // have a better chance of being unique than the string generated by
+  // GenerateCandidate, because we embed extra information into the string:
+  // 1) we walk the full chain of ancestors, all the way to the main frame
+  // 2) we use frame-position-within-parent (aka |position_in_parent|)
+  //    instead of sibling-count.
+  return position_string;
+}
+
+std::string AppendUniqueSuffix(blink::WebFrame* top,
+                               const std::string& prefix,
+                               const std::string& likely_unique_suffix) {
+  // This should only be called if the |prefix| isn't unique, as this is
+  // otherwise pointless work.
+  DCHECK(UniqueNameExists(top, prefix));
+
+  // We want unique name to be stable across page reloads - this is why
+  // we use a deterministic |number_of_tries| rather than a random number
+  // (a random number would be more likely to avoid a collision, but
+  // would change after every page reload).
+  int number_of_retries = 0;
+
+  // Keep trying |prefix| + |likely_unique_suffix| + |number_of_tries|
+  // concatenations until we get a truly unique name.
+  std::string candidate(prefix);
+  candidate += likely_unique_suffix;
+  candidate += '/';
+  while (true) {
+    size_t current_length = candidate.size();
+    candidate += base::IntToString(number_of_retries++);
+    candidate += "-->";
+    if (!UniqueNameExists(top, candidate))
+      break;
+    candidate.resize(current_length);
+  }
+  return candidate;
+}
+
+std::string CalculateNewName(blink::WebFrame* parent,
+                             blink::WebFrame* child,
+                             const std::string& name) {
+  blink::WebFrame* top = parent->top();
+  if (!name.empty() && !UniqueNameExists(top, name) && name != "_blank")
+    return name;
+
+  std::string candidate = GenerateCandidate(parent, child);
+  if (!UniqueNameExists(top, candidate))
+    return candidate;
+
+  std::string likely_unique_suffix = GenerateFramePosition(parent, child);
+  return AppendUniqueSuffix(top, candidate, likely_unique_suffix);
+}
+
+}  // namespace
+
+UniqueNameHelper::UniqueNameHelper(RenderFrameImpl* render_frame)
+    : render_frame_(render_frame) {}
+
+UniqueNameHelper::~UniqueNameHelper() {}
+
+std::string UniqueNameHelper::GenerateNameForNewChildFrame(
+    blink::WebFrame* parent,
+    const std::string& name) {
+  return CalculateNewName(parent, nullptr, name);
+}
+
+void UniqueNameHelper::UpdateName(const std::string& name) {
+  // The unique name of the main frame is always the empty string.
+  if (!GetWebFrame()->parent())
+    return;
+  // It's important to clear this before calculating a new name, as the
+  // calculation checks for collisions with existing unique names.
+  unique_name_.clear();
+  unique_name_ = CalculateNewName(GetWebFrame()->parent(), GetWebFrame(), name);
+}
+
+blink::WebLocalFrame* UniqueNameHelper::GetWebFrame() const {
+  return render_frame_->GetWebFrame();
+}
+
+}  // namespace content
diff --git a/content/renderer/unique_name_helper.h b/content/renderer/unique_name_helper.h
new file mode 100644
index 0000000..db8beb6
--- /dev/null
+++ b/content/renderer/unique_name_helper.h
@@ -0,0 +1,113 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_RENDERER_UNIQUE_NAME_HELPER_H_
+#define CONTENT_RENDERER_UNIQUE_NAME_HELPER_H_
+
+#include <string>
+
+#include "base/macros.h"
+
+namespace blink {
+class WebFrame;
+class WebLocalFrame;
+}  // namespace blink
+
+namespace content {
+
+class RenderFrameImpl;
+
+// Frame helper that manages the details of generating a quasi-stable unique
+// name for the frame. The name is unique within a page, and is used for:
+// - matching up session history items with recreated frames
+// - layout test results
+//
+// Description of the current unique name format
+// ---------------------------------------------
+// Changing the format of unique name has a high cost, because it breaks
+// backwards compatibility of session history (which stores unique names
+// generated in the past on user's disk). The evolution of unique name in a
+// mostly backwards-compatible way has resulted in the rather baroque format...
+//
+// uniqueName ::= <nullForMainFrame> | <assignedName> | <generatedName>
+// Note: generatedName is used if assignedName is non-unique / conflicts with
+// other frame's unique name.
+//
+// assignedName ::= value of iframe's name attribute
+//                  or value assigned to window.name
+// Note: The name must be assigned *before* the first real commit: afterwards,
+// the unique name is immutable.
+//
+// generatedName ::= oldGeneratedName newUniqueSuffix?
+// Note: newUniqueSuffix is only present if oldGeneratedName is not unique.
+//
+// oldGeneratedName ::= "<!--framePath //" ancestorChain
+//                      "/<!--frame" childCount "-->-->"
+// Note: oldGeneratedName is generated by the internal GenerateCandidate()
+// method.
+//
+// childCount ::= current number of siblings
+//
+// ancestorChain ::= concatenated unique names of ancestor chain,
+//                   terminated on the first ancestor (if any) starting with
+//                   "<!--framePath"; each ancestor's unique name is
+//                   separated by "/" character
+// Note: Two examples are provided below, with each portion of the name from a
+// distinct ancestor annotated.
+//
+// Example ancestor chain #1:
+//     "grandparent/parent"
+//     |    #1     |  #2  |
+// Example ancestor chain #2:
+//     "<!--framePath //foo/bar/<!--frame42-->-->/blah/foobar"
+//     |                    #1                   | #2 |  #3  |
+//
+// newUniqueSuffix ::= "<!--framePosition" framePosition "/" retryNumber "-->"
+//
+// framePosition ::= "-" numberOfSiblingsBeforeChild
+//                   [ framePosition-forParent? ]
+//
+// retryNumber ::= smallest non-negative integer resulting in unique name
+class UniqueNameHelper {
+ public:
+  explicit UniqueNameHelper(RenderFrameImpl* render_frame);
+  ~UniqueNameHelper();
+
+  // Returns the generated unique name.
+  const std::string& value() const { return unique_name_; }
+
+  // Used to propagate an already calculated unique name.
+  //
+  // TODO(lukasza): It would be nice to assert uniqueness of the propagated
+  // name here but:
+  // 1) uniqueness is currently violated by provisional/old frame pairs.
+  // 2) there is an unresolved race between 2 OOPIFs, that can result in a
+  //    non-unique unique name: see https://crbug.com/558680#c14.
+  void set_propagated_name(const std::string& name) { unique_name_ = name; }
+
+  // Note: when creating a new child frame, the unique name needs to be
+  // calculated before the RenderFrameImpl is created. To avoid this chicken and
+  // egg problem, this method is static, which means that |parent| needs to be
+  // passed as a parameter.
+  static std::string GenerateNameForNewChildFrame(blink::WebFrame* parent,
+                                                  const std::string& name);
+
+  // Called after a browsing context name change to generate a new name. Note
+  // that this should not be called if the frame is no longer displaying the
+  // initial empty document, as unique name changes after that point will break
+  // history navigations. See https://crbug.com/607205.
+  void UpdateName(const std::string& name);
+
+ private:
+  blink::WebLocalFrame* GetWebFrame() const;
+
+  RenderFrameImpl* const render_frame_;
+  std::string unique_name_;
+
+  DISALLOW_COPY_AND_ASSIGN(UniqueNameHelper);
+};
+
+}  // namespace content
+
+#endif  // CONTENT_RENDERER_UNIQUE_NAME_HELPER_H_
diff --git a/content/shell/test_runner/BUILD.gn b/content/shell/test_runner/BUILD.gn
index f8be5d5..9cf5bcc 100644
--- a/content/shell/test_runner/BUILD.gn
+++ b/content/shell/test_runner/BUILD.gn
@@ -114,6 +114,7 @@
     "//cc/blink",
     "//cc/paint",
     "//content/public/common",
+    "//content/test:test_runner_support",
     "//device/sensors/public/cpp:full",
     "//gin",
     "//gpu",
diff --git a/content/shell/test_runner/DEPS b/content/shell/test_runner/DEPS
index 5a153a7..f76fc0d7c 100644
--- a/content/shell/test_runner/DEPS
+++ b/content/shell/test_runner/DEPS
@@ -1,5 +1,6 @@
 include_rules = [
   "+cc",
+  "+content/public/test",
   "+device/sensors/public/cpp",
   "+gin",
   "+gpu/command_buffer/client",
diff --git a/content/shell/test_runner/layout_dump.cc b/content/shell/test_runner/layout_dump.cc
index 9c6837c..6d4f1d25 100644
--- a/content/shell/test_runner/layout_dump.cc
+++ b/content/shell/test_runner/layout_dump.cc
@@ -6,6 +6,7 @@
 
 #include "base/logging.h"
 #include "base/strings/stringprintf.h"
+#include "content/public/test/test_runner_support.h"
 #include "third_party/WebKit/public/platform/WebSize.h"
 #include "third_party/WebKit/public/platform/WebString.h"
 #include "third_party/WebKit/public/web/WebDocument.h"
@@ -23,26 +24,26 @@
 
 namespace {
 
-std::string DumpFrameHeaderIfNeeded(WebFrame* frame) {
+std::string DumpFrameHeaderIfNeeded(WebLocalFrame* frame) {
   std::string result;
 
   // Add header for all but the main frame. Skip empty frames.
   if (frame->parent() && !frame->document().documentElement().isNull()) {
     result.append("\n--------\nFrame: '");
-    result.append(frame->uniqueName().utf8());
+    result.append(content::GetUniqueNameForFrame(frame));
     result.append("'\n--------\n");
   }
 
   return result;
 }
 
-std::string DumpFrameScrollPosition(WebFrame* frame) {
+std::string DumpFrameScrollPosition(WebLocalFrame* frame) {
   std::string result;
   WebSize offset = frame->getScrollOffset();
   if (offset.width > 0 || offset.height > 0) {
     if (frame->parent()) {
       result =
-          std::string("frame '") + frame->uniqueName().utf8().data() + "' ";
+          std::string("frame '") + content::GetUniqueNameForFrame(frame) + "' ";
     }
     base::StringAppendF(&result, "scrolled to %d,%d\n", offset.width,
                         offset.height);
diff --git a/content/shell/test_runner/web_frame_test_client.cc b/content/shell/test_runner/web_frame_test_client.cc
index dc8a4ee..f8fd9e9 100644
--- a/content/shell/test_runner/web_frame_test_client.cc
+++ b/content/shell/test_runner/web_frame_test_client.cc
@@ -10,6 +10,7 @@
 #include "base/strings/string_piece.h"
 #include "base/strings/string_util.h"
 #include "base/strings/stringprintf.h"
+#include "content/public/test/test_runner_support.h"
 #include "content/shell/test_runner/accessibility_controller.h"
 #include "content/shell/test_runner/event_sender.h"
 #include "content/shell/test_runner/mock_color_chooser.h"
@@ -44,21 +45,19 @@
 
 namespace {
 
-void PrintFrameDescription(WebTestDelegate* delegate, blink::WebFrame* frame) {
-  std::string name8 = frame->uniqueName().utf8();
+void PrintFrameDescription(WebTestDelegate* delegate,
+                           blink::WebLocalFrame* frame) {
+  std::string name = content::GetUniqueNameForFrame(frame);
   if (frame == frame->view()->mainFrame()) {
-    if (!name8.length()) {
-      delegate->PrintMessage("main frame");
-      return;
-    }
-    delegate->PrintMessage(std::string("main frame \"") + name8 + "\"");
+    DCHECK(name.empty());
+    delegate->PrintMessage("main frame");
     return;
   }
-  if (!name8.length()) {
+  if (name.empty()) {
     delegate->PrintMessage("frame (anonymous)");
     return;
   }
-  delegate->PrintMessage(std::string("frame \"") + name8 + "\"");
+  delegate->PrintMessage(std::string("frame \"") + name + "\"");
 }
 
 void PrintFrameuserGestureStatus(WebTestDelegate* delegate,
diff --git a/content/test/BUILD.gn b/content/test/BUILD.gn
index 568b1c1..3088bdd 100644
--- a/content/test/BUILD.gn
+++ b/content/test/BUILD.gn
@@ -504,6 +504,26 @@
   }
 }
 
+static_library("test_runner_support") {
+  testonly = true
+
+  # See comment at the top of //content/BUILD.gn for why this is disabled in
+  # component builds.
+  if (is_component_build) {
+    check_includes = false
+  }
+
+  sources = [
+    "../public/test/test_runner_support.h",
+    "test_runner_support.cc",
+  ]
+
+  deps = [
+    "//content/renderer:for_content_tests",
+    "//third_party/WebKit/public:blink",
+  ]
+}
+
 if (is_android) {
   import("//build/config/android/rules.gni")
 
diff --git a/content/test/test_runner_support.cc b/content/test/test_runner_support.cc
new file mode 100644
index 0000000..f5ee65b
--- /dev/null
+++ b/content/test/test_runner_support.cc
@@ -0,0 +1,16 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/public/test/test_runner_support.h"
+
+#include "content/renderer/render_frame_impl.h"
+#include "third_party/WebKit/public/web/WebLocalFrame.h"
+
+namespace content {
+
+std::string GetUniqueNameForFrame(blink::WebLocalFrame* frame) {
+  return RenderFrameImpl::FromWebFrame(frame)->unique_name();
+}
+
+}  // namespace content
diff --git a/extensions/renderer/script_context_browsertest.cc b/extensions/renderer/script_context_browsertest.cc
index 4ee033f6..8fc6595 100644
--- a/extensions/renderer/script_context_browsertest.cc
+++ b/extensions/renderer/script_context_browsertest.cc
@@ -49,22 +49,22 @@
 
   WebFrame* frame1 = frame->firstChild();
   ASSERT_TRUE(frame1);
-  ASSERT_EQ("frame1", frame1->uniqueName());
+  ASSERT_EQ("frame1", frame1->assignedName());
   WebFrame* frame1_1 = frame1->firstChild();
   ASSERT_TRUE(frame1_1);
-  ASSERT_EQ("frame1_1", frame1_1->uniqueName());
+  ASSERT_EQ("frame1_1", frame1_1->assignedName());
   WebFrame* frame1_2 = frame1_1->nextSibling();
   ASSERT_TRUE(frame1_2);
-  ASSERT_EQ("frame1_2", frame1_2->uniqueName());
+  ASSERT_EQ("frame1_2", frame1_2->assignedName());
   WebFrame* frame2 = frame1->nextSibling();
   ASSERT_TRUE(frame2);
-  ASSERT_EQ("frame2", frame2->uniqueName());
+  ASSERT_EQ("frame2", frame2->assignedName());
   WebFrame* frame2_1 = frame2->firstChild();
   ASSERT_TRUE(frame2_1);
-  ASSERT_EQ("frame2_1", frame2_1->uniqueName());
+  ASSERT_EQ("frame2_1", frame2_1->assignedName());
   WebFrame* frame3 = frame2->nextSibling();
   ASSERT_TRUE(frame3);
-  ASSERT_EQ("frame3", frame3->uniqueName());
+  ASSERT_EQ("frame3", frame3->assignedName());
 
   // Load a blank document in a frame from a different origin.
   frame3->loadHTMLString(frame3_html, different_url);
@@ -72,7 +72,7 @@
 
   WebFrame* frame3_1 = frame3->firstChild();
   ASSERT_TRUE(frame3_1);
-  ASSERT_EQ("frame3_1", frame3_1->uniqueName());
+  ASSERT_EQ("frame3_1", frame3_1->assignedName());
 
   // Top-level frame
   EXPECT_EQ(GetEffectiveDocumentURL(frame), top_url);
diff --git a/gpu/ipc/client/gpu_memory_buffer_impl_ozone_native_pixmap.cc b/gpu/ipc/client/gpu_memory_buffer_impl_ozone_native_pixmap.cc
index a132323b..f3ea25a 100644
--- a/gpu/ipc/client/gpu_memory_buffer_impl_ozone_native_pixmap.cc
+++ b/gpu/ipc/client/gpu_memory_buffer_impl_ozone_native_pixmap.cc
@@ -47,7 +47,6 @@
     gfx::BufferFormat format,
     gfx::BufferUsage usage,
     const DestructionCallback& callback) {
-  DCHECK_LE(handle.native_pixmap_handle.fds.size(), 1u);
 
   // GpuMemoryBufferImpl needs the FD to implement GetHandle() but
   // ui::ClientNativePixmapFactory::ImportFromHandle is expected to take
diff --git a/mojo/public/cpp/bindings/binding.h b/mojo/public/cpp/bindings/binding.h
index ba5954c2..1da331b 100644
--- a/mojo/public/cpp/bindings/binding.h
+++ b/mojo/public/cpp/bindings/binding.h
@@ -208,10 +208,6 @@
     internal_state_.CloseWithReason(custom_reason, description);
   }
 
-  void EnableNestedDispatch(bool enabled) {
-    internal_state_.EnableNestedDispatch(enabled);
-  }
-
   // Unbinds the underlying pipe from this binding and returns it so it can be
   // used in another context, such as on another thread or with a different
   // implementation. Put this object into a state where it can be rebound to a
diff --git a/mojo/public/cpp/bindings/connector.h b/mojo/public/cpp/bindings/connector.h
index fdfeb73..cb065c17 100644
--- a/mojo/public/cpp/bindings/connector.h
+++ b/mojo/public/cpp/bindings/connector.h
@@ -155,13 +155,10 @@
   // |tag| must be a const string literal.
   void SetWatcherHeapProfilerTag(const char* tag);
 
-  // Enables support for nested message dispatch so that the Connector can
-  // continue dispatching inbound messages even if one of them spins a nested
-  // message loop. This should be enabled only when needed, as dispatch in this
-  // mode is generally less efficient.
-  void EnableNestedDispatch(bool enabled);
-
  private:
+  class ActiveDispatchTracker;
+  class MessageLoopNestingObserver;
+
   // Callback of mojo::SimpleWatcher.
   void OnWatcherHandleReady(MojoResult result);
   // Callback of SyncHandleWatcher.
@@ -202,8 +199,6 @@
 
   bool paused_ = false;
 
-  bool nested_dispatch_enabled_ = false;
-
   // If sending messages is allowed from multiple threads, |lock_| is used to
   // protect modifications to |message_pipe_| and |drop_writes_|.
   base::Optional<base::Lock> lock_;
@@ -223,6 +218,14 @@
   // notification.
   const char* heap_profiler_tag_ = nullptr;
 
+  // A cached pointer to the MessageLoopNestingObserver for the MessageLoop on
+  // which this Connector was created.
+  MessageLoopNestingObserver* const nesting_observer_;
+
+  // |true| iff the Connector is currently dispatching a message. Used to detect
+  // nested dispatch operations.
+  bool is_dispatching_ = false;
+
   // Create a single weak ptr and use it everywhere, to avoid the malloc/free
   // cost of creating a new weak ptr whenever it is needed.
   // NOTE: This weak pointer is invalidated when the message pipe is closed or
diff --git a/mojo/public/cpp/bindings/interface_ptr.h b/mojo/public/cpp/bindings/interface_ptr.h
index a56b9dd..e88be74 100644
--- a/mojo/public/cpp/bindings/interface_ptr.h
+++ b/mojo/public/cpp/bindings/interface_ptr.h
@@ -193,10 +193,6 @@
     return !(*this) && !other;
   }
 
-  void EnableNestedDispatch(bool enabled) {
-    internal_state_.EnableNestedDispatch(enabled);
-  }
-
   // DO NOT USE. Exposed only for internal use and for testing.
   internal::InterfacePtrState<Interface>* internal_state() {
     return &internal_state_;
diff --git a/mojo/public/cpp/bindings/lib/binding_state.cc b/mojo/public/cpp/bindings/lib/binding_state.cc
index 85392cd..b34cb47 100644
--- a/mojo/public/cpp/bindings/lib/binding_state.cc
+++ b/mojo/public/cpp/bindings/lib/binding_state.cc
@@ -51,10 +51,6 @@
   Close();
 }
 
-void BindingStateBase::EnableNestedDispatch(bool enabled) {
-  router_->EnableNestedDispatch(enabled);
-}
-
 void BindingStateBase::FlushForTesting() {
   endpoint_client_->FlushForTesting();
 }
diff --git a/mojo/public/cpp/bindings/lib/binding_state.h b/mojo/public/cpp/bindings/lib/binding_state.h
index ca7d5a11..0b0dbee0 100644
--- a/mojo/public/cpp/bindings/lib/binding_state.h
+++ b/mojo/public/cpp/bindings/lib/binding_state.h
@@ -68,8 +68,6 @@
     return router_->handle();
   }
 
-  void EnableNestedDispatch(bool enabled);
-
   void FlushForTesting();
 
   void EnableTestingMode();
diff --git a/mojo/public/cpp/bindings/lib/connector.cc b/mojo/public/cpp/bindings/lib/connector.cc
index 5074810..d93e45ed 100644
--- a/mojo/public/cpp/bindings/lib/connector.cc
+++ b/mojo/public/cpp/bindings/lib/connector.cc
@@ -8,21 +8,131 @@
 #include <utility>
 
 #include "base/bind.h"
+#include "base/lazy_instance.h"
 #include "base/location.h"
 #include "base/logging.h"
 #include "base/macros.h"
+#include "base/memory/ptr_util.h"
+#include "base/message_loop/message_loop.h"
 #include "base/synchronization/lock.h"
+#include "base/threading/thread_local.h"
 #include "mojo/public/cpp/bindings/lib/may_auto_lock.h"
 #include "mojo/public/cpp/bindings/sync_handle_watcher.h"
 #include "mojo/public/cpp/system/wait.h"
 
 namespace mojo {
 
+namespace {
+
+// The NestingObserver for each thread. Note that this is always a
+// Connector::MessageLoopNestingObserver; we use the base type here because that
+// subclass is private to Connector.
+base::LazyInstance<
+    base::ThreadLocalPointer<base::MessageLoop::NestingObserver>>::Leaky
+    g_tls_nesting_observer = LAZY_INSTANCE_INITIALIZER;
+
+}  // namespace
+
+// Used to efficiently maintain a doubly-linked list of all Connectors
+// currently dispatching on any given thread.
+class Connector::ActiveDispatchTracker {
+ public:
+  explicit ActiveDispatchTracker(const base::WeakPtr<Connector>& connector);
+  ~ActiveDispatchTracker();
+
+  void NotifyBeginNesting();
+
+ private:
+  const base::WeakPtr<Connector> connector_;
+  MessageLoopNestingObserver* const nesting_observer_;
+  ActiveDispatchTracker* outer_tracker_ = nullptr;
+  ActiveDispatchTracker* inner_tracker_ = nullptr;
+
+  DISALLOW_COPY_AND_ASSIGN(ActiveDispatchTracker);
+};
+
+// Watches the MessageLoop on the current thread. Notifies the current chain of
+// ActiveDispatchTrackers when a nested message loop is started.
+class Connector::MessageLoopNestingObserver
+    : public base::MessageLoop::NestingObserver,
+      public base::MessageLoop::DestructionObserver {
+ public:
+  MessageLoopNestingObserver() {
+    base::MessageLoop::current()->AddNestingObserver(this);
+    base::MessageLoop::current()->AddDestructionObserver(this);
+  }
+
+  ~MessageLoopNestingObserver() override {}
+
+  // base::MessageLoop::NestingObserver:
+  void OnBeginNestedMessageLoop() override {
+    if (top_tracker_)
+      top_tracker_->NotifyBeginNesting();
+  }
+
+  // base::MessageLoop::DestructionObserver:
+  void WillDestroyCurrentMessageLoop() override {
+    base::MessageLoop::current()->RemoveNestingObserver(this);
+    base::MessageLoop::current()->RemoveDestructionObserver(this);
+    DCHECK_EQ(this, g_tls_nesting_observer.Get().Get());
+    g_tls_nesting_observer.Get().Set(nullptr);
+    delete this;
+  }
+
+  static MessageLoopNestingObserver* GetForThread() {
+    if (!base::MessageLoop::current() ||
+        !base::MessageLoop::current()->nesting_allowed())
+      return nullptr;
+    auto* observer = static_cast<MessageLoopNestingObserver*>(
+        g_tls_nesting_observer.Get().Get());
+    if (!observer) {
+      observer = new MessageLoopNestingObserver;
+      g_tls_nesting_observer.Get().Set(observer);
+    }
+    return observer;
+  }
+
+ private:
+  friend class ActiveDispatchTracker;
+
+  ActiveDispatchTracker* top_tracker_ = nullptr;
+
+  DISALLOW_COPY_AND_ASSIGN(MessageLoopNestingObserver);
+};
+
+Connector::ActiveDispatchTracker::ActiveDispatchTracker(
+    const base::WeakPtr<Connector>& connector)
+    : connector_(connector), nesting_observer_(connector_->nesting_observer_) {
+  DCHECK(nesting_observer_);
+  if (nesting_observer_->top_tracker_) {
+    outer_tracker_ = nesting_observer_->top_tracker_;
+    outer_tracker_->inner_tracker_ = this;
+  }
+  nesting_observer_->top_tracker_ = this;
+}
+
+Connector::ActiveDispatchTracker::~ActiveDispatchTracker() {
+  if (nesting_observer_->top_tracker_ == this)
+    nesting_observer_->top_tracker_ = outer_tracker_;
+  else if (inner_tracker_)
+    inner_tracker_->outer_tracker_ = outer_tracker_;
+  if (outer_tracker_)
+    outer_tracker_->inner_tracker_ = inner_tracker_;
+}
+
+void Connector::ActiveDispatchTracker::NotifyBeginNesting() {
+  if (connector_ && connector_->handle_watcher_)
+    connector_->handle_watcher_->ArmOrNotify();
+  if (outer_tracker_)
+    outer_tracker_->NotifyBeginNesting();
+}
+
 Connector::Connector(ScopedMessagePipeHandle message_pipe,
                      ConnectorConfig config,
                      scoped_refptr<base::SingleThreadTaskRunner> runner)
     : message_pipe_(std::move(message_pipe)),
       task_runner_(std::move(runner)),
+      nesting_observer_(MessageLoopNestingObserver::GetForThread()),
       weak_factory_(this) {
   if (config == MULTI_THREADED_SEND)
     lock_.emplace();
@@ -197,12 +307,6 @@
   }
 }
 
-void Connector::EnableNestedDispatch(bool enabled) {
-  nested_dispatch_enabled_ = enabled;
-  handle_watcher_.reset();
-  WaitToReadMore();
-}
-
 void Connector::OnWatcherHandleReady(MojoResult result) {
   OnHandleReadyInternal(result);
 }
@@ -272,25 +376,26 @@
   const MojoResult rv = ReadMessage(message_pipe_.get(), &message);
   *read_result = rv;
 
-  if (nested_dispatch_enabled_) {
-    // When supporting nested dispatch, we have to rearm the Watcher immediately
-    // after reading each message (i.e. before dispatch) to ensure that the next
-    // inbound message can trigger OnHandleReady on the nested loop.
-    handle_watcher_->ArmOrNotify();
-  }
-
   if (rv == MOJO_RESULT_OK) {
+    base::Optional<ActiveDispatchTracker> dispatch_tracker;
+    if (!is_dispatching_ && nesting_observer_) {
+      is_dispatching_ = true;
+      dispatch_tracker.emplace(weak_self);
+    }
+
     receiver_result =
         incoming_receiver_ && incoming_receiver_->Accept(&message);
-  }
 
-  if (!weak_self)
-    return false;
+    if (!weak_self)
+      return false;
 
-  if (rv == MOJO_RESULT_SHOULD_WAIT)
+    if (dispatch_tracker) {
+      is_dispatching_ = false;
+      dispatch_tracker.reset();
+    }
+  } else if (rv == MOJO_RESULT_SHOULD_WAIT) {
     return true;
-
-  if (rv != MOJO_RESULT_OK) {
+  } else {
     HandleError(rv != MOJO_RESULT_FAILED_PRECONDITION, false);
     return false;
   }
diff --git a/mojo/public/cpp/bindings/lib/interface_ptr_state.h b/mojo/public/cpp/bindings/lib/interface_ptr_state.h
index d1cd3e6..8f5b4ff 100644
--- a/mojo/public/cpp/bindings/lib/interface_ptr_state.h
+++ b/mojo/public/cpp/bindings/lib/interface_ptr_state.h
@@ -150,11 +150,6 @@
     return endpoint_client_->associated_group();
   }
 
-  void EnableNestedDispatch(bool enabled) {
-    ConfigureProxyIfNecessary();
-    router_->EnableNestedDispatch(enabled);
-  }
-
   void EnableTestingMode() {
     ConfigureProxyIfNecessary();
     router_->EnableTestingMode();
diff --git a/mojo/public/cpp/bindings/lib/multiplex_router.cc b/mojo/public/cpp/bindings/lib/multiplex_router.cc
index 40d07887..2da459a 100644
--- a/mojo/public/cpp/bindings/lib/multiplex_router.cc
+++ b/mojo/public/cpp/bindings/lib/multiplex_router.cc
@@ -605,11 +605,6 @@
   return !base::ContainsKey(endpoints_, kMasterInterfaceId);
 }
 
-void MultiplexRouter::EnableNestedDispatch(bool enabled) {
-  DCHECK(thread_checker_.CalledOnValidThread());
-  connector_.EnableNestedDispatch(enabled);
-}
-
 void MultiplexRouter::EnableTestingMode() {
   DCHECK(thread_checker_.CalledOnValidThread());
   MayAutoLock locker(&lock_);
diff --git a/mojo/public/cpp/bindings/lib/multiplex_router.h b/mojo/public/cpp/bindings/lib/multiplex_router.h
index 8f1f793..cac138bcb 100644
--- a/mojo/public/cpp/bindings/lib/multiplex_router.h
+++ b/mojo/public/cpp/bindings/lib/multiplex_router.h
@@ -130,8 +130,6 @@
   // Whether there are any associated interfaces running currently.
   bool HasAssociatedEndpoints() const;
 
-  void EnableNestedDispatch(bool enabled);
-
   // Sets this object to testing mode.
   // In testing mode, the object doesn't disconnect the underlying message pipe
   // when it receives unexpected or invalid messages.
diff --git a/mojo/public/cpp/bindings/tests/connector_unittest.cc b/mojo/public/cpp/bindings/tests/connector_unittest.cc
index a081d1d095..74ecb7a 100644
--- a/mojo/public/cpp/bindings/tests/connector_unittest.cc
+++ b/mojo/public/cpp/bindings/tests/connector_unittest.cc
@@ -568,7 +568,6 @@
                                      run_loop.QuitClosure()));
   connector1.set_incoming_receiver(&accumulator);
 
-  connector1.EnableNestedDispatch(true);
   run_loop.Run();
 
   ASSERT_EQ(2u, accumulator.size());
diff --git a/net/cert/x509_certificate.h b/net/cert/x509_certificate.h
index cc31996..59f1b77 100644
--- a/net/cert/x509_certificate.h
+++ b/net/cert/x509_certificate.h
@@ -197,8 +197,10 @@
   // Gets the subjectAltName extension field from the certificate, if any.
   // For future extension; currently this only returns those name types that
   // are required for HTTP certificate name verification - see VerifyHostname.
-  // Unrequired parameters may be passed as NULL.
-  void GetSubjectAltName(std::vector<std::string>* dns_names,
+  // Returns true if any dNSName or iPAddress SAN was present. If |dns_names|
+  // is non-null, it will be set to all dNSNames present. If |ip_addrs| is
+  // non-null, it will be set to all iPAddresses present.
+  bool GetSubjectAltName(std::vector<std::string>* dns_names,
                          std::vector<std::string>* ip_addrs) const;
 
   // Convenience method that returns whether this certificate has expired as of
diff --git a/net/cert/x509_certificate_ios.cc b/net/cert/x509_certificate_ios.cc
index ce93f90..fd1b351 100644
--- a/net/cert/x509_certificate_ios.cc
+++ b/net/cert/x509_certificate_ios.cc
@@ -100,49 +100,53 @@
                                       &principal->country_name);
 }
 
-void ParseSubjectAltName(X509Certificate::OSCertHandle os_cert,
+bool ParseSubjectAltName(X509Certificate::OSCertHandle os_cert,
                          std::vector<std::string>* dns_names,
                          std::vector<std::string>* ip_addresses) {
-  DCHECK(dns_names || ip_addresses);
   bssl::UniquePtr<X509> cert = OSCertHandleToOpenSSL(os_cert);
   if (!cert.get())
-    return;
+    return false;
   int index = X509_get_ext_by_NID(cert.get(), NID_subject_alt_name, -1);
   X509_EXTENSION* alt_name_ext = X509_get_ext(cert.get(), index);
   if (!alt_name_ext)
-    return;
+    return false;
 
   bssl::UniquePtr<GENERAL_NAMES> alt_names(
       reinterpret_cast<GENERAL_NAMES*>(X509V3_EXT_d2i(alt_name_ext)));
   if (!alt_names.get())
-    return;
+    return false;
 
+  bool has_san = false;
   for (size_t i = 0; i < sk_GENERAL_NAME_num(alt_names.get()); ++i) {
     const GENERAL_NAME* name = sk_GENERAL_NAME_value(alt_names.get(), i);
-    if (name->type == GEN_DNS && dns_names) {
-      const unsigned char* dns_name = ASN1_STRING_data(name->d.dNSName);
-      if (!dns_name)
-        continue;
-      int dns_name_len = ASN1_STRING_length(name->d.dNSName);
-      dns_names->push_back(
-          std::string(reinterpret_cast<const char*>(dns_name), dns_name_len));
-    } else if (name->type == GEN_IPADD && ip_addresses) {
-      const unsigned char* ip_addr = name->d.iPAddress->data;
-      if (!ip_addr)
-        continue;
-      int ip_addr_len = name->d.iPAddress->length;
-      if (ip_addr_len != static_cast<int>(IPAddress::kIPv4AddressSize) &&
-          ip_addr_len != static_cast<int>(IPAddress::kIPv6AddressSize)) {
-        // http://www.ietf.org/rfc/rfc3280.txt requires subjectAltName iPAddress
-        // to have 4 or 16 bytes, whereas in a name constraint it includes a
-        // net mask hence 8 or 32 bytes. Logging to help diagnose any mixup.
-        LOG(WARNING) << "Bad sized IP Address in cert: " << ip_addr_len;
-        continue;
+    if (name->type == GEN_DNS) {
+      has_san = true;
+      if (dns_names) {
+        const unsigned char* dns_name = ASN1_STRING_data(name->d.dNSName);
+        int dns_name_len = ASN1_STRING_length(name->d.dNSName);
+        dns_names->push_back(
+            base::StringPiece(reinterpret_cast<const char*>(dns_name),
+                              dns_name_len)
+                .as_string());
       }
-      ip_addresses->push_back(
-          std::string(reinterpret_cast<const char*>(ip_addr), ip_addr_len));
+    } else if (name->type == GEN_IPADD) {
+      has_san = true;
+      if (ip_addresses) {
+        const unsigned char* ip_addr = name->d.iPAddress->data;
+        int ip_addr_len = name->d.iPAddress->length;
+        ip_addresses->push_back(
+            base::StringPiece(reinterpret_cast<const char*>(ip_addr),
+                              ip_addr_len)
+                .as_string());
+      }
     }
+    // Fast path: Found at least one subjectAltName and the caller doesn't
+    // need the actual values.
+    if (has_san && !ip_addresses && !dns_names)
+      return true;
   }
+
+  return has_san;
 }
 
 }  // namespace
@@ -271,7 +275,7 @@
   return results;
 }
 
-void X509Certificate::GetSubjectAltName(
+bool X509Certificate::GetSubjectAltName(
     std::vector<std::string>* dns_names,
     std::vector<std::string>* ip_addrs) const {
   if (dns_names)
@@ -279,7 +283,7 @@
   if (ip_addrs)
     ip_addrs->clear();
 
-  ParseSubjectAltName(cert_handle_, dns_names, ip_addrs);
+  return ParseSubjectAltName(cert_handle_, dns_names, ip_addrs);
 }
 
 // static
diff --git a/net/cert/x509_certificate_mac.cc b/net/cert/x509_certificate_mac.cc
index 60f33a9..85aacac5 100644
--- a/net/cert/x509_certificate_mac.cc
+++ b/net/cert/x509_certificate_mac.cc
@@ -216,7 +216,7 @@
   return false;
 }
 
-void X509Certificate::GetSubjectAltName(
+bool X509Certificate::GetSubjectAltName(
     std::vector<std::string>* dns_names,
     std::vector<std::string>* ip_addrs) const {
   if (dns_names)
@@ -227,34 +227,47 @@
   x509_util::CSSMCachedCertificate cached_cert;
   OSStatus status = cached_cert.Init(cert_handle_);
   if (status)
-    return;
+    return false;
+
   x509_util::CSSMFieldValue subject_alt_name;
   status = cached_cert.GetField(&CSSMOID_SubjectAltName, &subject_alt_name);
   if (status || !subject_alt_name.field())
-    return;
+    return false;
+
   const CSSM_X509_EXTENSION* cssm_ext =
       subject_alt_name.GetAs<CSSM_X509_EXTENSION>();
   if (!cssm_ext || !cssm_ext->value.parsedValue)
-    return;
+    return false;
   const CE_GeneralNames* alt_name =
       reinterpret_cast<const CE_GeneralNames*>(cssm_ext->value.parsedValue);
 
+  bool has_san = false;
   for (size_t name = 0; name < alt_name->numNames; ++name) {
     const CE_GeneralName& name_struct = alt_name->generalName[name];
     const CSSM_DATA& name_data = name_struct.name;
     // DNSName and IPAddress are encoded as IA5String and OCTET STRINGs
     // respectively, both of which can be byte copied from
     // CSSM_DATA::data into the appropriate output vector.
-    if (dns_names && name_struct.nameType == GNT_DNSName) {
-      dns_names->push_back(std::string(
-          reinterpret_cast<const char*>(name_data.Data),
-          name_data.Length));
-    } else if (ip_addrs && name_struct.nameType == GNT_IPAddress) {
-      ip_addrs->push_back(std::string(
-          reinterpret_cast<const char*>(name_data.Data),
-          name_data.Length));
+    if (name_struct.nameType == GNT_DNSName) {
+      has_san = true;
+      if (dns_names) {
+        dns_names->push_back(std::string(
+            reinterpret_cast<const char*>(name_data.Data), name_data.Length));
+      }
+    } else if (name_struct.nameType == GNT_IPAddress) {
+      has_san = true;
+      if (ip_addrs) {
+        ip_addrs->push_back(std::string(
+            reinterpret_cast<const char*>(name_data.Data), name_data.Length));
+      }
     }
+    // Fast path: Found at least one subjectAltName and the caller doesn't
+    // need the actual values.
+    if (has_san && !ip_addrs && !dns_names)
+      return true;
   }
+
+  return has_san;
 }
 
 // static
diff --git a/net/cert/x509_certificate_nss.cc b/net/cert/x509_certificate_nss.cc
index 822102fe..baa0a595 100644
--- a/net/cert/x509_certificate_nss.cc
+++ b/net/cert/x509_certificate_nss.cc
@@ -88,10 +88,10 @@
   return result;
 }
 
-void X509Certificate::GetSubjectAltName(
+bool X509Certificate::GetSubjectAltName(
     std::vector<std::string>* dns_names,
     std::vector<std::string>* ip_addrs) const {
-  x509_util::GetSubjectAltName(cert_handle_, dns_names, ip_addrs);
+  return x509_util::GetSubjectAltName(cert_handle_, dns_names, ip_addrs);
 }
 
 bool X509Certificate::IsIssuedByEncoded(
diff --git a/net/cert/x509_certificate_openssl.cc b/net/cert/x509_certificate_openssl.cc
index 3644984..dfbdfa1 100644
--- a/net/cert/x509_certificate_openssl.cc
+++ b/net/cert/x509_certificate_openssl.cc
@@ -93,46 +93,49 @@
                                       &principal->country_name);
 }
 
-void ParseSubjectAltName(X509Certificate::OSCertHandle cert,
+bool ParseSubjectAltName(X509Certificate::OSCertHandle cert,
                          std::vector<std::string>* dns_names,
                          std::vector<std::string>* ip_addresses) {
-  DCHECK(dns_names || ip_addresses);
   int index = X509_get_ext_by_NID(cert, NID_subject_alt_name, -1);
   X509_EXTENSION* alt_name_ext = X509_get_ext(cert, index);
   if (!alt_name_ext)
-    return;
+    return false;
 
   bssl::UniquePtr<GENERAL_NAMES> alt_names(
       reinterpret_cast<GENERAL_NAMES*>(X509V3_EXT_d2i(alt_name_ext)));
   if (!alt_names.get())
-    return;
+    return false;
 
+  bool has_san = false;
   for (size_t i = 0; i < sk_GENERAL_NAME_num(alt_names.get()); ++i) {
     const GENERAL_NAME* name = sk_GENERAL_NAME_value(alt_names.get(), i);
-    if (name->type == GEN_DNS && dns_names) {
-      const unsigned char* dns_name = ASN1_STRING_data(name->d.dNSName);
-      if (!dns_name)
-        continue;
-      int dns_name_len = ASN1_STRING_length(name->d.dNSName);
-      dns_names->push_back(
-          std::string(reinterpret_cast<const char*>(dns_name), dns_name_len));
-    } else if (name->type == GEN_IPADD && ip_addresses) {
-      const unsigned char* ip_addr = name->d.iPAddress->data;
-      if (!ip_addr)
-        continue;
-      int ip_addr_len = name->d.iPAddress->length;
-      if (ip_addr_len != static_cast<int>(IPAddress::kIPv4AddressSize) &&
-          ip_addr_len != static_cast<int>(IPAddress::kIPv6AddressSize)) {
-        // http://www.ietf.org/rfc/rfc3280.txt requires subjectAltName iPAddress
-        // to have 4 or 16 bytes, whereas in a name constraint it includes a
-        // net mask hence 8 or 32 bytes. Logging to help diagnose any mixup.
-        LOG(WARNING) << "Bad sized IP Address in cert: " << ip_addr_len;
-        continue;
+    if (name->type == GEN_DNS) {
+      has_san = true;
+      if (dns_names) {
+        const unsigned char* dns_name = ASN1_STRING_data(name->d.dNSName);
+        int dns_name_len = ASN1_STRING_length(name->d.dNSName);
+        dns_names->push_back(
+            base::StringPiece(reinterpret_cast<const char*>(dns_name),
+                              dns_name_len)
+                .as_string());
       }
-      ip_addresses->push_back(
-          std::string(reinterpret_cast<const char*>(ip_addr), ip_addr_len));
+    } else if (name->type == GEN_IPADD) {
+      has_san = true;
+      if (ip_addresses) {
+        const unsigned char* ip_addr = name->d.iPAddress->data;
+        int ip_addr_len = name->d.iPAddress->length;
+        ip_addresses->push_back(
+            base::StringPiece(reinterpret_cast<const char*>(ip_addr),
+                              ip_addr_len)
+                .as_string());
+      }
     }
+    // Fast path: Found at least one subjectAltName and the caller doesn't
+    // need the actual values.
+    if (has_san && !ip_addresses && !dns_names)
+      return true;
   }
+  return has_san;
 }
 
 class X509InitSingleton {
@@ -278,7 +281,7 @@
   return results;
 }
 
-void X509Certificate::GetSubjectAltName(
+bool X509Certificate::GetSubjectAltName(
     std::vector<std::string>* dns_names,
     std::vector<std::string>* ip_addrs) const {
   if (dns_names)
@@ -286,7 +289,7 @@
   if (ip_addrs)
     ip_addrs->clear();
 
-  ParseSubjectAltName(cert_handle_, dns_names, ip_addrs);
+  return ParseSubjectAltName(cert_handle_, dns_names, ip_addrs);
 }
 
 // static
diff --git a/net/cert/x509_certificate_unittest.cc b/net/cert/x509_certificate_unittest.cc
index 6b2e04d..f6c5eba 100644
--- a/net/cert/x509_certificate_unittest.cc
+++ b/net/cert/x509_certificate_unittest.cc
@@ -403,9 +403,16 @@
       ImportCertFromFile(certs_dir, "subjectAltName_sanity_check.pem");
   ASSERT_NE(static_cast<X509Certificate*>(NULL), san_cert.get());
 
+  // Ensure that testing for SAN without using it is accepted.
+  EXPECT_TRUE(san_cert->GetSubjectAltName(nullptr, nullptr));
+
+  // Ensure that it's possible to get just dNSNames.
   std::vector<std::string> dns_names;
+  EXPECT_TRUE(san_cert->GetSubjectAltName(&dns_names, nullptr));
+
+  // Ensure that it's possible to get just iPAddresses.
   std::vector<std::string> ip_addresses;
-  san_cert->GetSubjectAltName(&dns_names, &ip_addresses);
+  EXPECT_TRUE(san_cert->GetSubjectAltName(nullptr, &ip_addresses));
 
   // Ensure that DNS names are correctly parsed.
   ASSERT_EQ(1U, dns_names.size());
@@ -432,6 +439,16 @@
   // Ensure the subjectAltName dirName has not influenced the handling of
   // the subject commonName.
   EXPECT_EQ("127.0.0.1", san_cert->subject().common_name);
+
+  scoped_refptr<X509Certificate> no_san_cert =
+      ImportCertFromFile(certs_dir, "salesforce_com_test.pem");
+  ASSERT_NE(static_cast<X509Certificate*>(NULL), no_san_cert.get());
+
+  EXPECT_NE(0u, dns_names.size());
+  EXPECT_NE(0u, ip_addresses.size());
+  EXPECT_FALSE(no_san_cert->GetSubjectAltName(&dns_names, &ip_addresses));
+  EXPECT_EQ(0u, dns_names.size());
+  EXPECT_EQ(0u, ip_addresses.size());
 }
 
 #if defined(USE_NSS_CERTS)
diff --git a/net/cert/x509_certificate_win.cc b/net/cert/x509_certificate_win.cc
index c67011e..3414734 100644
--- a/net/cert/x509_certificate_win.cc
+++ b/net/cert/x509_certificate_win.cc
@@ -151,7 +151,7 @@
       reinterpret_cast<char*>(serial_bytes.get()), serial->cbData);
 }
 
-void X509Certificate::GetSubjectAltName(
+bool X509Certificate::GetSubjectAltName(
     std::vector<std::string>* dns_names,
     std::vector<std::string>* ip_addrs) const {
   if (dns_names)
@@ -160,28 +160,39 @@
     ip_addrs->clear();
 
   if (!cert_handle_)
-    return;
+    return false;
 
   std::unique_ptr<CERT_ALT_NAME_INFO, base::FreeDeleter> alt_name_info;
   GetCertSubjectAltName(cert_handle_, &alt_name_info);
   CERT_ALT_NAME_INFO* alt_name = alt_name_info.get();
-  if (alt_name) {
-    int num_entries = alt_name->cAltEntry;
-    for (int i = 0; i < num_entries; i++) {
-      // dNSName is an ASN.1 IA5String representing a string of ASCII
-      // characters, so we can use UTF16ToASCII here.
-      const CERT_ALT_NAME_ENTRY& entry = alt_name->rgAltEntry[i];
+  if (!alt_name)
+    return false;
 
-      if (dns_names && entry.dwAltNameChoice == CERT_ALT_NAME_DNS_NAME) {
+  bool has_san = false;
+  for (DWORD i = 0, num_entries = alt_name->cAltEntry; i < num_entries; i++) {
+    // dNSName is an ASN.1 IA5String representing a string of ASCII
+    // characters, so we can use UTF16ToASCII here.
+    const CERT_ALT_NAME_ENTRY& entry = alt_name->rgAltEntry[i];
+
+    if (entry.dwAltNameChoice == CERT_ALT_NAME_DNS_NAME) {
+      has_san = true;
+      if (dns_names)
         dns_names->push_back(base::UTF16ToASCII(entry.pwszDNSName));
-      } else if (ip_addrs &&
-                 entry.dwAltNameChoice == CERT_ALT_NAME_IP_ADDRESS) {
+    } else if (entry.dwAltNameChoice == CERT_ALT_NAME_IP_ADDRESS) {
+      has_san = true;
+      if (ip_addrs) {
         ip_addrs->push_back(std::string(
             reinterpret_cast<const char*>(entry.IPAddress.pbData),
             entry.IPAddress.cbData));
       }
     }
+    // Fast path: Found at least one subjectAltName and the caller doesn't
+    // need the actual values.
+    if (has_san && !ip_addrs && !dns_names)
+      return true;
   }
+
+  return has_san;
 }
 
 PCCERT_CONTEXT X509Certificate::CreateOSCertChainForCert() const {
diff --git a/net/cert/x509_util_nss.cc b/net/cert/x509_util_nss.cc
index 04c34c5..29884176 100644
--- a/net/cert/x509_util_nss.cc
+++ b/net/cert/x509_util_nss.cc
@@ -159,7 +159,7 @@
                      certificate->serialNumber.len);
 }
 
-void GetSubjectAltName(CERTCertificate* cert_handle,
+bool GetSubjectAltName(CERTCertificate* cert_handle,
                        std::vector<std::string>* dns_names,
                        std::vector<std::string>* ip_addrs) {
   if (dns_names)
@@ -171,34 +171,45 @@
   SECStatus rv = CERT_FindCertExtension(
       cert_handle, SEC_OID_X509_SUBJECT_ALT_NAME, &alt_name);
   if (rv != SECSuccess)
-    return;
+    return false;
 
-  PLArenaPool* arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
-  DCHECK(arena != NULL);
+  crypto::ScopedPLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE));
 
   CERTGeneralName* alt_name_list;
-  alt_name_list = CERT_DecodeAltNameExtension(arena, &alt_name);
+  alt_name_list = CERT_DecodeAltNameExtension(arena.get(), &alt_name);
   SECITEM_FreeItem(&alt_name, PR_FALSE);
 
+  bool has_san = false;
   CERTGeneralName* name = alt_name_list;
   while (name) {
     // DNSName and IPAddress are encoded as IA5String and OCTET STRINGs
     // respectively, both of which can be byte copied from
     // SECItemType::data into the appropriate output vector.
-    if (dns_names && name->type == certDNSName) {
-      dns_names->push_back(
-          std::string(reinterpret_cast<char*>(name->name.other.data),
-                      name->name.other.len));
-    } else if (ip_addrs && name->type == certIPAddress) {
-      ip_addrs->push_back(
-          std::string(reinterpret_cast<char*>(name->name.other.data),
-                      name->name.other.len));
+    if (name->type == certDNSName) {
+      has_san = true;
+      if (dns_names) {
+        dns_names->push_back(
+            std::string(reinterpret_cast<char*>(name->name.other.data),
+                        name->name.other.len));
+      }
+    } else if (name->type == certIPAddress) {
+      has_san = true;
+      if (ip_addrs) {
+        ip_addrs->push_back(
+            std::string(reinterpret_cast<char*>(name->name.other.data),
+                        name->name.other.len));
+      }
     }
+    // Fast path: Found at least one subjectAltName and the caller doesn't
+    // need the actual values.
+    if (has_san && !ip_addrs && !dns_names)
+      return true;
+
     name = CERT_GetNextGeneralName(name);
     if (name == alt_name_list)
       break;
   }
-  PORT_FreeArena(arena, PR_FALSE);
+  return has_san;
 }
 
 void GetRFC822SubjectAltNames(CERTCertificate* cert_handle,
diff --git a/net/cert/x509_util_nss.h b/net/cert/x509_util_nss.h
index d0839c9..b11a91f 100644
--- a/net/cert/x509_util_nss.h
+++ b/net/cert/x509_util_nss.h
@@ -39,12 +39,12 @@
 // Parses the serial number from |certificate|.
 std::string ParseSerialNumber(const CERTCertificate* certificate);
 
-// Gets the dNSName and iPAddress name types from the subjectAltName
-// extension of |cert_handle|, storing them in |dns_names| and
-// |ip_addrs|, respectively.
-// If no subjectAltName is present, or no names of that type are
-// present, the relevant vectors are cleared.
-void GetSubjectAltName(CERTCertificate* cert_handle,
+// Gets the dNSName and iPAddress name fields from the subjectAltName
+// extension of |cert_handle|.
+// If |dns_names| is non-null, each dNSName will be stored in |*dns_names|.
+// If |ip_addrs| is non-null, each iPAddress will be stored in |*ip_addrs|.
+// Returns true if any dNSName or iPAddress was present.
+bool GetSubjectAltName(CERTCertificate* cert_handle,
                        std::vector<std::string>* dns_names,
                        std::vector<std::string>* ip_addrs);
 
diff --git a/styleguide/web/OWNERS b/styleguide/web/OWNERS
new file mode 100644
index 0000000..6aad1e8
--- /dev/null
+++ b/styleguide/web/OWNERS
@@ -0,0 +1,4 @@
+# Interested in helping maintain this? Let us know!
+dbeam@chromium.org
+dpapad@chromium.org
+michaelpg@chromium.org
diff --git a/styleguide/web/web.md b/styleguide/web/web.md
new file mode 100644
index 0000000..60150ef
--- /dev/null
+++ b/styleguide/web/web.md
@@ -0,0 +1,470 @@
+<style>
+
+.note::before {
+  content: 'Note: ';
+  font-variant: small-caps;
+  font-style: italic;
+}
+
+</style>
+
+# Chromium Web Development Style Guide
+
+[TOC]
+
+## Where does this style guide apply?
+
+This style guide targets Chromium frontend features implemented with JavaScript,
+CSS, and HTML.  Developers of these features should adhere to the following
+rules where possible, just like those using C++ conform to the [Chromium C++
+styleguide](../c++/c++.md).
+
+This guide follows and builds on:
+
+* [Google HTML/CSS Style Guide](https://google.github.io/styleguide/htmlcssguide.xml)
+* [Google JavaScript Style Guide](https://google.github.io/styleguide/javascriptguide.xml)
+* [Google Polymer Style Guide](http://go/polymer-style)
+
+<div class="note">
+Concerns for browser compatibility are usually not relevant for Chromium-only
+code.
+</div>
+
+## Separation of presentation and content
+
+When designing a feature with web technologies, separate the:
+
+* **content** you are presenting to the user (**HTML**)
+* **styling** of the data (**CSS**)
+* **logic** that controls the dynamic behavior of the content and presentation
+  (**JS**)
+
+This highlights the concern of each part of the code and promotes looser
+coupling (which makes refactor easier down the road).
+
+Another way to envision this principle is using the MVC pattern:
+
+| MVC Component | Web Component |
+|:-------------:|:-------------:|
+| Model         | HTML          |
+| View          | CSS           |
+| Controller    | JS            |
+
+It's also often appropriate to separate each implementation into separate files.
+
+DO:
+```html
+<!-- missile-button.html -->
+<link rel="stylesheet" href="warnings.css">
+<b class="warning">LAUNCH BUTTON WARNING</b>
+<script src="missile-button.js">
+```
+```css
+/* warnings.css */
+.warning {
+  color: red;
+}
+```
+```js
+// missile-button.js
+document.querySelector('b').onclick = fireZeeMissiles;
+```
+
+DON'T:
+```html
+<!-- missile-button.html -->
+<b style="color: red;" onclick="fireZeeMissiles()">LAUNCH BUTTON WARNING</span>
+```
+
+<div class="note">
+For various technical and historical reasons, code using the Polymer library may
+use <code>on-event</code>-style event listener wiring and
+<code>&lt;style&gt;</code> tags that live inside of .html files.
+</div>
+
+## HTML
+
+See the [Google HTML/CSS Style
+guide](https://google.github.io/styleguide/htmlcssguide.xml).
+
+### Head
+
+```html
+<!doctype html>
+<html dir="$i18n{direction}">
+<head>
+  <meta charset="utf-8">
+  <title>$i18n{myFeatureTitle}</title>
+  <link rel="icon" href="feature.png">
+  <link rel="stylesheet" href="feature.css">
+  <script src="feature.js"></script>
+</head>
+…
+</html>
+```
+
+* Specify `<!doctype html>`.
+
+* Set the `dir` attribute of the html element to the localized ‘textdirection’
+  value.  This flips the page visually for RTL languages and allows
+  `html[dir=rtl]` selectors to work.
+
+* Specify the charset, UTF-8.
+
+* Link in image, icon and stylesheet resources.
+    * Do not style elements with `style="..."` attributes.
+
+* Include the appropriate JS scripts.
+    * Do not add JS to element event handlers.
+
+<div class="note">
+Polymer event handlers like <code>on-tap</code> are allowed and often reduce the
+amount of addressing (adding an ID just to wire up event handling).
+</div>
+
+### Body
+
+```html
+<h3>$i18n{autofillAddresses}</h3>
+<div class="settings-list">
+  <list id="address-list"></list>
+  <div>
+    <button id="autofill-add-address">$i18n{autofillAddAddress}</button>
+  </div>
+</div>
+<if expr="chromeos">
+  <a href="https://www.google.com/support/chromeos/bin/answer.py?answer=142893"
+      target="_blank">$i18n{learnMore}</a>
+</if>
+```
+
+* Element IDs use `dash-form`
+    * Exception: `camelCase` is allowed in Polymer code for easier
+      `this.$.idName` access.
+
+* Localize all strings using $i18n{}
+
+* Use camelCase for $i18n{} keys names.
+
+* Add 2 space indentation in each new block.
+
+* Adhere to the 80-column limit.
+    * Indent 4 spaces when wrapping a previous line.
+
+* Use double-quotes instead of single-quotes for all attributes.
+
+* Don't close single tags
+    * DO: `<input type="radio">`
+    * DON'T: `<input type="radio" />`
+
+<div class="note">
+All <code>&lt;custom-elements&gt;</code> and some HTML elements like
+<code>&lt;iframe&gt;</code> require closing.
+</div>
+
+* Use the `button` element instead of `<input type="button">`.
+
+* Do not use `<br>`; place blocking elements (`<div>`) as appropriate.
+
+* Do not use spacing-only divs; set the margins on the surrounding elements.
+
+* Only use `<table>` elements when displaying tabular data.
+
+* Do not use the `for` attribute of `<label>`
+    * If you're labelling a checkbox, put the `<input>` inside the `<label>`
+    * If you're labelling purely for accessibility, e.g. a `<select>`, use
+      `aria-labelledby`
+
+## CSS
+
+See the [Google HTML/CSS style
+guide](https://google.github.io/styleguide/htmlcssguide.xml) (and again, browser
+compatibility issues are less relevant for Chrome-only code).
+
+```css
+.raw-button,
+.raw-button:hover,
+.raw-button:active {
+  --sky-color: blue;
+  -webkit-margin-start: 0;
+  background-color: rgb(253, 123, 42);
+  background-repeat: no-repeat;
+  border: none;
+  min-width: 0;
+  padding: 1px 6px;
+}
+```
+
+* Specify one selector per line.
+     * Exception: One rule / one line frames in a `@keyframe` (see below).
+
+* Opening brace on the same line as the last (or only) selector.
+
+* Two-space indentation for each declaration, one declaration per line,
+  terminated by a semicolon.
+
+* Use shorthand notation when possible.
+
+* Alphabetize properties.
+    * `-webkit` properties should be listed at the top, sorted alphabetically.
+    * `--variables` and `--mixins: {}` should be alphabetically declared when
+      possible.
+
+* Insert a space after the colon separating property and value.
+
+* Do not create a class for only one element; use the element ID instead.
+
+* When specifying length values, do not specify units for a zero value, e.g.,
+  `left: 0px;` becomes `left: 0;`
+    * Exception: 0% values in lists of percentages like `hsl(5, 0%, 90%)` or
+      within @keyframe directives, e.g:
+```css
+@keyframe animation-name {
+  0% { /* beginning of animation */ }
+  100% { /* end of animation */ }
+}
+```
+
+* Use single quotes instead of double quotes for all strings.
+
+* Don't use quotes around `url()`s unless needed (i.e. a `data:` URI).
+
+* Class names use `dash-form`.
+
+* If time lengths are less than 1 second, use millisecond granularity.
+    * DO: `transition: height 200ms;`
+    * DON'T: `transition: height 0.2s;`
+
+* Use two colons when addressing a pseudo-element (i.e. `::after`, `::before`,
+  `::-webkit-scrollbar`).
+
+* Use scalable `font-size` units like `%` or `em` to respect users' default font
+  size
+
+* Use `*-top/bottom` instead of `-webkit-*-before/after`
+    * `-top/bottom` are easier to understand (`before/after` is confusingly
+      similar to `start/end`)
+    * `-webkit-*-before/after` has far less advantage than `-webkit-*-start/end`
+
+### Color
+
+* When possible, use named colors (i.e. `white`, `black`) to enhance
+  readability.
+
+* Prefer `rgb()` or `rgba()` with decimal values instead of hex notation
+  (`#rrggbb`) because alpha can be more easily added.
+    * Exception: shades of gray (i.e. `#333`)
+
+* If the hex value is `#rrggbb`, use the shorthand notation `#rgb`.
+
+### URLs
+
+* Don't embed data URIs in source files. Instead, use grit's flattening.
+
+```css
+background-image: url(../path/to/image.svg);
+```
+
+The contents of file.png are base64-encoded and the `url()` is replaced with
+
+```css
+background-image: url(data:image/xml+svg;base64,...);
+```
+
+if `flattenhtml="true"` is specified in your .grd file.
+
+### RTL
+
+```css
+.suboption {
+  -webkit-margin-start: 16px;
+}
+
+#save-button {
+  color: #fff;
+  left: 10px;
+}
+
+html[dir='rtl'] #save-button {
+  right: 10px;
+}
+```
+
+Use RTL-friendly versions of things like `margin` or `padding` where possible:
+
+* `margin-left` -> `-webkit-margin-start`
+* `padding-right` -> `-webkit-padding-end`
+* `text-align: left` -> `text-align: start`
+* `text-align: right` -> `text-align: end`
+* set both `left` for `[dir='ltr']` and `right` for `[dir='rtl']`
+
+For properties that don't have an RTL-friendly alternatives, use
+`html[dir='rtl']` as a prefix in your selectors.
+
+## JavaScript
+
+### Style
+
+See the [Google JavaScript Style
+Guide](https://google.github.io/styleguide/javascriptguide.xml).
+
+* Use `$('element-id')` instead of `document.getElementById`
+
+* Use single-quotes instead of double-quotes for all strings.
+    * `clang-format` now handles this automatically.
+
+* Omit curly braces for single-line if statements.
+
+* Use ES5 getters and setters
+    * Use `@type` (instead of `@return` or `@param`) for JSDoc annotations on
+      getters/setters
+
+* See [Annotating JavaScript for the Closure
+  Compiler](https://developers.google.com/closure/compiler/docs/js-for-compiler)
+  for @ directives
+
+* Prefer `event.preventDefault()` to `return false` from event handlers
+
+### Closure compiler
+
+* Use the [closure
+  compiler](https://chromium.googlesource.com/chromium/src/+/master/docs/closure_compilation.md)
+  to identify JS type errors and enforce correct JSDoc annotations.
+
+* Add a `compiled_resources2.gyp` file to any new web UI code directory.
+
+* Ensure that your `compiled_resources2.gyp` file is included in
+  `third_party/closure_compiler/compiled_resources2.gyp` (or somewhere in its
+  include hierarchy) so that your code is typechecked in an automated way.
+
+* Type Polymer elements by appending 'Element' to the element name, e.g.
+  `/** @type {IronIconElement} */`
+
+* Use explicit nullability in JSDoc type information
+    * Rather than `@type {Object}` use:
+        * `{!Object}` for only Object
+        * `{!Object|undefined}` for an Object that may be undefined
+        * `{?Object}` for Object that may be null
+    * Do the same for typedefs and Array (or any other nullable type)
+
+* Don't add a `.` after template types
+    * DO: `Array<number>`
+    * DON'T: `Array.<number>`
+
+* Don't specify string in template object types. That's the only type of key
+  `Object` can possibly have.
+    * DO: `Object<T>`
+    * DON'T: `Object<string, T>`
+
+### Events
+
+* Use Polymer's `on-tap` for click events instead of `on-click`
+    * `on-tap` handlers should use `stopPropagation()` to prevent parents from
+      handling the event where appropriate.
+
+<div class="note">
+Calling <code>stopPropagation()</code> from an <code>on-tap</code> handler will
+not prevent on-click event handlers, so make sure that <i>on-tap</i> is used
+consistently throughout the page.
+</div>
+
+## Polymer
+
+Also see the [Google Polymer Style Guide](http://go/polymer-style).
+
+* Use a consistent ordering in the “prototype” object passed to `Polymer()`:
+    * `is`
+    * `behaviors`
+    * `properties` (public, then private)
+    * `hostAttributes`
+    * `listeners`, `observers`
+    * `created`, `ready`, `attached`, `detached`
+    * public methods
+    * event handlers, computed functions, and private methods
+ 
+* Use camelCase for element IDs to simplify local DOM accessors (i.e. 
+  `this.$.camelCase` instead of `this.$[‘dash-case’]`).
+
+* Use `this.foo` instead of `newFoo` arguments in observers when possible.
+  This makes changing the type of `this.foo` easier (as the `@type` is
+  duplicated in less places, i.e. `@param`).
+
+```js
+properties: {
+  foo: {type: Number, observer: 'fooChanged_'}
+},
+
+/** @private */
+fooChanged_: function() {
+  this.bar = this.derive(this.foo);
+},
+```
+
+## Grit processing
+
+Grit is a tool that runs at compile time to pack resources together into
+Chromium.
+
+### Preprocessing
+
+Grit can be used to selectively include or exclude code at compile-time in web
+code.  Preprocessing is be enabled by adding the `preprocess="true"` attribute
+inside of a `.grd` file on `<structure>` and `<include>` nodes.
+
+<div class="note">
+These preprocesor statements can live in places that surprise linters or
+formatters (for example: running clang-format on a .js file with an &lt;if&gt;
+in it).  Generally, putting these language-invalid features inside of comments
+helps alleviate problems with unexpected input.
+</div>
+
+`<if>` tags allow conditional logic by evaluating an expression in a
+compile-time environment of grit variables.  These allow conditionally include
+or excluding code.
+
+Example:
+```js
+function isWindows() {
+  // <if expr="win">
+  return true;
+  // </if>
+  return false;
+}
+```
+
+`<include src="[path]">` reads the file at `path` and replaces the `<include>`
+tag with the file contents of `[path]`.
+
+Don't use `</include>` to close these tags; they're not needed nor supported.
+
+<div class="note">
+Using <code>&lt;include&gt;</code> simply pastes the entire contents of a file,
+which can lead to duplication.  If you simply want to ensure some code is loaded
+(and usually you do), you should use HTML Imports instead.
+</div>
+
+Grit can read and inline resources when enabled via `flattenhtml="true"`.
+
+<div class="note">
+The implementation of flattening does HTML parsing and URL detection via regular
+expresions and is not guaranteed to work in all cases.
+</div>
+
+Example:
+
+```css
+.spinner {
+  background: url(../relative/file/path/to/spinner.svg);
+}
+```
+
+Is transformed to:
+
+```css
+.spinner {
+  background: url(data:image/svg+xml;... base64-encoded content ...);
+}
+```
+
+A minification tool can be specified to Grit (like Closure compiler) to
+transform the code before it's packed into a bundle.
diff --git a/testing/android/proguard_for_test.flags b/testing/android/proguard_for_test.flags
index 3326074..51f1428 100644
--- a/testing/android/proguard_for_test.flags
+++ b/testing/android/proguard_for_test.flags
@@ -7,11 +7,22 @@
 # correctness of the .apk we are testing, since it will apply to that .apk as
 # well.
 
-# We want all tests to stick around.
+# Keep all junit3 tests
 -keep class * extends junit.framework.TestCase {
   *;
 }
 
+# Keep all junit4 tests
+-keep @**.RunWith class * {
+    *;
+}
+
+# Keep any class inherited from junit Runner since their constructor is called
+# reflectively
+-keep class * extends org.junit.runner.Runner {
+    *;
+}
+
 # TODO(yfriedman): Remove when crbug.com/488192 is fixed.
 -dontwarn org.apache.http.conn.scheme.LayeredSocketFactory
 
diff --git a/testing/buildbot/chromium.fyi.json b/testing/buildbot/chromium.fyi.json
index 3386b90..7b9bead 100644
--- a/testing/buildbot/chromium.fyi.json
+++ b/testing/buildbot/chromium.fyi.json
@@ -12933,7 +12933,7 @@
               "os": "Ubuntu-14.04"
             }
           ],
-          "shards": 20
+          "shards": 6
         }
       }
     ]
diff --git a/third_party/WebKit/LayoutTests/fast/history/frameset-repeated-name-expected.txt b/third_party/WebKit/LayoutTests/fast/history/frameset-repeated-name-expected.txt
new file mode 100644
index 0000000..13097dd
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/fast/history/frameset-repeated-name-expected.txt
@@ -0,0 +1,15 @@
+Tests that unique names for frames with identical name attributes don't collide. This test requires content_shell --run-layout-test to dump the back forward list.  
+
+============== Back Forward List ==============
+curr->  (file test):fast/history/frameset-repeated-name.html
+            (file test):fast/history/resources/frameset-dest.html (in frame "<!--framePath //<!--frame0-->-->")
+                about:blank (in frame "f1")
+                about:blank (in frame "f2")
+                about:blank (in frame "f3")
+                about:blank (in frame "f4")
+            (file test):fast/history/resources/frameset-dest.html (in frame "<!--framePath //<!--frame1-->-->")
+                about:blank (in frame "<!--framePath //<!--frame1-->/<!--frame0-->-->")
+                about:blank (in frame "<!--framePath //<!--frame1-->/<!--frame1-->-->")
+                about:blank (in frame "<!--framePath //<!--frame1-->/<!--frame2-->-->")
+                about:blank (in frame "<!--framePath //<!--frame1-->/<!--frame3-->-->")
+===============================================
diff --git a/third_party/WebKit/LayoutTests/fast/history/frameset-repeated-name.html b/third_party/WebKit/LayoutTests/fast/history/frameset-repeated-name.html
new file mode 100644
index 0000000..52ac791
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/fast/history/frameset-repeated-name.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script>
+if (testRunner) {
+  testRunner.dumpAsText();
+  testRunner.dumpBackForwardList();
+}
+</script>
+</head>
+<body>
+<p>Tests that unique names for frames with identical name attributes don't
+collide. This test requires content_shell --run-layout-test to dump the back
+forward list.
+<iframe src="resources/frameset-dest.html"></iframe>
+<iframe src="resources/frameset-dest.html"></iframe>
+</body>
+</html>
diff --git a/third_party/WebKit/Source/web/tests/data/frameset-dest.html b/third_party/WebKit/LayoutTests/fast/history/resources/frameset-dest.html
similarity index 100%
rename from third_party/WebKit/Source/web/tests/data/frameset-dest.html
rename to third_party/WebKit/LayoutTests/fast/history/resources/frameset-dest.html
diff --git a/third_party/WebKit/LayoutTests/http/tests/history/frameset-repeated-name-expected.txt b/third_party/WebKit/LayoutTests/http/tests/history/frameset-repeated-name-expected.txt
deleted file mode 100644
index 9a62d76..0000000
--- a/third_party/WebKit/LayoutTests/http/tests/history/frameset-repeated-name-expected.txt
+++ /dev/null
@@ -1,15 +0,0 @@
-
-
-============== Back Forward List ==============
-curr->  http://127.0.0.1:8000/history/frameset-repeated-name.html
-            http://127.0.0.1:8000/history/resources/frameset-dest.html (in frame "<!--framePath //<!--frame0-->-->")
-                about:blank (in frame "f1")
-                about:blank (in frame "f2")
-                about:blank (in frame "f3")
-                about:blank (in frame "f4")
-            http://127.0.0.1:8000/history/resources/frameset-dest.html (in frame "<!--framePath //<!--frame1-->-->")
-                about:blank (in frame "<!--framePath //<!--frame1-->/<!--frame0-->-->")
-                about:blank (in frame "<!--framePath //<!--frame1-->/<!--frame1-->-->")
-                about:blank (in frame "<!--framePath //<!--frame1-->/<!--frame2-->-->")
-                about:blank (in frame "<!--framePath //<!--frame1-->/<!--frame3-->-->")
-===============================================
diff --git a/third_party/WebKit/LayoutTests/http/tests/inspector/bindings/resources/sourcemap-typescript.ts b/third_party/WebKit/LayoutTests/http/tests/inspector/bindings/resources/sourcemap-typescript.ts
index e4e444b..ef0afc3 100644
--- a/third_party/WebKit/LayoutTests/http/tests/inspector/bindings/resources/sourcemap-typescript.ts
+++ b/third_party/WebKit/LayoutTests/http/tests/inspector/bindings/resources/sourcemap-typescript.ts
@@ -1,2 +1 @@
 window.foo = console.log.bind(console, 'foo');
-//# sourceMappingURL=bundle.js.map
diff --git a/third_party/WebKit/LayoutTests/http/tests/inspector/console-test.js b/third_party/WebKit/LayoutTests/http/tests/inspector/console-test.js
index 11e3d1f9..429a429 100644
--- a/third_party/WebKit/LayoutTests/http/tests/inspector/console-test.js
+++ b/third_party/WebKit/LayoutTests/http/tests/inspector/console-test.js
@@ -243,7 +243,7 @@
 
 InspectorTest.expandConsoleMessages = function(callback, deepFilter, sectionFilter)
 {
-    Console.ConsoleView.instance()._viewportThrottler.flush();
+    Console.ConsoleView.instance()._invalidateViewport();
     var messageViews = Console.ConsoleView.instance()._visibleViewMessages;
 
     // Initiate round-trips to fetch necessary data for further rendering.
diff --git a/third_party/WebKit/LayoutTests/http/tests/inspector/elements/styles/inline-stylesheet-sourceurl-and-sourcemapurl-expected.txt b/third_party/WebKit/LayoutTests/http/tests/inspector/elements/styles/inline-stylesheet-sourceurl-and-sourcemapurl-expected.txt
new file mode 100644
index 0000000..720bb8a
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/http/tests/inspector/elements/styles/inline-stylesheet-sourceurl-and-sourcemapurl-expected.txt
@@ -0,0 +1,6 @@
+Verify that in case of complex scenario with both sourceURL and sourceMappingURL in inline stylesheet the sourceMap is resolved properly.
+
+
+Running: Adding style sheet
+SourceMap successfully loaded.
+
diff --git a/third_party/WebKit/LayoutTests/http/tests/inspector/elements/styles/inline-stylesheet-sourceurl-and-sourcemapurl.html b/third_party/WebKit/LayoutTests/http/tests/inspector/elements/styles/inline-stylesheet-sourceurl-and-sourcemapurl.html
new file mode 100644
index 0000000..3223f07
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/http/tests/inspector/elements/styles/inline-stylesheet-sourceurl-and-sourcemapurl.html
@@ -0,0 +1,36 @@
+<html>
+<head>
+<script src="../../inspector-test.js"></script>
+<script src="../../bindings/bindings-test.js"></script>
+<script>
+
+function addStyleSheet() {
+    var styleTag = document.createElement('style');
+    styleTag.textContent = `div {color: red}
+/*# sourceURL=style.css */
+/*# sourceMappingURL=resources/selector-line.css.map */`;
+    document.head.appendChild(styleTag);
+}
+
+async function test()
+{
+    InspectorTest.markStep('Adding style sheet');
+    await Promise.all([
+        InspectorTest.evaluateInPagePromise('addStyleSheet()'),
+        InspectorTest.waitForSourceMap('selector-line.css.map'),
+    ]);
+    InspectorTest.addResult("SourceMap successfully loaded.");
+    InspectorTest.completeTest();
+}
+
+</script>
+</head>
+
+<body onload="runTest()">
+<p>
+Verify that in case of complex scenario with both sourceURL and sourceMappingURL in inline stylesheet
+the sourceMap is resolved properly.
+</p>
+
+</body>
+</html>
diff --git a/third_party/WebKit/LayoutTests/http/tests/inspector/elements/styles/update-locations-on-filesystem-scss-load.html b/third_party/WebKit/LayoutTests/http/tests/inspector/elements/styles/update-locations-on-filesystem-scss-load.html
index 6bf6764e5..9552bbb 100644
--- a/third_party/WebKit/LayoutTests/http/tests/inspector/elements/styles/update-locations-on-filesystem-scss-load.html
+++ b/third_party/WebKit/LayoutTests/http/tests/inspector/elements/styles/update-locations-on-filesystem-scss-load.html
@@ -35,7 +35,7 @@
         var header = event.data;
         var cssLocation = new SDK.CSSLocation(header, 0, 1);
         liveLocation = Bindings.cssWorkspaceBinding.createLiveLocation(cssLocation, function() {}, new Bindings.LiveLocationPool());
-        InspectorTest.cssModel.addEventListener(SDK.CSSModel.Events.SourceMapAttached, afterBind);
+        InspectorTest.cssModel.sourceMapManager().addEventListener(SDK.SourceMapManager.Events.SourceMapAttached, afterBind);
         dumpLiveLocation();
     }
 
diff --git a/third_party/WebKit/LayoutTests/http/tests/inspector/security/main-origin-assigned-despite-request-missing-expected.txt b/third_party/WebKit/LayoutTests/http/tests/inspector/security/main-origin-assigned-despite-request-missing-expected.txt
new file mode 100644
index 0000000..176bc3e
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/http/tests/inspector/security/main-origin-assigned-despite-request-missing-expected.txt
@@ -0,0 +1,16 @@
+Tests that the Main Origin is assigned even if there is no matching Request.
+
+Page origin: http://127.0.0.1:8000
+Detected main origin: http://127.0.0.1:8000
+Group: Main Origin
+<SPAN class=title >
+http://127.0.0.1:8000
+</SPAN>
+Group: Unknown / Canceled
+<SPAN class=title >
+https://foo.test
+</SPAN>
+<SPAN class=title >
+https://bar.test
+</SPAN>
+
diff --git a/third_party/WebKit/LayoutTests/http/tests/inspector/security/main-origin-assigned-despite-request-missing.html b/third_party/WebKit/LayoutTests/http/tests/inspector/security/main-origin-assigned-despite-request-missing.html
new file mode 100644
index 0000000..bf22953
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/http/tests/inspector/security/main-origin-assigned-despite-request-missing.html
@@ -0,0 +1,38 @@
+<html>
+<head>
+<script src="../inspector-test.js"></script>
+<script src="../security-test.js"></script>
+<script>
+  function test()
+  {
+    //** @type {!Protocol.Security.InsecureContentStatus} */
+    var insecureContentStatus = { ranMixedContent: false, displayedMixedContent: false, ranContentWithCertErrors: false, displayedContentWithCertErrors: false, ranInsecureContentStyle: Protocol.Security.SecurityState.Insecure, displayedInsecureContentStyle: Protocol.Security.SecurityState.Neutral };
+    InspectorTest.mainTarget.model(Security.SecurityModel).dispatchEventToListeners(Security.SecurityModel.Events.SecurityStateChanged, new Security.PageSecurityState(Protocol.Security.SecurityState.Secure, true, [], insecureContentStatus, null));
+
+    const page_url = InspectorTest.resourceTreeModel.mainFrame.url;
+    const page_origin =  Common.ParsedURL.extractOrigin(page_url);
+    InspectorTest.addResult("Page origin: " + page_origin);
+    // Fire a Main Frame Navigation event without firing a NetworkRequest first.
+    InspectorTest.mainTarget.model(SDK.ResourceTreeModel).dispatchEventToListeners(SDK.ResourceTreeModel.Events.MainFrameNavigated, InspectorTest.resourceTreeModel.mainFrame);
+    // Validate that this set the MainOrigin in the sidebar
+    const detectedMainOrigin = Security.SecurityPanel._instance()._sidebarTree._mainOrigin;
+    InspectorTest.addResult("Detected main origin: " + detectedMainOrigin);
+
+    // Send subdownload resource requests to other origins.
+    const request1 = new SDK.NetworkRequest(InspectorTest.mainTarget, 0, "https://foo.test/favicon.ico", page_url, 0, 0, null);
+    InspectorTest.dispatchRequestFinished(request1);
+    const request2 = new SDK.NetworkRequest(InspectorTest.mainTarget, 0, "https://bar.test/bar.css", page_url, 0, 0, null);
+    InspectorTest.dispatchRequestFinished(request2);
+
+    // Send one request to the Same Origin as the original page to ensure it appears in the group.
+    const request3 = new SDK.NetworkRequest(InspectorTest.mainTarget, 0, detectedMainOrigin + "/favicon.ico", page_url, 0, 0, null);
+    InspectorTest.dispatchRequestFinished(request3);
+    InspectorTest.dumpSecurityPanelSidebarOrigins();
+    InspectorTest.completeTest();
+}
+</script>
+</head>
+<body onload="runTest()">
+<p>Tests that the Main Origin is assigned even if there is no matching Request.</p>
+</body>
+</html>
diff --git a/third_party/WebKit/LayoutTests/inspector/elements/styles-4/styles-rerequest-sourcemap-on-watchdog.html b/third_party/WebKit/LayoutTests/inspector/elements/styles-4/styles-rerequest-sourcemap-on-watchdog.html
index 38f73e5f..f573f10 100644
--- a/third_party/WebKit/LayoutTests/inspector/elements/styles-4/styles-rerequest-sourcemap-on-watchdog.html
+++ b/third_party/WebKit/LayoutTests/inspector/elements/styles-4/styles-rerequest-sourcemap-on-watchdog.html
@@ -13,19 +13,19 @@
 
 function test()
 {
-    InspectorTest.cssModel.addEventListener(SDK.CSSModel.Events.SourceMapAttached, onInitialSourceMap);
+    InspectorTest.cssModel.sourceMapManager().addEventListener(SDK.SourceMapManager.Events.SourceMapAttached, onInitialSourceMap);
 
     InspectorTest.evaluateInPagePromise("addStyleSheet()");
 
     function onInitialSourceMap()
     {
-        InspectorTest.cssModel.removeEventListener(SDK.CSSModel.Events.SourceMapAttached, onInitialSourceMap);
+        InspectorTest.cssModel.removeEventListener(SDK.SourceMapManager.Events.SourceMapAttached, onInitialSourceMap);
         InspectorTest.waitForScriptSource("styles-rerequest-sourcemap-on-watchdog.css", onCSSFile);
     }
 
     function onCSSFile(uiSourceCode)
     {
-        InspectorTest.addSniffer(SDK.CSSModel.prototype, "_sourceMapLoadedForTest", onSourceMapRerequested);
+        InspectorTest.addSniffer(SDK.SourceMapManager.prototype, "_sourceMapLoadedForTest", onSourceMapRerequested);
         uiSourceCode.addRevision("div { color: blue; } /*# sourceMappingURL=styles-rerequest-sourcemap-on-watchdog.css.map */");
     }
 
diff --git a/third_party/WebKit/LayoutTests/inspector/extensions/extensions-panel.html b/third_party/WebKit/LayoutTests/inspector/extensions/extensions-panel.html
index cbcbb1a..78c6d05 100644
--- a/third_party/WebKit/LayoutTests/inspector/extensions/extensions-panel.html
+++ b/third_party/WebKit/LayoutTests/inspector/extensions/extensions-panel.html
@@ -69,7 +69,7 @@
         function consoleMessageAdded()
         {
             ConsoleModel.consoleModel.removeEventListener(ConsoleModel.ConsoleModel.Events.MessageAdded, wrappedConsoleMessageAdded);
-            Console.ConsoleView.instance()._viewportThrottler.flush();
+            Console.ConsoleView.instance()._invalidateViewport();
             InspectorTest.deprecatedRunAfterPendingDispatches(clickOnMessage)
         }
 
diff --git a/third_party/WebKit/LayoutTests/inspector/sass/sass-test.js b/third_party/WebKit/LayoutTests/inspector/sass/sass-test.js
index bcb06b833..b0fe524 100644
--- a/third_party/WebKit/LayoutTests/inspector/sass/sass-test.js
+++ b/third_party/WebKit/LayoutTests/inspector/sass/sass-test.js
@@ -18,13 +18,20 @@
 
 InspectorTest.loadASTMapping = function(header, callback)
 {
-    var completeSourceMapURL = Common.ParsedURL.completeURL(header.sourceURL, header.sourceMapURL);
-    SDK.TextSourceMap.load(completeSourceMapURL, header.sourceURL).then(onSourceMapLoaded);
+    var sourceMapManager = header.cssModel().sourceMapManager();
+    var sourceMap = sourceMapManager.sourceMapForClient(header);
+    if (sourceMap)  {
+        callback(sourceMap.editable() ? sourceMap : null);
+        return;
+    }
+    sourceMapManager.addEventListener(SDK.SourceMapManager.Events.SourceMapAttached, onAttached);
 
-    function onSourceMapLoaded(sourceMap)
-    {
-        InspectorTest.sassSourceMapFactory().editableSourceMap(header.cssModel().target(), sourceMap)
-            .then(map => callback(map));
+    function onAttached(event) {
+        if (event.data !== header)
+            return;
+        sourceMapManager.removeEventListener(SDK.SourceMapManager.Events.SourceMapAttached, onAttached);
+        var sourceMap = sourceMapManager.sourceMapForClient(header);
+        callback(sourceMap.editable()? sourceMap : null);
     }
 }
 
diff --git a/third_party/WebKit/LayoutTests/inspector/sass/test-mapping-with-cache-busting-url.html b/third_party/WebKit/LayoutTests/inspector/sass/test-mapping-with-cache-busting-url.html
index 44dc83a..d80ead7 100644
--- a/third_party/WebKit/LayoutTests/inspector/sass/test-mapping-with-cache-busting-url.html
+++ b/third_party/WebKit/LayoutTests/inspector/sass/test-mapping-with-cache-busting-url.html
@@ -19,14 +19,13 @@
 
 function test()
 {
-    Runtime.experiments.enableForTest("liveSASS");
     InspectorTest.evaluateInPage("simulateBrowserSync()", function() { });
-    InspectorTest.cssModel.addEventListener(SDK.CSSModel.Events.SourceMapAttached, onSourceMapAttached);
+    InspectorTest.cssModel.sourceMapManager().addEventListener(SDK.SourceMapManager.Events.SourceMapAttached, onSourceMapAttached);
 
     function onSourceMapAttached(event)
     {
         var header = event.data;
-        var sourceMap = InspectorTest.cssModel.sourceMapForHeader(header);
+        var sourceMap = InspectorTest.cssModel.sourceMapManager().sourceMapForClient(header);
         InspectorTest.addResult("SourceMap is editable: " + sourceMap.editable());
         InspectorTest.completeTest();
     }
diff --git a/third_party/WebKit/LayoutTests/inspector/sass/test-ssp-breaking-edits.html b/third_party/WebKit/LayoutTests/inspector/sass/test-ssp-breaking-edits.html
index 209b6078..4806fbb1 100644
--- a/third_party/WebKit/LayoutTests/inspector/sass/test-ssp-breaking-edits.html
+++ b/third_party/WebKit/LayoutTests/inspector/sass/test-ssp-breaking-edits.html
@@ -15,7 +15,6 @@
 function test()
 {
     var sourceCode;
-    Runtime.experiments.enableForTest("liveSASS");
     InspectorTest.evaluateInPage("insertStyleSheet()", onStyleSheet);
 
     function onStyleSheet()
diff --git a/third_party/WebKit/LayoutTests/inspector/sass/test-ssp-editing.html b/third_party/WebKit/LayoutTests/inspector/sass/test-ssp-editing.html
index 1ae5a3d..3dbae4c 100644
--- a/third_party/WebKit/LayoutTests/inspector/sass/test-ssp-editing.html
+++ b/third_party/WebKit/LayoutTests/inspector/sass/test-ssp-editing.html
@@ -15,7 +15,6 @@
 function test()
 {
     var sourceCode;
-    Runtime.experiments.enableForTest("liveSASS");
     InspectorTest.evaluateInPage("insertStyleSheet()", onStyleSheet);
 
     function onStyleSheet()
diff --git a/third_party/WebKit/LayoutTests/inspector/sass/test-ssp-incremental-edit-invalid-value.html b/third_party/WebKit/LayoutTests/inspector/sass/test-ssp-incremental-edit-invalid-value.html
index 72f8c5d1..fe4e140 100644
--- a/third_party/WebKit/LayoutTests/inspector/sass/test-ssp-incremental-edit-invalid-value.html
+++ b/third_party/WebKit/LayoutTests/inspector/sass/test-ssp-incremental-edit-invalid-value.html
@@ -15,7 +15,6 @@
 function test()
 {
     var sourceCode;
-    Runtime.experiments.enableForTest("liveSASS");
     InspectorTest.evaluateInPage("insertStyleSheet()", onStyleSheet);
 
     function onStyleSheet()
diff --git a/third_party/WebKit/Source/build/scripts/css_properties.py b/third_party/WebKit/Source/build/scripts/css_properties.py
index 1a798a70..98636a7 100755
--- a/third_party/WebKit/Source/build/scripts/css_properties.py
+++ b/third_party/WebKit/Source/build/scripts/css_properties.py
@@ -4,7 +4,9 @@
 # found in the LICENSE file.
 
 import json5_generator
-import name_utilities
+from name_utilities import (
+    upper_camel_case, lower_camel_case, enum_for_css_property, enum_for_css_property_alias
+)
 
 
 class CSSProperties(json5_generator.Writer):
@@ -47,9 +49,9 @@
                 ' or both'
 
         for offset, property in enumerate(properties):
-            property['property_id'] = name_utilities.enum_for_css_property(property['name'])
-            property['upper_camel_name'] = name_utilities.camel_case(property['name'])
-            property['lower_camel_name'] = name_utilities.lower_first(property['upper_camel_name'])
+            property['property_id'] = enum_for_css_property(property['name'])
+            property['upper_camel_name'] = upper_camel_case(property['name'])
+            property['lower_camel_name'] = lower_camel_case(property['name'])
             property['enum_value'] = self._first_enum_value + offset
             property['is_internal'] = property['name'].startswith('-internal-')
 
@@ -62,17 +64,14 @@
         # Update property aliases to include the fields of the property being aliased.
         for i, alias in enumerate(self._aliases):
             aliased_property = self._properties[
-                name_utilities.enum_for_css_property(alias['alias_for'])]
+                enum_for_css_property(alias['alias_for'])]
             updated_alias = aliased_property.copy()
             updated_alias['name'] = alias['name']
             updated_alias['alias_for'] = alias['alias_for']
-            updated_alias['property_id'] = \
-                name_utilities.enum_for_css_property_alias(alias['name'])
+            updated_alias['property_id'] = enum_for_css_property_alias(alias['name'])
             updated_alias['enum_value'] = aliased_property['enum_value'] + 512
-            updated_alias['upper_camel_name'] = \
-                name_utilities.camel_case(alias['name'])
-            updated_alias['lower_camel_name'] = \
-                name_utilities.lower_first(updated_alias['upper_camel_name'])
+            updated_alias['upper_camel_name'] = upper_camel_case(alias['name'])
+            updated_alias['lower_camel_name'] = lower_camel_case(alias['name'])
             self._aliases[i] = updated_alias
         self._properties_including_aliases += self._aliases
 
diff --git a/third_party/WebKit/Source/build/scripts/make_computed_style_base.py b/third_party/WebKit/Source/build/scripts/make_computed_style_base.py
index 0809366..d001d03 100755
--- a/third_party/WebKit/Source/build/scripts/make_computed_style_base.py
+++ b/third_party/WebKit/Source/build/scripts/make_computed_style_base.py
@@ -10,27 +10,29 @@
 import template_expander
 import make_style_builder
 
-from name_utilities import camel_case, lower_first, upper_first_letter, enum_for_css_keyword
+from name_utilities import (
+    enum_for_css_keyword, enum_value_name, class_member_name, method_name
+)
 
 
 # Temporary hard-coded list of fields that are not CSS properties.
 # Ideally these would be specified in a .json5 file.
 NONPROPERTY_FIELDS = [
-    {'name': 'isLink', 'field_template': 'monotonic_flag'},
+    {'name': 'IsLink', 'field_template': 'monotonic_flag'},
     # Style can not be shared.
-    {'name': 'unique', 'field_template': 'monotonic_flag'},
+    {'name': 'Unique', 'field_template': 'monotonic_flag'},
     # Whether this style is affected by these pseudo-classes.
-    {'name': 'affectedByFocus', 'field_template': 'monotonic_flag'},
-    {'name': 'affectedByHover', 'field_template': 'monotonic_flag'},
-    {'name': 'affectedByActive', 'field_template': 'monotonic_flag'},
-    {'name': 'affectedByDrag', 'field_template': 'monotonic_flag'},
+    {'name': 'AffectedByFocus', 'field_template': 'monotonic_flag'},
+    {'name': 'AffectedByHover', 'field_template': 'monotonic_flag'},
+    {'name': 'AffectedByActive', 'field_template': 'monotonic_flag'},
+    {'name': 'AffectedByDrag', 'field_template': 'monotonic_flag'},
     # A non-inherited property references a variable or @apply is used
-    {'name': 'hasVariableReferenceFromNonInheritedProperty', 'field_template': 'monotonic_flag'},
+    {'name': 'HasVariableReferenceFromNonInheritedProperty', 'field_template': 'monotonic_flag'},
     # Explicitly inherits a non-inherited property
-    {'name': 'hasExplicitlyInheritedProperties', 'field_template': 'monotonic_flag'},
+    {'name': 'HasExplicitlyInheritedProperties', 'field_template': 'monotonic_flag'},
     # These properties only have generated storage, and their methods are handwritten in ComputedStyle.
     # TODO(shend): Remove these fields and delete the 'storage_only' template.
-    {'name': 'emptyState', 'field_template': 'storage_only', 'size': 1}
+    {'name': 'EmptyState', 'field_template': 'storage_only', 'size': 1}
 ]
 
 
@@ -121,8 +123,7 @@
         # Only generate enums for keyword properties that use the default field_type_path.
         if property_['field_template'] == 'keyword' and property_['field_type_path'] is None:
             enum_name = property_['type_name']
-            # From the Blink style guide: Enum members should use InterCaps with an initial capital letter. [names-enum-members]
-            enum_values = [('k' + camel_case(k)) for k in property_['keywords']]
+            enum_values = [enum_value_name(k) for k in property_['keywords']]
 
             if enum_name in enums:
                 # There's an enum with the same name, check if the enum values are the same
@@ -140,29 +141,27 @@
     """
     Create a property field from a CSS property and return the Field object.
     """
-    property_name = property_['name_for_methods']
-    property_name_lower = lower_first(property_name)
+    name_for_methods = property_['name_for_methods']
 
-    # From the Blink style guide: Other data members should be prefixed by "m_". [names-data-members]
-    field_name = 'm_' + property_name_lower
     bits_needed = math.log(len(property_['keywords']), 2)  # TODO: implement for non-enums
     type_name = property_['type_name']
 
     # For now, the getter name should match the field name. Later, getter names
     # will start with an uppercase letter, so if they conflict with the type name,
     # add 'get' to the front.
-    getter_method_name = property_name_lower
-    if type_name == property_name:
-        getter_method_name = 'get' + property_name
+    if type_name != name_for_methods:
+        getter_method_name = method_name(name_for_methods)
+    else:
+        getter_method_name = method_name('get-' + name_for_methods)
 
     assert property_['initial_keyword'] is not None, \
         ('MakeComputedStyleBase requires an initial keyword for keyword fields, none specified '
          'for property ' + property_['name'])
-    default_value = type_name + '::k' + camel_case(property_['initial_keyword'])
+    default_value = type_name + '::' + enum_value_name(property_['initial_keyword'])
 
     return Field(
         'property',
-        name=field_name,
+        name=class_member_name(name_for_methods),
         property_name=property_['name'],
         inherited=property_['inherited'],
         independent=property_['independent'],
@@ -171,10 +170,10 @@
         size=int(math.ceil(bits_needed)),
         default_value=default_value,
         getter_method_name=getter_method_name,
-        setter_method_name='set' + property_name,
-        initial_method_name='initial' + property_name,
-        resetter_method_name='reset' + property_name,
-        is_inherited_method_name=property_name_lower + 'IsInherited',
+        setter_method_name=method_name('set-' + name_for_methods),
+        initial_method_name=method_name('initial-' + name_for_methods),
+        resetter_method_name=method_name('reset-' + name_for_methods),
+        is_inherited_method_name=method_name(name_for_methods + '-IsInherited'),
     )
 
 
@@ -183,24 +182,21 @@
     Create the field used for an inheritance fast path from an independent CSS property,
     and return the Field object.
     """
-    property_name = property_['name_for_methods']
-    property_name_lower = lower_first(property_name)
-
-    field_name_suffix_upper = property_name + 'IsInherited'
-    field_name_suffix_lower = property_name_lower + 'IsInherited'
+    name_for_methods = property_['name_for_methods']
+    name_for_methods_suffixed = name_for_methods + 'IsInherited'
 
     return Field(
         'inherited_flag',
-        name='m_' + field_name_suffix_lower,
+        name=class_member_name(name_for_methods_suffixed),
         property_name=property_['name'],
         type_name='bool',
         field_template='flag',
         size=1,
         default_value='true',
-        getter_method_name=field_name_suffix_lower,
-        setter_method_name='set' + field_name_suffix_upper,
-        initial_method_name='initial' + field_name_suffix_upper,
-        resetter_method_name='reset' + field_name_suffix_upper,
+        getter_method_name=method_name(name_for_methods_suffixed),
+        setter_method_name=method_name('set-' + name_for_methods_suffixed),
+        initial_method_name=method_name('initial-' + name_for_methods_suffixed),
+        resetter_method_name=method_name('reset-' + name_for_methods_suffixed),
     )
 
 
@@ -211,8 +207,7 @@
     # TODO(shend): Make this work for nonflags
     assert property_['field_template'] in ('flag', 'monotonic_flag', 'storage_only'), \
         "Nonproperties with arbitrary templates are not yet supported"
-    member_name = 'm_' + property_['name']
-    field_name_upper = upper_first_letter(property_['name'])
+    name_for_methods = property_['name_for_methods']
 
     if property_['field_template'] == 'storage_only':
         assert 'size' in property_, 'storage_only fields need to specify a size'
@@ -223,16 +218,16 @@
 
     return Field(
         'nonproperty',
-        name=member_name,
-        property_name=property_['name'],
+        name=class_member_name(name_for_methods),
+        property_name=name_for_methods,
         type_name='bool',
         field_template=property_['field_template'],
         size=size,
         default_value='false',
-        getter_method_name=property_['name'],
-        setter_method_name='set' + field_name_upper,
-        initial_method_name='initial' + field_name_upper,
-        resetter_method_name='reset' + field_name_upper,
+        getter_method_name=method_name(name_for_methods),
+        setter_method_name=method_name('set-' + name_for_methods),
+        initial_method_name=method_name('initial-' + name_for_methods),
+        resetter_method_name=method_name('reset-' + name_for_methods),
     )
 
 
@@ -254,6 +249,7 @@
     # TODO(shend): Merge NONPROPERTY_FIELDS with property_values so that properties and
     # nonproperties can be treated uniformly.
     for property_ in NONPROPERTY_FIELDS:
+        property_['name_for_methods'] = property_['name']
         fields.append(_create_nonproperty_field(property_))
 
     return fields
@@ -382,8 +378,8 @@
         for property_ in self._properties.values():
             if property_['field_template'] == 'keyword':
                 mappings[property_['type_name']] = {
-                    'default_value': 'k' + camel_case(property_['initial_keyword']),
-                    'mapping': [('k' + camel_case(k), enum_for_css_keyword(k)) for k in property_['keywords']],
+                    'default_value': enum_value_name(property_['initial_keyword']),
+                    'mapping': [(enum_value_name(k), enum_for_css_keyword(k)) for k in property_['keywords']],
                 }
 
         return {
diff --git a/third_party/WebKit/Source/build/scripts/name_utilities.py b/third_party/WebKit/Source/build/scripts/name_utilities.py
index c5fdcaa..ca3fa3a 100644
--- a/third_party/WebKit/Source/build/scripts/name_utilities.py
+++ b/third_party/WebKit/Source/build/scripts/name_utilities.py
@@ -69,6 +69,13 @@
     return upper_first_letter(name)
 
 
+def lower_first_letter(name):
+    """Return name with first letter lowercased."""
+    if not name:
+        return ''
+    return name[0].lower() + name[1:]
+
+
 def upper_first_letter(name):
     """Return name with first letter uppercased."""
     if not name:
@@ -76,14 +83,6 @@
     return name[0].upper() + name[1:]
 
 
-def camel_case(css_name):
-    """Convert hyphen-separated-name to UpperCamelCase.
-
-    E.g., '-foo-bar' becomes 'FooBar'.
-    """
-    return ''.join(upper_first_letter(word) for word in css_name.split('-'))
-
-
 def to_macro_style(name):
     s1 = re.sub('(.)([A-Z][a-z]+)', r'\1_\2', name)
     return re.sub('([a-z0-9])([A-Z])', r'\1_\2', s1).upper()
@@ -108,12 +107,49 @@
 
 
 def enum_for_css_keyword(keyword):
-    return 'CSSValue' + ''.join(camel_case(keyword))
+    return 'CSSValue' + upper_camel_case(keyword)
 
 
 def enum_for_css_property(property_name):
-    return 'CSSProperty' + ''.join(camel_case(property_name))
+    return 'CSSProperty' + upper_camel_case(property_name)
 
 
 def enum_for_css_property_alias(property_name):
-    return 'CSSPropertyAlias' + camel_case(property_name)
+    return 'CSSPropertyAlias' + upper_camel_case(property_name)
+
+# TODO(shend): Merge these with the above methods.
+# and update all the generators to use these ones.
+# TODO(shend): Switch external callers of these methods to use the high level
+# API below instead.
+
+
+def split_name(name):
+    """Splits a name in some format to a list of words"""
+    return re.findall(r'(?:[A-Z][a-z]*)|[a-z]+|(?:\d+[a-z]*)', upper_first_letter(name))
+
+
+def upper_camel_case(name):
+    return ''.join(upper_first_letter(word) for word in split_name(name))
+
+
+def lower_camel_case(name):
+    return lower_first_letter(upper_camel_case(name))
+
+# Use these high level naming functions which describe the semantics of the name,
+# rather than a particular style.
+
+
+def enum_type_name(name):
+    return upper_camel_case(name)
+
+
+def enum_value_name(name):
+    return 'k' + upper_camel_case(name)
+
+
+def class_member_name(name):
+    return 'm_' + lower_camel_case(name)
+
+
+def method_name(name):
+    return lower_camel_case(name)
diff --git a/third_party/WebKit/Source/core/editing/DOMSelection.cpp b/third_party/WebKit/Source/core/editing/DOMSelection.cpp
index 5b9f4478..1c73eeed 100644
--- a/third_party/WebKit/Source/core/editing/DOMSelection.cpp
+++ b/third_party/WebKit/Source/core/editing/DOMSelection.cpp
@@ -258,11 +258,15 @@
 
   // 5. Set ([DOM4]) the start and the end of newRange to (node, offset).
   newRange->setStart(node, offset, exceptionState);
-  if (exceptionState.hadException())
+  if (exceptionState.hadException()) {
+    newRange->dispose();
     return;
+  }
   newRange->setEnd(node, offset, exceptionState);
-  if (exceptionState.hadException())
+  if (exceptionState.hadException()) {
+    newRange->dispose();
     return;
+  }
 
   // 6. Set the context object's range to newRange.
   updateFrameSelection(
diff --git a/third_party/WebKit/Source/core/frame/LocalDOMWindow.cpp b/third_party/WebKit/Source/core/frame/LocalDOMWindow.cpp
index aa776fe5..222a4e0 100644
--- a/third_party/WebKit/Source/core/frame/LocalDOMWindow.cpp
+++ b/third_party/WebKit/Source/core/frame/LocalDOMWindow.cpp
@@ -1081,12 +1081,19 @@
   if (!isCurrentlyDisplayedInFrame())
     return;
 
+  // Avoid calling out to notify the embedder if the browsing context name
+  // didn't change. This is important to avoid violating the browser assumption
+  // that the unique name doesn't change if the browsing context name doesn't
+  // change.
+  // TODO(dcheng): This comment is indicative of a problematic layering
+  // violation. The browser should not be relying on the renderer to get this
+  // correct; unique name calculation should be moved up into the browser.
   if (name == frame()->tree().name())
     return;
 
   frame()->tree().setName(name);
   ASSERT(frame()->loader().client());
-  frame()->loader().client()->didChangeName(name, frame()->tree().uniqueName());
+  frame()->loader().client()->didChangeName(name);
 }
 
 void LocalDOMWindow::setStatus(const String& string) {
diff --git a/third_party/WebKit/Source/core/frame/LocalFrameClient.h b/third_party/WebKit/Source/core/frame/LocalFrameClient.h
index 373d7d1..da4d9fd 100644
--- a/third_party/WebKit/Source/core/frame/LocalFrameClient.h
+++ b/third_party/WebKit/Source/core/frame/LocalFrameClient.h
@@ -266,7 +266,7 @@
 
   virtual WebCookieJar* cookieJar() const = 0;
 
-  virtual void didChangeName(const String& name, const String& uniqueName) {}
+  virtual void didChangeName(const String&) {}
 
   virtual void didEnforceInsecureRequestPolicy(WebInsecureRequestPolicy) {}
 
diff --git a/third_party/WebKit/Source/core/loader/FrameLoader.cpp b/third_party/WebKit/Source/core/loader/FrameLoader.cpp
index e2003b6..f108a6c 100644
--- a/third_party/WebKit/Source/core/loader/FrameLoader.cpp
+++ b/third_party/WebKit/Source/core/loader/FrameLoader.cpp
@@ -413,7 +413,6 @@
     m_currentItem = HistoryItem::create();
   m_currentItem->setURL(m_documentLoader->urlForHistory());
   m_currentItem->setDocumentState(m_frame->document()->formElementsState());
-  m_currentItem->setTarget(m_frame->tree().uniqueName());
   m_currentItem->setReferrer(SecurityPolicy::generateReferrer(
       m_documentLoader->getRequest().getReferrerPolicy(), m_currentItem->url(),
       m_documentLoader->getRequest().httpReferrer()));
diff --git a/third_party/WebKit/Source/core/loader/HistoryItem.cpp b/third_party/WebKit/Source/core/loader/HistoryItem.cpp
index 97aeb25..8e68e7c 100644
--- a/third_party/WebKit/Source/core/loader/HistoryItem.cpp
+++ b/third_party/WebKit/Source/core/loader/HistoryItem.cpp
@@ -63,10 +63,6 @@
   return m_referrer;
 }
 
-const String& HistoryItem::target() const {
-  return m_target;
-}
-
 void HistoryItem::setURLString(const String& urlString) {
   if (m_urlString != urlString)
     m_urlString = urlString;
@@ -82,10 +78,6 @@
                                                 referrer.referrer);
 }
 
-void HistoryItem::setTarget(const String& target) {
-  m_target = target;
-}
-
 const ScrollOffset& HistoryItem::visualViewportScrollOffset() const {
   return m_visualViewportScrollOffset;
 }
diff --git a/third_party/WebKit/Source/core/loader/HistoryItem.h b/third_party/WebKit/Source/core/loader/HistoryItem.h
index e8e2f402..453643da 100644
--- a/third_party/WebKit/Source/core/loader/HistoryItem.h
+++ b/third_party/WebKit/Source/core/loader/HistoryItem.h
@@ -55,7 +55,6 @@
   KURL url() const;
 
   const Referrer& referrer() const;
-  const String& target() const;
 
   EncodedFormData* formData();
   const AtomicString& formContentType() const;
@@ -83,7 +82,6 @@
   void setURL(const KURL&);
   void setURLString(const String&);
   void setReferrer(const Referrer&);
-  void setTarget(const String&);
 
   void setStateObject(PassRefPtr<SerializedScriptValue>);
   SerializedScriptValue* stateObject() const { return m_stateObject.get(); }
@@ -118,7 +116,6 @@
 
   String m_urlString;
   Referrer m_referrer;
-  String m_target;
 
   bool m_didSaveScrollOrScaleState;
   ScrollOffset m_visualViewportScrollOffset;
diff --git a/third_party/WebKit/Source/core/page/FrameTree.cpp b/third_party/WebKit/Source/core/page/FrameTree.cpp
index 36ae58dc..a5e7194 100644
--- a/third_party/WebKit/Source/core/page/FrameTree.cpp
+++ b/third_party/WebKit/Source/core/page/FrameTree.cpp
@@ -48,63 +48,7 @@
 FrameTree::~FrameTree() {}
 
 void FrameTree::setName(const AtomicString& name) {
-  // This method should only be called for local frames
-  // (remote frames should be updated via setPrecalculatedName).
-  DCHECK(m_thisFrame->isLocalFrame());
-
-  // When this method is called, m_uniqueName should be already initialized.
-  // This assert helps ensure that early return (a few lines below) won't
-  // result in an uninitialized m_uniqueName.
-  DCHECK(!m_uniqueName.isNull() || (m_uniqueName.isNull() && !parent()));
-
-  // Do not recalculate m_uniqueName if there is no real change of m_name.
-  // This is not just a performance optimization - other code relies on the
-  // assumption that unique name shouldn't change if the assigned name didn't
-  // change (i.e. code in content::FrameTreeNode::SetFrameName).
-  if (m_name == name)
-    return;
-
   m_name = name;
-
-  // https://crbug.com/607205: Make sure m_uniqueName doesn't change after
-  // initial navigation - session history depends on this.
-  if (toLocalFrame(m_thisFrame)
-          ->loader()
-          .stateMachine()
-          ->committedFirstRealDocumentLoad())
-    return;
-
-  // Leave main frame's unique name set to a null string.
-  if (!parent())
-    return;
-
-  // Remove our old frame name so it's not considered in
-  // calculateUniqueNameForChildFrame call below.
-  m_uniqueName = AtomicString();
-
-  // Calculate a new unique name based on inputs.
-  setUniqueName(parent()->tree().calculateUniqueNameForChildFrame(
-      m_thisFrame, name, nullAtom));
-}
-
-void FrameTree::setPrecalculatedName(const AtomicString& name,
-                                     const AtomicString& uniqueName) {
-  m_name = name;
-
-  if (parent()) {
-    // Non-main frames should have a non-empty unique name.
-    DCHECK(!uniqueName.isEmpty());
-  } else {
-    // Unique name of main frames should always stay empty.
-    DCHECK(uniqueName.isEmpty());
-  }
-
-  // TODO(lukasza): We would like to assert uniqueness below (i.e. by calling
-  // setUniqueName), but
-  // 1) uniqueness is currently violated by provisional/old frame pairs.
-  // 2) there is an unresolved race between 2 OOPIFs, that can result in a
-  //    non-unique |uniqueName| - see https://crbug.com/558680#c14.
-  m_uniqueName = uniqueName;
 }
 
 DISABLE_CFI_PERF
@@ -136,211 +80,6 @@
   return m_thisFrame->client()->firstChild();
 }
 
-bool FrameTree::uniqueNameExists(const String& uniqueNameCandidate) const {
-  // This method is currently O(N), where N = number of frames in the tree.
-
-  // Before recalculating or checking unique name, we set m_uniqueName
-  // to an empty string (so the soon-to-be-removed name does not count
-  // as a collision).  This means that uniqueNameExists would return
-  // false positives when called with an empty |name|.
-  DCHECK(!uniqueNameCandidate.isEmpty());
-
-  for (Frame* frame = top(); frame; frame = frame->tree().traverseNext()) {
-    if (frame->tree().uniqueName() == uniqueNameCandidate)
-      return true;
-  }
-  return false;
-}
-
-AtomicString FrameTree::calculateUniqueNameForNewChildFrame(
-    const AtomicString& name,
-    const AtomicString& fallbackName) const {
-  AtomicString uniqueName =
-      calculateUniqueNameForChildFrame(nullptr, name, fallbackName);
-
-  // Caller will likely set the name via setPrecalculatedName, which
-  // unfortunately cannot currently assert uniqueness of the name - let's
-  // therefore assert the uniqueness here.
-  DCHECK(!uniqueNameExists(uniqueName));
-
-  return uniqueName;
-}
-
-String FrameTree::generateUniqueNameCandidate(bool existingChildFrame) const {
-  const char framePathPrefix[] = "<!--framePath ";
-  const int framePathPrefixLength = 14;
-  const int framePathSuffixLength = 3;
-
-  // Find the nearest parent that has a frame with a path in it.
-  HeapVector<Member<Frame>, 16> chain;
-  Frame* frame;
-  for (frame = m_thisFrame; frame; frame = frame->tree().parent()) {
-    if (frame->tree().uniqueName().startsWith(framePathPrefix))
-      break;
-    chain.push_back(frame);
-  }
-  StringBuilder uniqueName;
-  uniqueName.append(framePathPrefix);
-  if (frame) {
-    uniqueName.append(frame->tree().uniqueName().getString().substring(
-        framePathPrefixLength, frame->tree().uniqueName().length() -
-                                   framePathPrefixLength -
-                                   framePathSuffixLength));
-  }
-  for (int i = chain.size() - 1; i >= 0; --i) {
-    frame = chain[i];
-    uniqueName.append('/');
-    uniqueName.append(frame->tree().uniqueName());
-  }
-
-  uniqueName.append("/<!--frame");
-  uniqueName.appendNumber(childCount() - (existingChildFrame ? 1 : 0));
-  uniqueName.append("-->-->");
-
-  // NOTE: This name might not be unique - see http://crbug.com/588800.
-  return uniqueName.toAtomicString();
-}
-
-String FrameTree::generateFramePosition(Frame* child) const {
-  // This method is currently O(N), where N = number of frames in the tree.
-
-  StringBuilder framePositionBuilder;
-  framePositionBuilder.append("<!--framePosition");
-
-  if (!child) {
-    framePositionBuilder.append('-');
-    framePositionBuilder.appendNumber(childCount());
-    child = m_thisFrame;
-  }
-
-  while (child->tree().parent()) {
-    int numberOfSiblingsBeforeChild = 0;
-    Frame* sibling = child->tree().parent()->tree().firstChild();
-    while (sibling != child) {
-      sibling = sibling->tree().nextSibling();
-      numberOfSiblingsBeforeChild++;
-    }
-
-    framePositionBuilder.append('-');
-    framePositionBuilder.appendNumber(numberOfSiblingsBeforeChild);
-
-    child = child->tree().parent();
-  }
-
-  // NOTE: The generated string is not guaranteed to be unique, but should
-  // have a better chance of being unique than the string generated by
-  // generateUniqueNameCandidate, because we embed extra information into the
-  // string:
-  // 1) we walk the full chain of ancestors, all the way to the main frame
-  // 2) we use frame-position-within-parent (aka |numberOfSiblingsBeforeChild|)
-  //    instead of sibling-count.
-  return framePositionBuilder.toString();
-}
-
-AtomicString FrameTree::appendUniqueSuffix(
-    const String& prefix,
-    const String& likelyUniqueSuffix) const {
-  // Verify that we are not doing unnecessary work.
-  DCHECK(uniqueNameExists(prefix));
-
-  // We want unique name to be stable across page reloads - this is why
-  // we use a deterministic |numberOfTries| rather than a random number
-  // (a random number would be more likely to avoid a collision, but
-  // would change after every page reload).
-  int numberOfTries = 0;
-
-  // Keep trying |prefix| + |likelyUniqueSuffix| + |numberOfTries|
-  // concatenations until we get a truly unique name.
-  String candidate;
-  do {
-    StringBuilder uniqueNameBuilder;
-    uniqueNameBuilder.append(prefix);
-    uniqueNameBuilder.append(likelyUniqueSuffix);
-    uniqueNameBuilder.append('/');
-    uniqueNameBuilder.appendNumber(numberOfTries++);
-    uniqueNameBuilder.append("-->");
-
-    candidate = uniqueNameBuilder.toString();
-  } while (uniqueNameExists(candidate));
-  return AtomicString(candidate);
-}
-
-AtomicString FrameTree::calculateUniqueNameForChildFrame(
-    Frame* child,
-    const AtomicString& assignedName,
-    const AtomicString& fallbackName) const {
-  // Try to use |assignedName| (i.e. window.name or iframe.name) or
-  // |fallbackName| if possible.
-  const AtomicString& requestedName =
-      assignedName.isEmpty() ? fallbackName : assignedName;
-  if (!requestedName.isEmpty() && !uniqueNameExists(requestedName) &&
-      requestedName != "_blank")
-    return requestedName;
-
-  String candidate = generateUniqueNameCandidate(child);
-  if (!uniqueNameExists(candidate))
-    return AtomicString(candidate);
-
-  String likelyUniqueSuffix = generateFramePosition(child);
-  return appendUniqueSuffix(candidate, likelyUniqueSuffix);
-
-  // Description of the current unique name format
-  // ---------------------------------------------
-  //
-  // Changing the format of unique name is undesirable, because it breaks
-  // backcompatibility of session history (which stores unique names
-  // generated in the past on user's disk).  This incremental,
-  // backcompatibility-aware approach has resulted so far in the following
-  // rather baroque format... :
-  //
-  //   uniqueName ::= <nullForMainFrame> | <assignedName> | <generatedName>
-  //                 (generatedName is used if assignedName is
-  //                 non-unique / conflicts with other frame's unique name.
-  //
-  //   assignedName ::= value of iframe's name attribute
-  //                 or value assigned to window.name (*before* the first
-  //                    real commit - afterwards unique name stays immutable).
-  //
-  //   generatedName ::= oldGeneratedName newUniqueSuffix?
-  //                  (newUniqueSuffix is only present if oldGeneratedName was
-  //                  not unique after all)
-  //
-  //   oldGeneratedName ::= "<!--framePath //" ancestorChain
-  //                        "/<!--frame" childCount "-->-->"
-  //                  (oldGeneratedName is generated by
-  //                  generateUniqueNameCandidate method).
-  //
-  //   childCount ::= current number of siblings
-  //
-  //   ancestorChain ::= concatenated unique names of ancestor chain,
-  //                     terminated on the first ancestor (if any) starting with
-  //                     "<!--framePath"; each ancestor's unique name is
-  //                     separated by "/" character
-  //   ancestorChain example1: "grandparent/parent"
-  //  (ancestor's unique names :   #1--^   | #2-^ )
-  //   ancestorChain example2:
-  //                     "<!--framePath //foo/bar/<!--frame42-->-->/blah/foobar"
-  //  (ancestor's unique names:
-  //                              ^--#1--^                         | #2 | #3-^ )
-  //
-  //   newUniqueSuffix ::= "<!--framePosition" framePosition "/" retryNumber
-  //                       "-->"
-  //
-  //   framePosition ::= "-" numberOfSiblingsBeforeChild
-  //                     [ framePosition-forParent? ]
-  //
-  //   retryNumber ::= smallest non-negative integer resulting in unique name
-}
-
-void FrameTree::setUniqueName(const AtomicString& uniqueName) {
-  // Only subframes can have a non-null unique name - setUniqueName should
-  // only be called for subframes and never for a main frame.
-  DCHECK(parent());
-
-  DCHECK(!uniqueName.isEmpty() && !uniqueNameExists(uniqueName));
-  m_uniqueName = uniqueName;
-}
-
 Frame* FrameTree::scopedChild(unsigned index) const {
   unsigned scopedIndex = 0;
   for (Frame* child = firstChild(); child;
diff --git a/third_party/WebKit/Source/core/page/FrameTree.h b/third_party/WebKit/Source/core/page/FrameTree.h
index c69edeb..e04ddc85 100644
--- a/third_party/WebKit/Source/core/page/FrameTree.h
+++ b/third_party/WebKit/Source/core/page/FrameTree.h
@@ -39,20 +39,6 @@
   const AtomicString& name() const { return m_name; }
   void setName(const AtomicString&);
 
-  // Unique name of a frame (unique per page).  Mainly used to identify the
-  // frame for session history purposes, but also used in expected results
-  // of layout tests.
-  //
-  // The value should be treated as an unstructured, opaque string.
-  const AtomicString& uniqueName() const { return m_uniqueName; }
-
-  // Directly assigns both the name and uniqueName.  Can be used when
-  // |uniqueName| is already known (i.e. when it has been precalculated by
-  // calculateUniqueNameForNewChildFrame OR when replicating the name between
-  // LocalFrames and RemoteFrames for the same logical frame).
-  void setPrecalculatedName(const AtomicString& name,
-                            const AtomicString& uniqueName);
-
   Frame* parent() const;
   Frame* top() const;
   Frame* nextSibling() const;
@@ -71,47 +57,10 @@
 
   DECLARE_TRACE();
 
-  AtomicString calculateUniqueNameForNewChildFrame(
-      const AtomicString& name,
-      const AtomicString& fallbackName = nullAtom) const;
-
  private:
-  // Returns true if one of frames in the tree already has unique name equal
-  // to |uniqueNameCandidate|.
-  bool uniqueNameExists(const String& uniqueNameCandidate) const;
-
-  // Generates a hopefully-but-not-necessarily unique name based on frame's
-  // relative position in the tree and on unique names of ancestors.
-  String generateUniqueNameCandidate(bool existingChildFrame) const;
-
-  // Generates a hopefully-but-not-necessarily unique suffix based on |child|
-  // absolute position in the tree.  If |child| is nullptr, calculations are
-  // made for a position that a new child of |this| would have.
-  String generateFramePosition(Frame* child) const;
-
-  // Concatenates |prefix|, |likelyUniqueSuffix| (and additional, internally
-  // generated suffix) until the result is a unique name, that doesn't exist
-  // elsewhere in the frame tree.  Returns the unique name built in this way.
-  AtomicString appendUniqueSuffix(const String& prefix,
-                                  const String& likelyUniqueSuffix) const;
-
-  // Calculates a unique name for |child| frame (which might be nullptr if the
-  // child has not yet been created - i.e. when we need unique name for a new
-  // frame).  Tries to use the |assignedName| or |fallbackName| if possible,
-  // otherwise falls back to generating a deterministic,
-  // stable-across-page-reloads string based on |child| position in the tree.
-  AtomicString calculateUniqueNameForChildFrame(
-      Frame* child,
-      const AtomicString& assignedName,
-      const AtomicString& fallbackName = nullAtom) const;
-
-  // Sets |m_uniqueName| and asserts its uniqueness.
-  void setUniqueName(const AtomicString&);
-
   Member<Frame> m_thisFrame;
 
   AtomicString m_name;  // The actual frame name (may be empty).
-  AtomicString m_uniqueName;
 
   mutable unsigned m_scopedChildCount;
 };
diff --git a/third_party/WebKit/Source/devtools/BUILD.gn b/third_party/WebKit/Source/devtools/BUILD.gn
index d9de7733..849a8bec 100644
--- a/third_party/WebKit/Source/devtools/BUILD.gn
+++ b/third_party/WebKit/Source/devtools/BUILD.gn
@@ -475,6 +475,7 @@
   "front_end/sdk/ServiceWorkerCacheModel.js",
   "front_end/sdk/ServiceWorkerManager.js",
   "front_end/sdk/SourceMap.js",
+  "front_end/sdk/SourceMapManager.js",
   "front_end/sdk/Target.js",
   "front_end/sdk/TargetManager.js",
   "front_end/sdk/TracingManager.js",
diff --git a/third_party/WebKit/Source/devtools/front_end/bindings/CSSWorkspaceBinding.js b/third_party/WebKit/Source/devtools/front_end/bindings/CSSWorkspaceBinding.js
index 620a238..fe8044c 100644
--- a/third_party/WebKit/Source/devtools/front_end/bindings/CSSWorkspaceBinding.js
+++ b/third_party/WebKit/Source/devtools/front_end/bindings/CSSWorkspaceBinding.js
@@ -99,8 +99,9 @@
     ];
 
     this._stylesSourceMapping = new Bindings.StylesSourceMapping(cssModel, workspace);
-    this._sassSourceMapping =
-        new Bindings.SASSSourceMapping(cssModel, workspace, Bindings.NetworkProject.forTarget(cssModel.target()));
+    var sourceMapManager = cssModel.sourceMapManager();
+    this._sassSourceMapping = new Bindings.SASSSourceMapping(
+        sourceMapManager, workspace, Bindings.NetworkProject.forTarget(cssModel.target()));
 
     /** @type {!Multimap<!SDK.CSSStyleSheetHeader, !Bindings.CSSWorkspaceBinding.LiveLocation>} */
     this._locations = new Multimap();
diff --git a/third_party/WebKit/Source/devtools/front_end/bindings/SASSSourceMapping.js b/third_party/WebKit/Source/devtools/front_end/bindings/SASSSourceMapping.js
index a6912b0..f7f9507 100644
--- a/third_party/WebKit/Source/devtools/front_end/bindings/SASSSourceMapping.js
+++ b/third_party/WebKit/Source/devtools/front_end/bindings/SASSSourceMapping.js
@@ -33,18 +33,21 @@
  */
 Bindings.SASSSourceMapping = class {
   /**
-   * @param {!SDK.CSSModel} cssModel
+   * @param {!SDK.SourceMapManager} sourceMapManager
    * @param {!Workspace.Workspace} workspace
    * @param {!Bindings.NetworkProject} networkProject
    */
-  constructor(cssModel, workspace, networkProject) {
-    this._cssModel = cssModel;
+  constructor(sourceMapManager, workspace, networkProject) {
+    this._sourceMapManager = sourceMapManager;
     this._networkProject = networkProject;
     this._workspace = workspace;
     this._eventListeners = [
-      this._cssModel.addEventListener(SDK.CSSModel.Events.SourceMapAttached, this._sourceMapAttached, this),
-      this._cssModel.addEventListener(SDK.CSSModel.Events.SourceMapDetached, this._sourceMapDetached, this),
-      this._cssModel.addEventListener(SDK.CSSModel.Events.SourceMapChanged, this._sourceMapChanged, this)
+      this._sourceMapManager.addEventListener(
+          SDK.SourceMapManager.Events.SourceMapAttached, this._sourceMapAttached, this),
+      this._sourceMapManager.addEventListener(
+          SDK.SourceMapManager.Events.SourceMapDetached, this._sourceMapDetached, this),
+      this._sourceMapManager.addEventListener(
+          SDK.SourceMapManager.Events.SourceMapChanged, this._sourceMapChanged, this)
     ];
   }
 
@@ -59,7 +62,7 @@
    */
   _sourceMapAttached(event) {
     var header = /** @type {!SDK.CSSStyleSheetHeader} */ (event.data);
-    var sourceMap = this._cssModel.sourceMapForHeader(header);
+    var sourceMap = this._sourceMapManager.sourceMapForClient(header);
     for (var sassURL of sourceMap.sourceURLs()) {
       var contentProvider = sourceMap.sourceContentProvider(sassURL, Common.resourceTypes.SourceMapStyleSheet);
       var embeddedContent = sourceMap.embeddedContentByURL(sassURL);
@@ -84,7 +87,7 @@
   _sourceMapChanged(event) {
     var sourceMap = /** @type {!SDK.SourceMap} */ (event.data.sourceMap);
     var newSources = /** @type {!Map<string, string>} */ (event.data.newSources);
-    var headers = this._cssModel.headersForSourceMap(sourceMap);
+    var headers = this._sourceMapManager.clientsForSourceMap(sourceMap);
     var handledUISourceCodes = new Set();
     for (var header of headers) {
       Bindings.cssWorkspaceBinding.updateLocations(header);
@@ -111,7 +114,7 @@
     var header = rawLocation.header();
     if (!header)
       return null;
-    var sourceMap = this._cssModel.sourceMapForHeader(header);
+    var sourceMap = this._sourceMapManager.sourceMapForClient(header);
     if (!sourceMap)
       return null;
     var entry = sourceMap.findEntry(rawLocation.lineNumber, rawLocation.columnNumber);
diff --git a/third_party/WebKit/Source/devtools/front_end/common/Throttler.js b/third_party/WebKit/Source/devtools/front_end/common/Throttler.js
index 5bc43cc..0dd9c59 100644
--- a/third_party/WebKit/Source/devtools/front_end/common/Throttler.js
+++ b/third_party/WebKit/Source/devtools/front_end/common/Throttler.js
@@ -57,11 +57,6 @@
     this._innerSchedule(forceTimerUpdate);
   }
 
-  flush() {
-    if (this._process)
-      this._onTimeout();
-  }
-
   /**
    * @param {boolean} forceTimerUpdate
    */
diff --git a/third_party/WebKit/Source/devtools/front_end/console/ConsoleView.js b/third_party/WebKit/Source/devtools/front_end/console/ConsoleView.js
index 091f477a..f32a81c 100644
--- a/third_party/WebKit/Source/devtools/front_end/console/ConsoleView.js
+++ b/third_party/WebKit/Source/devtools/front_end/console/ConsoleView.js
@@ -344,24 +344,24 @@
     this._prompt.clearAutocomplete();
   }
 
-  _scheduleViewportRefresh() {
-    /**
-     * @this {Console.ConsoleView}
-     * @return {!Promise.<undefined>}
-     */
-    function invalidateViewport() {
-      if (this._muteViewportUpdates) {
-        this._maybeDirtyWhileMuted = true;
-        return Promise.resolve();
-      }
-      if (this._needsFullUpdate) {
-        this._updateMessageList();
-        delete this._needsFullUpdate;
-      } else {
-        this._viewport.invalidate();
-      }
+  /**
+   * @return {!Promise.<undefined>}
+   */
+  _invalidateViewport() {
+    if (this._muteViewportUpdates) {
+      this._maybeDirtyWhileMuted = true;
       return Promise.resolve();
     }
+    if (this._needsFullUpdate) {
+      this._updateMessageList();
+      delete this._needsFullUpdate;
+    } else {
+      this._viewport.invalidate();
+    }
+    return Promise.resolve();
+  }
+
+  _scheduleViewportRefresh() {
     if (this._muteViewportUpdates) {
       this._maybeDirtyWhileMuted = true;
       this._scheduleViewportRefreshForTest(true);
@@ -369,7 +369,7 @@
     } else {
       this._scheduleViewportRefreshForTest(false);
     }
-    this._viewportThrottler.schedule(invalidateViewport.bind(this));
+    this._viewportThrottler.schedule(this._invalidateViewport.bind(this));
   }
 
   /**
diff --git a/third_party/WebKit/Source/devtools/front_end/elements/StylesSidebarPane.js b/third_party/WebKit/Source/devtools/front_end/elements/StylesSidebarPane.js
index 0a4d978..3323be7 100644
--- a/third_party/WebKit/Source/devtools/front_end/elements/StylesSidebarPane.js
+++ b/third_party/WebKit/Source/devtools/front_end/elements/StylesSidebarPane.js
@@ -789,7 +789,7 @@
         this._style.styleSheetId ? this._style.cssModel().styleSheetHeaderForId(this._style.styleSheetId) : null;
     if (!header)
       return false;
-    var sourceMap = header.cssModel().sourceMapForHeader(header);
+    var sourceMap = header.cssModel().sourceMapManager().sourceMapForClient(header);
     return sourceMap ? sourceMap.editable() : false;
   }
 
diff --git a/third_party/WebKit/Source/devtools/front_end/layer_viewer/Layers3DView.js b/third_party/WebKit/Source/devtools/front_end/layer_viewer/Layers3DView.js
index 949a6fb..df3eda95 100644
--- a/third_party/WebKit/Source/devtools/front_end/layer_viewer/Layers3DView.js
+++ b/third_party/WebKit/Source/devtools/front_end/layer_viewer/Layers3DView.js
@@ -980,7 +980,7 @@
   forceUpdate() {
     this._queue.forEach(layer => this._updateLayer(layer));
     this._queue = [];
-    this._throttler.flush();
+    this._update();
   }
 
   /**
diff --git a/third_party/WebKit/Source/devtools/front_end/main/Main.js b/third_party/WebKit/Source/devtools/front_end/main/Main.js
index fce6a14e..0dc04d7b 100644
--- a/third_party/WebKit/Source/devtools/front_end/main/Main.js
+++ b/third_party/WebKit/Source/devtools/front_end/main/Main.js
@@ -132,6 +132,8 @@
         Runtime.experiments.enableForTest('audits2');
       if (testPath.indexOf('help/') !== -1)
         Runtime.experiments.enableForTest('releaseNote');
+      if (testPath.indexOf('sass/') !== -1)
+        Runtime.experiments.enableForTest('liveSASS');
     }
 
     Runtime.experiments.setDefaultExperiments(['persistenceValidation']);
diff --git a/third_party/WebKit/Source/devtools/front_end/perf_ui/flameChart.css b/third_party/WebKit/Source/devtools/front_end/perf_ui/flameChart.css
index a07e5e37..822c536 100644
--- a/third_party/WebKit/Source/devtools/front_end/perf_ui/flameChart.css
+++ b/third_party/WebKit/Source/devtools/front_end/perf_ui/flameChart.css
@@ -13,19 +13,16 @@
 }
 
 .flame-chart-highlight-element {
-    background-color: black;
     position: absolute;
-    opacity: 0.2;
     pointer-events: none;
+    background-color: rgba(56, 121, 217, 0.1);
 }
 
 .flame-chart-selected-element {
     position: absolute;
     pointer-events: none;
-    border-color: rgb(56, 121, 217);
-    border-width: 1px;
-    border-style: solid;
-    background-color: rgba(56, 121, 217, 0.2);
+    outline: 2px solid rgb(56, 121, 217);
+    background-color: rgba(56, 121, 217, 0.1);
 }
 
 .flame-chart-v-scroll {
diff --git a/third_party/WebKit/Source/devtools/front_end/sass/SASSSourceMapFactory.js b/third_party/WebKit/Source/devtools/front_end/sass/SASSSourceMapFactory.js
index 5598cd9ac..5bcbbb0 100644
--- a/third_party/WebKit/Source/devtools/front_end/sass/SASSSourceMapFactory.js
+++ b/third_party/WebKit/Source/devtools/front_end/sass/SASSSourceMapFactory.js
@@ -21,8 +21,8 @@
     if (!cssModel)
       return Promise.resolve(/** @type {?SDK.SourceMap} */ (null));
 
-    var header =
-        cssModel.styleSheetHeaders().find(styleSheetHeader => styleSheetHeader.sourceMapURL === sourceMap.url());
+    var headers = cssModel.sourceMapManager().clientsForSourceMap(sourceMap);
+    var header = headers.length ? headers[0] : null;
     if (!header)
       return Promise.resolve(/** @type {?SDK.SourceMap} */ (null));
 
diff --git a/third_party/WebKit/Source/devtools/front_end/sdk/CSSModel.js b/third_party/WebKit/Source/devtools/front_end/sdk/CSSModel.js
index 088c0eb..4857584 100644
--- a/third_party/WebKit/Source/devtools/front_end/sdk/CSSModel.js
+++ b/third_party/WebKit/Source/devtools/front_end/sdk/CSSModel.js
@@ -38,6 +38,8 @@
   constructor(target) {
     super(target);
     this._domModel = /** @type {!SDK.DOMModel} */ (target.model(SDK.DOMModel));
+    /** @type {!SDK.SourceMapManager<!SDK.CSSStyleSheetHeader>} */
+    this._sourceMapManager = new SDK.SourceMapManager(target);
     this._agent = target.cssAgent();
     this._styleLoader = new SDK.CSSModel.ComputedStyleLoader(this);
     this._resourceTreeModel = target.model(SDK.ResourceTreeModel);
@@ -55,14 +57,16 @@
     /** @type {!Map.<!SDK.CSSStyleSheetHeader, !Promise<?string>>} */
     this._originalStyleSheetText = new Map();
 
-    /** @type {!Multimap<string, !Protocol.CSS.StyleSheetId>} */
-    this._sourceMapLoadingStyleSheetsIds = new Multimap();
+    this._sourceMapManager.setEnabled(Common.moduleSetting('cssSourceMapsEnabled').get());
+    Common.moduleSetting('cssSourceMapsEnabled')
+        .addChangeListener(event => this._sourceMapManager.setEnabled(/** @type {boolean} */ (event.data)));
+  }
 
-    /** @type {!Map<string, !SDK.SourceMap>} */
-    this._sourceMapByURL = new Map();
-    /** @type {!Multimap<string, !SDK.CSSStyleSheetHeader>} */
-    this._sourceMapURLToHeaders = new Multimap();
-    Common.moduleSetting('cssSourceMapsEnabled').addChangeListener(this._toggleSourceMapSupport, this);
+  /**
+   * @return {!SDK.SourceMapManager<!SDK.CSSStyleSheetHeader>}
+   */
+  sourceMapManager() {
+    return this._sourceMapManager;
   }
 
   /**
@@ -95,138 +99,6 @@
   }
 
   /**
-   * @param {!Common.Event} event
-   */
-  _toggleSourceMapSupport(event) {
-    var enabled = /** @type {boolean} */ (event.data);
-    var headers = this.styleSheetHeaders();
-    for (var header of headers) {
-      if (enabled)
-        this._attachSourceMap(header);
-      else
-        this._detachSourceMap(header);
-    }
-  }
-
-  /**
-   * @param {!SDK.CSSStyleSheetHeader} header
-   * @return {?SDK.SourceMap}
-   */
-  sourceMapForHeader(header) {
-    return this._sourceMapByURL.get(header.sourceMapURL) || null;
-  }
-
-  _sourceMapLoadedForTest() {
-  }
-
-  /**
-   * @param {!SDK.SourceMap} sourceMap
-   * @return {!Array<!SDK.CSSStyleSheetHeader>}
-   */
-  headersForSourceMap(sourceMap) {
-    return this._sourceMapURLToHeaders.get(sourceMap.url()).valuesArray();
-  }
-
-  /**
-   * @param {!SDK.CSSStyleSheetHeader} header
-   */
-  _attachSourceMap(header) {
-    var sourceMapURL = header.sourceMapURL;
-    if (!sourceMapURL || !Common.moduleSetting('cssSourceMapsEnabled').get())
-      return;
-    if (this._sourceMapByURL.has(sourceMapURL)) {
-      attach.call(this, sourceMapURL, header);
-      return;
-    }
-    if (!this._sourceMapLoadingStyleSheetsIds.has(sourceMapURL)) {
-      SDK.TextSourceMap.load(sourceMapURL, header.sourceURL)
-          .then(onTextSourceMapLoaded.bind(this, sourceMapURL))
-          .then(onSourceMap.bind(this, sourceMapURL));
-    }
-    this._sourceMapLoadingStyleSheetsIds.set(sourceMapURL, header.id);
-
-    /**
-     * @param {string} sourceMapURL
-     * @param {?SDK.TextSourceMap} sourceMap
-     * @return {!Promise<?SDK.SourceMap>}
-     * @this {SDK.CSSModel}
-     */
-    function onTextSourceMapLoaded(sourceMapURL, sourceMap) {
-      if (!sourceMap)
-        return Promise.resolve(/** @type {?SDK.SourceMap} */ (null));
-      var factoryExtension = this._factoryForSourceMap(sourceMap);
-      if (!factoryExtension)
-        return Promise.resolve(/** @type {?SDK.SourceMap} */ (sourceMap));
-      return factoryExtension.instance()
-          .then(factory => factory.editableSourceMap(this.target(), sourceMap))
-          .then(map => map || sourceMap)
-          .catchException(/** @type {?SDK.SourceMap} */ (null));
-    }
-
-    /**
-     * @param {string} sourceMapURL
-     * @param {?SDK.SourceMap} sourceMap
-     * @this {SDK.CSSModel}
-     */
-    function onSourceMap(sourceMapURL, sourceMap) {
-      this._sourceMapLoadedForTest();
-      var styleSheetIds = this._sourceMapLoadingStyleSheetsIds.get(sourceMapURL);
-      this._sourceMapLoadingStyleSheetsIds.removeAll(sourceMapURL);
-      if (!sourceMap)
-        return;
-      var headers = new Set();
-      for (var styleSheetId of styleSheetIds) {
-        var header = this.styleSheetHeaderForId(styleSheetId);
-        if (header)
-          headers.add(header);
-      }
-      if (!headers.size)
-        return;
-      this._sourceMapByURL.set(sourceMapURL, sourceMap);
-      for (var header of headers)
-        attach.call(this, sourceMapURL, header);
-    }
-
-    /**
-     * @param {string} sourceMapURL
-     * @param {!SDK.CSSStyleSheetHeader} header
-     * @this {SDK.CSSModel}
-     */
-    function attach(sourceMapURL, header) {
-      this._sourceMapURLToHeaders.set(sourceMapURL, header);
-      this.dispatchEventToListeners(SDK.CSSModel.Events.SourceMapAttached, header);
-    }
-  }
-
-  /**
-   * @param {!SDK.SourceMap} sourceMap
-   * @return {?Runtime.Extension}
-   */
-  _factoryForSourceMap(sourceMap) {
-    var sourceExtensions = new Set();
-    for (var url of sourceMap.sourceURLs())
-      sourceExtensions.add(Common.ParsedURL.extractExtension(url));
-    for (var runtimeExtension of self.runtime.extensions(SDK.SourceMapFactory)) {
-      var supportedExtensions = new Set(runtimeExtension.descriptor()['extensions']);
-      if (supportedExtensions.containsAll(sourceExtensions))
-        return runtimeExtension;
-    }
-    return null;
-  }
-
-  /**
-   * @param {!SDK.CSSStyleSheetHeader} header
-   */
-  _detachSourceMap(header) {
-    if (!header.sourceMapURL || !this._sourceMapURLToHeaders.hasValue(header.sourceMapURL, header))
-      return;
-    this._sourceMapURLToHeaders.remove(header.sourceMapURL, header);
-    if (!this._sourceMapURLToHeaders.has(header.sourceMapURL))
-      this._sourceMapByURL.delete(header.sourceMapURL);
-    this.dispatchEventToListeners(SDK.CSSModel.Events.SourceMapDetached, header);
-  }
-
-  /**
    * @return {!SDK.DOMModel}
    */
   domModel() {
@@ -246,7 +118,7 @@
     if (!header)
       return original();
 
-    var sourceMap = this.sourceMapForHeader(header);
+    var sourceMap = this._sourceMapManager.sourceMapForClient(header);
     if (!sourceMap)
       return original();
 
@@ -296,9 +168,7 @@
       if (!success)
         return originalAndDetach();
 
-      this._sourceMapByURL.set(header.sourceMapURL, editResult.map);
-      this.dispatchEventToListeners(
-          SDK.CSSModel.Events.SourceMapChanged, {sourceMap: editResult.map, newSources: editResult.newSources});
+      this._sourceMapManager.applySourceMapEdit(editResult);
       return Promise.resolve(true);
     }
 
@@ -311,7 +181,7 @@
     function onError(header, error) {
       Common.console.error(Common.UIString('LiveSASS failed: %s', sourceMap.compiledURL()));
       console.error(error);
-      this._detachSourceMap(header);
+      this._sourceMapManager.detachSourceMap(header);
       return original();
     }
 
@@ -330,7 +200,7 @@
        */
       function detachIfSuccess(success) {
         if (success)
-          this._detachSourceMap(header);
+          this._sourceMapManager.detachSourceMap(header);
         return success;
       }
     }
@@ -830,7 +700,7 @@
       frameIdToStyleSheetIds[styleSheetHeader.frameId] = styleSheetIds;
     }
     styleSheetIds.push(styleSheetHeader.id);
-    this._attachSourceMap(styleSheetHeader);
+    this._sourceMapManager.attachSourceMap(styleSheetHeader, styleSheetHeader.sourceURL, styleSheetHeader.sourceMapURL);
     this.dispatchEventToListeners(SDK.CSSModel.Events.StyleSheetAdded, styleSheetHeader);
   }
 
@@ -854,7 +724,7 @@
         this._styleSheetIdsForURL.remove(url);
     }
     this._originalStyleSheetText.remove(header);
-    this._detachSourceMap(header);
+    this._sourceMapManager.detachSourceMap(header);
     this.dispatchEventToListeners(SDK.CSSModel.Events.StyleSheetRemoved, header);
   }
 
@@ -895,9 +765,9 @@
      * @this {SDK.CSSModel}
      */
     function callback(error, sourceMapURL) {
-      this._detachSourceMap(header);
+      this._sourceMapManager.detachSourceMap(header);
       header.setSourceMapURL(sourceMapURL);
-      this._attachSourceMap(header);
+      this._sourceMapManager.attachSourceMap(header, header.sourceURL, header.sourceMapURL);
       if (error)
         return error;
       if (majorChange)
@@ -933,12 +803,9 @@
     this._styleSheetIdsForURL.clear();
     this._styleSheetIdToHeader.clear();
     for (var i = 0; i < headers.length; ++i) {
-      this._detachSourceMap(headers[i]);
+      this._sourceMapManager.detachSourceMap(headers[i]);
       this.dispatchEventToListeners(SDK.CSSModel.Events.StyleSheetRemoved, headers[i]);
     }
-    this._sourceMapByURL.clear();
-    this._sourceMapURLToHeaders.clear();
-    this._sourceMapLoadingStyleSheetsIds.clear();
   }
 
   /**
@@ -984,6 +851,14 @@
     delete this._cachedMatchedCascadeNode;
     delete this._cachedMatchedCascadePromise;
   }
+
+  /**
+   * @override
+   */
+  dispose() {
+    super.dispose();
+    this._sourceMapManager.dispose();
+  }
 };
 
 SDK.SDKModel.register(SDK.CSSModel, SDK.Target.Capability.DOM);
@@ -999,10 +874,7 @@
   PseudoStateForced: Symbol('PseudoStateForced'),
   StyleSheetAdded: Symbol('StyleSheetAdded'),
   StyleSheetChanged: Symbol('StyleSheetChanged'),
-  StyleSheetRemoved: Symbol('StyleSheetRemoved'),
-  SourceMapAttached: Symbol('SourceMapAttached'),
-  SourceMapDetached: Symbol('SourceMapDetached'),
-  SourceMapChanged: Symbol('SourceMapChanged')
+  StyleSheetRemoved: Symbol('StyleSheetRemoved')
 };
 
 SDK.CSSModel.MediaTypes =
diff --git a/third_party/WebKit/Source/devtools/front_end/sdk/CSSStyleSheetHeader.js b/third_party/WebKit/Source/devtools/front_end/sdk/CSSStyleSheetHeader.js
index e3863709..cc76e5b 100644
--- a/third_party/WebKit/Source/devtools/front_end/sdk/CSSStyleSheetHeader.js
+++ b/third_party/WebKit/Source/devtools/front_end/sdk/CSSStyleSheetHeader.js
@@ -44,9 +44,7 @@
    * @param {string=} sourceMapURL
    */
   setSourceMapURL(sourceMapURL) {
-    var completeSourceMapURL =
-        this.sourceURL && sourceMapURL ? Common.ParsedURL.completeURL(this.sourceURL, sourceMapURL) : sourceMapURL;
-    this.sourceMapURL = completeSourceMapURL;
+    this.sourceMapURL = sourceMapURL;
   }
 
   /**
@@ -67,7 +65,7 @@
    * @return {boolean}
    */
   isAnonymousInlineStyleSheet() {
-    return !this.resourceURL() && !this._cssModel.sourceMapForHeader(this);
+    return !this.resourceURL() && !this._cssModel.sourceMapManager().sourceMapForClient(this);
   }
 
   /**
diff --git a/third_party/WebKit/Source/devtools/front_end/sdk/SourceMapManager.js b/third_party/WebKit/Source/devtools/front_end/sdk/SourceMapManager.js
new file mode 100644
index 0000000..b6ab08a
--- /dev/null
+++ b/third_party/WebKit/Source/devtools/front_end/sdk/SourceMapManager.js
@@ -0,0 +1,241 @@
+// 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.
+
+/**
+ * @template T
+ */
+SDK.SourceMapManager = class extends SDK.SDKObject {
+  /**
+   * @param {!SDK.Target} target
+   */
+  constructor(target) {
+    super(target);
+
+    this._isEnabled = true;
+
+    /** @type {!Map<!T, string>} */
+    this._relativeSourceURL = new Map();
+    /** @type {!Map<!T, string>} */
+    this._relativeSourceMapURL = new Map();
+    /** @type {!Map<!T, string>} */
+    this._resolvedSourceMapURL = new Map();
+
+    /** @type {!Map<string, !SDK.SourceMap>} */
+    this._sourceMapByURL = new Map();
+    /** @type {!Multimap<string, !T>} */
+    this._sourceMapURLToLoadingClients = new Multimap();
+    /** @type {!Multimap<string, !T>} */
+    this._sourceMapURLToClients = new Multimap();
+
+    SDK.targetManager.addEventListener(SDK.TargetManager.Events.InspectedURLChanged, this._inspectedURLChanged, this);
+  }
+
+  /**
+   * @param {boolean} isEnabled
+   */
+  setEnabled(isEnabled) {
+    if (isEnabled === this._isEnabled)
+      return;
+    this._isEnabled = isEnabled;
+    var clients = Array.from(this._resolvedSourceMapURL.keys());
+    for (var client of clients) {
+      var relativeSourceURL = this._relativeSourceURL.get(client);
+      var relativeSourceMapURL = this._relativeSourceMapURL.get(client);
+      this.detachSourceMap(client);
+      if (isEnabled)
+        this.attachSourceMap(client, relativeSourceURL, relativeSourceMapURL);
+    }
+  }
+
+  /**
+   * @param {!Common.Event} event
+   */
+  _inspectedURLChanged(event) {
+    if (event.data !== this.target())
+      return;
+
+    var clients = Array.from(this._resolvedSourceMapURL.keys());
+    for (var client of clients) {
+      var relativeSourceURL = this._relativeSourceURL.get(client);
+      var relativeSourceMapURL = this._relativeSourceMapURL.get(client);
+      var resolvedSourceMapURL = this._resolvedSourceMapURL.get(client);
+      var sourceMapURL = this._resolveRelativeURLs(relativeSourceURL, relativeSourceMapURL).sourceMapURL;
+      if (sourceMapURL !== resolvedSourceMapURL) {
+        this.detachSourceMap(client);
+        this.attachSourceMap(client, relativeSourceURL, relativeSourceMapURL);
+      }
+    }
+  }
+
+  /**
+   * @param {!T} client
+   * @return {?SDK.SourceMap}
+   */
+  sourceMapForClient(client) {
+    var sourceMapURL = this._resolvedSourceMapURL.get(client);
+    return sourceMapURL ? this._sourceMapByURL.get(sourceMapURL) || null : null;
+  }
+
+  /**
+   * @param {!SDK.SourceMap} sourceMap
+   * @return {!Array<!T>}
+   */
+  clientsForSourceMap(sourceMap) {
+    if (this._sourceMapURLToClients.has(sourceMap.url()))
+      return this._sourceMapURLToClients.get(sourceMap.url()).valuesArray();
+    return this._sourceMapURLToLoadingClients.get(sourceMap.url()).valuesArray();
+  }
+
+  /**
+   * @param {!SDK.SourceMap.EditResult} editResult
+   */
+  applySourceMapEdit(editResult) {
+    console.assert(
+        this._sourceMapByURL.has(editResult.map.url()), 'Cannot apply edit result for non-existing source map');
+    this._sourceMapByURL.set(editResult.map.url(), editResult.map);
+    this.dispatchEventToListeners(
+        SDK.SourceMapManager.Events.SourceMapChanged, {sourceMap: editResult.map, newSources: editResult.newSources});
+  }
+
+  /**
+   * @param {string} sourceURL
+   * @param {string} sourceMapURL
+   * @return {!{sourceURL: ?string, sourceMapURL: ?string}}
+   */
+  _resolveRelativeURLs(sourceURL, sourceMapURL) {
+    // |sourceURL| can be a random string, but is generally an absolute path.
+    // Complete it to inspected page url for relative links.
+    var resolvedSourceURL = Common.ParsedURL.completeURL(this.target().inspectedURL(), sourceURL);
+    var resolvedSourceMapURL = resolvedSourceURL ? Common.ParsedURL.completeURL(resolvedSourceURL, sourceMapURL) : null;
+    return {sourceURL: resolvedSourceURL, sourceMapURL: resolvedSourceMapURL};
+  }
+
+  /**
+   * @param {!T} client
+   * @param {string} sourceURL
+   * @param {?string} sourceMapURL
+   */
+  attachSourceMap(client, sourceURL, sourceMapURL) {
+    if (!sourceMapURL)
+      return;
+    console.assert(!this._resolvedSourceMapURL.has(client), 'SourceMap is already attached to client');
+    var resolvedURLs = this._resolveRelativeURLs(sourceURL, sourceMapURL);
+    if (!resolvedURLs.sourceURL || !resolvedURLs.sourceMapURL)
+      return;
+    this._relativeSourceURL.set(client, sourceURL);
+    this._relativeSourceMapURL.set(client, sourceMapURL);
+    this._resolvedSourceMapURL.set(client, resolvedURLs.sourceMapURL);
+
+    sourceURL = resolvedURLs.sourceURL;
+    sourceMapURL = resolvedURLs.sourceMapURL;
+    if (!this._isEnabled)
+      return;
+
+    if (this._sourceMapByURL.has(sourceMapURL)) {
+      attach.call(this, sourceMapURL, client);
+      return;
+    }
+    if (!this._sourceMapURLToLoadingClients.has(sourceMapURL)) {
+      SDK.TextSourceMap.load(sourceMapURL, sourceURL)
+          .then(onTextSourceMapLoaded.bind(this, sourceMapURL))
+          .then(onSourceMap.bind(this, sourceMapURL));
+    }
+    this._sourceMapURLToLoadingClients.set(sourceMapURL, client);
+
+    /**
+     * @param {string} sourceMapURL
+     * @param {?SDK.TextSourceMap} sourceMap
+     * @return {!Promise<?SDK.SourceMap>}
+     * @this {SDK.SourceMapManager}
+     */
+    function onTextSourceMapLoaded(sourceMapURL, sourceMap) {
+      if (!sourceMap)
+        return Promise.resolve(/** @type {?SDK.SourceMap} */ (null));
+      var factoryExtension = this._factoryForSourceMap(sourceMap);
+      if (!factoryExtension)
+        return Promise.resolve(/** @type {?SDK.SourceMap} */ (sourceMap));
+      return factoryExtension.instance()
+          .then(factory => factory.editableSourceMap(this.target(), sourceMap))
+          .then(map => map || sourceMap)
+          .catchException(/** @type {?SDK.SourceMap} */ (null));
+    }
+
+    /**
+     * @param {string} sourceMapURL
+     * @param {?SDK.SourceMap} sourceMap
+     * @this {SDK.SourceMapManager}
+     */
+    function onSourceMap(sourceMapURL, sourceMap) {
+      this._sourceMapLoadedForTest();
+      var clients = this._sourceMapURLToLoadingClients.get(sourceMapURL);
+      this._sourceMapURLToLoadingClients.removeAll(sourceMapURL);
+      if (!sourceMap || !clients.size)
+        return;
+      this._sourceMapByURL.set(sourceMapURL, sourceMap);
+      for (var client of clients)
+        attach.call(this, sourceMapURL, client);
+    }
+
+    /**
+     * @param {string} sourceMapURL
+     * @param {!T} client
+     * @this {SDK.SourceMapManager}
+     */
+    function attach(sourceMapURL, client) {
+      this._sourceMapURLToClients.set(sourceMapURL, client);
+      this.dispatchEventToListeners(SDK.SourceMapManager.Events.SourceMapAttached, client);
+    }
+  }
+
+  /**
+   * @param {!SDK.SourceMap} sourceMap
+   * @return {?Runtime.Extension}
+   */
+  _factoryForSourceMap(sourceMap) {
+    var sourceExtensions = new Set();
+    for (var url of sourceMap.sourceURLs())
+      sourceExtensions.add(Common.ParsedURL.extractExtension(url));
+    for (var runtimeExtension of self.runtime.extensions(SDK.SourceMapFactory)) {
+      var supportedExtensions = new Set(runtimeExtension.descriptor()['extensions']);
+      if (supportedExtensions.containsAll(sourceExtensions))
+        return runtimeExtension;
+    }
+    return null;
+  }
+
+  /**
+   * @param {!T} client
+   */
+  detachSourceMap(client) {
+    var sourceMapURL = this._resolvedSourceMapURL.get(client);
+    this._relativeSourceURL.delete(client);
+    this._relativeSourceMapURL.delete(client);
+    this._resolvedSourceMapURL.delete(client);
+
+    if (!sourceMapURL)
+      return;
+    if (!this._sourceMapURLToClients.hasValue(sourceMapURL, client)) {
+      this._sourceMapURLToLoadingClients.remove(sourceMapURL, client);
+      return;
+    }
+    this._sourceMapURLToClients.remove(sourceMapURL, client);
+    if (!this._sourceMapURLToClients.has(sourceMapURL))
+      this._sourceMapByURL.delete(sourceMapURL);
+    this.dispatchEventToListeners(SDK.SourceMapManager.Events.SourceMapDetached, client);
+  }
+
+  _sourceMapLoadedForTest() {
+  }
+
+  dispose() {
+    SDK.targetManager.removeEventListener(
+        SDK.TargetManager.Events.InspectedURLChanged, this._inspectedURLChanged, this);
+  }
+};
+
+SDK.SourceMapManager.Events = {
+  SourceMapAttached: Symbol('SourceMapAttached'),
+  SourceMapDetached: Symbol('SourceMapDetached'),
+  SourceMapChanged: Symbol('SourceMapChanged')
+};
diff --git a/third_party/WebKit/Source/devtools/front_end/sdk/module.json b/third_party/WebKit/Source/devtools/front_end/sdk/module.json
index 84b4b79..a98364b 100644
--- a/third_party/WebKit/Source/devtools/front_end/sdk/module.json
+++ b/third_party/WebKit/Source/devtools/front_end/sdk/module.json
@@ -151,6 +151,7 @@
         "ResourceTreeModel.js",
         "SecurityOriginManager.js",
         "SourceMap.js",
+        "SourceMapManager.js",
         "NetworkManager.js",
         "NetworkRequest.js",
         "PaintProfiler.js",
diff --git a/third_party/WebKit/Source/devtools/front_end/security/SecurityPanel.js b/third_party/WebKit/Source/devtools/front_end/security/SecurityPanel.js
index 46d6acf9..b33cdad 100644
--- a/third_party/WebKit/Source/devtools/front_end/security/SecurityPanel.js
+++ b/third_party/WebKit/Source/devtools/front_end/security/SecurityPanel.js
@@ -350,11 +350,14 @@
     // explanations to reflect the new counts.
     this._mainView.refreshExplanations();
 
-    if (request) {
-      var origin = Common.ParsedURL.extractOrigin(request.url());
-      this._sidebarTree.setMainOrigin(origin);
+    // If we could not find a matching request (as in the case of clicking
+    // through an interstitial, see crbug.com/669309), set the origin based upon
+    // the url data from the MainFrameNavigated event itself.
+    let origin = Common.ParsedURL.extractOrigin(request ? request.url() : frame.url);
+    this._sidebarTree.setMainOrigin(origin);
+
+    if (request)
       this._processRequest(request);
-    }
   }
 
   _onInterstitialShown() {
diff --git a/third_party/WebKit/Source/modules/vr/VRDisplay.cpp b/third_party/WebKit/Source/modules/vr/VRDisplay.cpp
index 2fb51eb7..6f13f70e 100644
--- a/third_party/WebKit/Source/modules/vr/VRDisplay.cpp
+++ b/third_party/WebKit/Source/modules/vr/VRDisplay.cpp
@@ -600,6 +600,11 @@
       m_vrFrameId, gpu::MailboxHolder(staticImage->mailbox(),
                                       staticImage->syncToken(), GL_TEXTURE_2D));
 
+  // If preserveDrawingBuffer is false, must clear now. Normally this
+  // happens as part of compositing, but that's not active while
+  // presenting, so run the responsible code directly.
+  m_renderingContext->markCompositedAndClearBackbufferIfNeeded();
+
   // If we're not deferring the wait for transferring the mailbox,
   // we need to wait for it now to prevent the image going out of
   // scope before its mailbox is retrieved.
diff --git a/third_party/WebKit/Source/modules/webgl/WebGLRenderingContextBase.cpp b/third_party/WebKit/Source/modules/webgl/WebGLRenderingContextBase.cpp
index 833553d4..ceada6f9 100644
--- a/third_party/WebKit/Source/modules/webgl/WebGLRenderingContextBase.cpp
+++ b/third_party/WebKit/Source/modules/webgl/WebGLRenderingContextBase.cpp
@@ -1517,6 +1517,11 @@
   return combinedClear ? CombinedClear : JustClear;
 }
 
+void WebGLRenderingContextBase::markCompositedAndClearBackbufferIfNeeded() {
+  markLayerComposited();
+  clearIfComposited();
+}
+
 void WebGLRenderingContextBase::restoreScissorEnabled() {
   if (isContextLost())
     return;
diff --git a/third_party/WebKit/Source/modules/webgl/WebGLRenderingContextBase.h b/third_party/WebKit/Source/modules/webgl/WebGLRenderingContextBase.h
index fbde1a8..fb2455a 100644
--- a/third_party/WebKit/Source/modules/webgl/WebGLRenderingContextBase.h
+++ b/third_party/WebKit/Source/modules/webgl/WebGLRenderingContextBase.h
@@ -582,6 +582,10 @@
 
   ScriptPromise commit(ScriptState*, ExceptionState&);
 
+  // For use by WebVR which doesn't use the normal compositing path.
+  // This clears the backbuffer if preserveDrawingBuffer is false.
+  void markCompositedAndClearBackbufferIfNeeded();
+
  protected:
   friend class EXTDisjointTimerQuery;
   friend class EXTDisjointTimerQueryWebGL2;
diff --git a/third_party/WebKit/Source/platform/graphics/ExpensiveCanvasHeuristicParameters.h b/third_party/WebKit/Source/platform/graphics/ExpensiveCanvasHeuristicParameters.h
index 3fbcba25..82b7c53d 100644
--- a/third_party/WebKit/Source/platform/graphics/ExpensiveCanvasHeuristicParameters.h
+++ b/third_party/WebKit/Source/platform/graphics/ExpensiveCanvasHeuristicParameters.h
@@ -94,7 +94,7 @@
   // acceleration on the destination first. If that does not succeed,
   // we disable acceleration on the source canvas. Either way, future
   // readbacks are prevented.
-  EnableAccelerationToAvoidReadbacks = 1,
+  EnableAccelerationToAvoidReadbacks = 0,
 
 };  // enum
 
diff --git a/third_party/WebKit/Source/web/LocalFrameClientImpl.cpp b/third_party/WebKit/Source/web/LocalFrameClientImpl.cpp
index 83daad1..090852de 100644
--- a/third_party/WebKit/Source/web/LocalFrameClientImpl.cpp
+++ b/third_party/WebKit/Source/web/LocalFrameClientImpl.cpp
@@ -881,11 +881,10 @@
     m_webFrame->client()->frameFocused();
 }
 
-void LocalFrameClientImpl::didChangeName(const String& name,
-                                         const String& uniqueName) {
+void LocalFrameClientImpl::didChangeName(const String& name) {
   if (!m_webFrame->client())
     return;
-  m_webFrame->client()->didChangeName(name, uniqueName);
+  m_webFrame->client()->didChangeName(name);
 }
 
 void LocalFrameClientImpl::didEnforceInsecureRequestPolicy(
diff --git a/third_party/WebKit/Source/web/LocalFrameClientImpl.h b/third_party/WebKit/Source/web/LocalFrameClientImpl.h
index 7c317de2..d332c862 100644
--- a/third_party/WebKit/Source/web/LocalFrameClientImpl.h
+++ b/third_party/WebKit/Source/web/LocalFrameClientImpl.h
@@ -181,7 +181,7 @@
 
   WebCookieJar* cookieJar() const override;
   void frameFocused() const override;
-  void didChangeName(const String& name, const String& uniqueName) override;
+  void didChangeName(const String&) override;
   void didEnforceInsecureRequestPolicy(WebInsecureRequestPolicy) override;
   void didUpdateToUniqueOrigin() override;
   void didChangeSandboxFlags(Frame* childFrame, SandboxFlags) override;
diff --git a/third_party/WebKit/Source/web/WebFrame.cpp b/third_party/WebKit/Source/web/WebFrame.cpp
index 2d42aa2..96c4787 100644
--- a/third_party/WebKit/Source/web/WebFrame.cpp
+++ b/third_party/WebKit/Source/web/WebFrame.cpp
@@ -70,7 +70,6 @@
 
   FrameHost* host = oldFrame->host();
   AtomicString name = oldFrame->tree().name();
-  AtomicString uniqueName = oldFrame->tree().uniqueName();
   FrameOwner* owner = oldFrame->owner();
 
   v8::HandleScope handleScope(v8::Isolate::GetCurrent());
@@ -106,8 +105,7 @@
                            TRACE_EVENT_SCOPE_THREAD, "frame", &localFrame);
     }
   } else {
-    toWebRemoteFrameImpl(frame)->initializeCoreFrame(host, owner, name,
-                                                     uniqueName);
+    toWebRemoteFrameImpl(frame)->initializeCoreFrame(host, owner, name);
   }
 
   if (m_parent && oldFrame->hasReceivedUserGesture())
diff --git a/third_party/WebKit/Source/web/WebFrameImplBase.h b/third_party/WebKit/Source/web/WebFrameImplBase.h
index 0e6b304d..9b5367c 100644
--- a/third_party/WebKit/Source/web/WebFrameImplBase.h
+++ b/third_party/WebKit/Source/web/WebFrameImplBase.h
@@ -35,8 +35,7 @@
 
   virtual void initializeCoreFrame(FrameHost*,
                                    FrameOwner*,
-                                   const AtomicString& name,
-                                   const AtomicString& uniqueName) = 0;
+                                   const AtomicString& name) = 0;
   // TODO(dcheng): Rename this to coreFrame()? This probably also shouldn't be
   // const...
   virtual Frame* frame() const = 0;
diff --git a/third_party/WebKit/Source/web/WebHistoryItem.cpp b/third_party/WebKit/Source/web/WebHistoryItem.cpp
index 7dafe65..6315cb9 100644
--- a/third_party/WebKit/Source/web/WebHistoryItem.cpp
+++ b/third_party/WebKit/Source/web/WebHistoryItem.cpp
@@ -50,10 +50,12 @@
 
 void WebHistoryItem::reset() {
   m_private.reset();
+  m_target.reset();
 }
 
 void WebHistoryItem::assign(const WebHistoryItem& other) {
   m_private = other.m_private;
+  m_target = other.m_target;
 }
 
 WebString WebHistoryItem::urlString() const {
@@ -78,12 +80,12 @@
       Referrer(referrer, static_cast<ReferrerPolicy>(referrerPolicy)));
 }
 
-WebString WebHistoryItem::target() const {
-  return m_private->target();
+const WebString& WebHistoryItem::target() const {
+  return m_target;
 }
 
 void WebHistoryItem::setTarget(const WebString& target) {
-  m_private->setTarget(target);
+  m_target = target;
 }
 
 WebFloatPoint WebHistoryItem::visualViewportScrollOffset() const {
diff --git a/third_party/WebKit/Source/web/WebLocalFrameImpl.cpp b/third_party/WebKit/Source/web/WebLocalFrameImpl.cpp
index ab83a9a..ebda68aa 100644
--- a/third_party/WebKit/Source/web/WebLocalFrameImpl.cpp
+++ b/third_party/WebKit/Source/web/WebLocalFrameImpl.cpp
@@ -545,10 +545,6 @@
   m_selfKeepAlive.clear();
 }
 
-WebString WebLocalFrameImpl::uniqueName() const {
-  return frame()->tree().uniqueName();
-}
-
 WebString WebLocalFrameImpl::assignedName() const {
   return frame()->tree().name();
 }
@@ -1532,11 +1528,8 @@
   LocalFrame* frame = LocalFrame::create(webFrame->m_localFrameClientImpl.get(),
                                          oldFrame->page(), tempOwner,
                                          interfaceProvider, interfaceRegistry);
-  // Set the name and unique name directly, bypassing any of the normal logic
-  // to calculate unique name.
-  frame->tree().setPrecalculatedName(
-      toWebRemoteFrameImpl(oldWebFrame)->frame()->tree().name(),
-      toWebRemoteFrameImpl(oldWebFrame)->frame()->tree().uniqueName());
+  frame->tree().setName(
+      toWebRemoteFrameImpl(oldWebFrame)->frame()->tree().name());
   webFrame->setCoreFrame(frame);
 
   frame->setOwner(oldFrame->owner());
@@ -1608,12 +1601,11 @@
 
 void WebLocalFrameImpl::initializeCoreFrame(FrameHost* host,
                                             FrameOwner* owner,
-                                            const AtomicString& name,
-                                            const AtomicString& uniqueName) {
+                                            const AtomicString& name) {
   setCoreFrame(LocalFrame::create(m_localFrameClientImpl.get(),
                                   host ? &host->page() : nullptr, owner,
                                   m_interfaceProvider, m_interfaceRegistry));
-  frame()->tree().setPrecalculatedName(name, uniqueName);
+  frame()->tree().setName(name);
   // We must call init() after m_frame is assigned because it is referenced
   // during init(). Note that this may dispatch JS events; the frame may be
   // detached after init() returns.
@@ -1661,19 +1653,16 @@
   // solution. subResourceAttributeName returns just one attribute name. The
   // element might not have the attribute, and there might be other attributes
   // which can identify the element.
-  AtomicString uniqueName = frame()->tree().calculateUniqueNameForNewChildFrame(
-      name,
-      ownerElement->getAttribute(ownerElement->subResourceAttributeName()));
   WebLocalFrameImpl* webframeChild =
       toWebLocalFrameImpl(m_client->createChildFrame(
-          this, scope, name, uniqueName,
+          this, scope, name,
+          ownerElement->getAttribute(ownerElement->subResourceAttributeName()),
           static_cast<WebSandboxFlags>(ownerElement->getSandboxFlags()),
           ownerProperties));
   if (!webframeChild)
     return nullptr;
 
-  webframeChild->initializeCoreFrame(frame()->host(), ownerElement, name,
-                                     uniqueName);
+  webframeChild->initializeCoreFrame(frame()->host(), ownerElement, name);
   // Initializing the core frame may cause the new child to be detached, since
   // it may dispatch a load event in the parent.
   if (!webframeChild->parent())
diff --git a/third_party/WebKit/Source/web/WebLocalFrameImpl.h b/third_party/WebKit/Source/web/WebLocalFrameImpl.h
index 6aafb13a..e296884f 100644
--- a/third_party/WebKit/Source/web/WebLocalFrameImpl.h
+++ b/third_party/WebKit/Source/web/WebLocalFrameImpl.h
@@ -88,7 +88,6 @@
   // TODO(dcheng): Fix sorting here; a number of method have been moved to
   // WebLocalFrame but not correctly updated here.
   void close() override;
-  WebString uniqueName() const override;
   WebString assignedName() const override;
   void setName(const WebString&) override;
   WebVector<WebIconURL> iconURLs(int iconTypesMask) const override;
@@ -317,8 +316,7 @@
   // WebFrameImplBase methods:
   void initializeCoreFrame(FrameHost*,
                            FrameOwner*,
-                           const AtomicString& name,
-                           const AtomicString& uniqueName) override;
+                           const AtomicString& name) override;
   LocalFrame* frame() const override { return m_frame.get(); }
 
   void willBeDetached();
diff --git a/third_party/WebKit/Source/web/WebRemoteFrameImpl.cpp b/third_party/WebKit/Source/web/WebRemoteFrameImpl.cpp
index 0b68993..22ce4740 100644
--- a/third_party/WebKit/Source/web/WebRemoteFrameImpl.cpp
+++ b/third_party/WebKit/Source/web/WebRemoteFrameImpl.cpp
@@ -79,11 +79,6 @@
   m_selfKeepAlive.clear();
 }
 
-WebString WebRemoteFrameImpl::uniqueName() const {
-  NOTREACHED();
-  return WebString();
-}
-
 WebString WebRemoteFrameImpl::assignedName() const {
   NOTREACHED();
   return WebString();
@@ -325,7 +320,6 @@
 WebLocalFrame* WebRemoteFrameImpl::createLocalChild(
     WebTreeScopeType scope,
     const WebString& name,
-    const WebString& uniqueName,
     WebSandboxFlags sandboxFlags,
     WebFrameClient* client,
     blink::InterfaceProvider* interfaceProvider,
@@ -343,7 +337,7 @@
   // (one from the initial frame creation, and one from swapping it into the
   // remote process).  FrameLoader might need a special initialization function
   // for this case to avoid that duplicate navigation.
-  child->initializeCoreFrame(frame()->host(), owner, name, uniqueName);
+  child->initializeCoreFrame(frame()->host(), owner, name);
   // Partially related with the above FIXME--the init() call may trigger JS
   // dispatch. However,
   // if the parent is remote, it should never be detached synchronously...
@@ -353,18 +347,16 @@
 
 void WebRemoteFrameImpl::initializeCoreFrame(FrameHost* host,
                                              FrameOwner* owner,
-                                             const AtomicString& name,
-                                             const AtomicString& uniqueName) {
+                                             const AtomicString& name) {
   setCoreFrame(RemoteFrame::create(m_frameClient.get(),
                                    host ? &host->page() : nullptr, owner));
   frame()->createView();
-  m_frame->tree().setPrecalculatedName(name, uniqueName);
+  m_frame->tree().setName(name);
 }
 
 WebRemoteFrame* WebRemoteFrameImpl::createRemoteChild(
     WebTreeScopeType scope,
     const WebString& name,
-    const WebString& uniqueName,
     WebSandboxFlags sandboxFlags,
     WebRemoteFrameClient* client,
     WebFrame* opener) {
@@ -372,7 +364,7 @@
   appendChild(child);
   RemoteFrameOwner* owner = RemoteFrameOwner::create(
       static_cast<SandboxFlags>(sandboxFlags), WebFrameOwnerProperties());
-  child->initializeCoreFrame(frame()->host(), owner, name, uniqueName);
+  child->initializeCoreFrame(frame()->host(), owner, name);
   return child;
 }
 
@@ -393,8 +385,7 @@
   return static_cast<RemoteFrameClientImpl*>(frame.client())->webFrame();
 }
 
-void WebRemoteFrameImpl::setReplicatedOrigin(
-    const WebSecurityOrigin& origin) const {
+void WebRemoteFrameImpl::setReplicatedOrigin(const WebSecurityOrigin& origin) {
   DCHECK(frame());
   frame()->securityContext()->setReplicatedOrigin(origin);
 
@@ -413,21 +404,19 @@
   }
 }
 
-void WebRemoteFrameImpl::setReplicatedSandboxFlags(
-    WebSandboxFlags flags) const {
+void WebRemoteFrameImpl::setReplicatedSandboxFlags(WebSandboxFlags flags) {
   DCHECK(frame());
   frame()->securityContext()->enforceSandboxFlags(
       static_cast<SandboxFlags>(flags));
 }
 
-void WebRemoteFrameImpl::setReplicatedName(const WebString& name,
-                                           const WebString& uniqueName) const {
+void WebRemoteFrameImpl::setReplicatedName(const WebString& name) {
   DCHECK(frame());
-  frame()->tree().setPrecalculatedName(name, uniqueName);
+  frame()->tree().setName(name);
 }
 
 void WebRemoteFrameImpl::setReplicatedFeaturePolicyHeader(
-    const WebParsedFeaturePolicy& parsedHeader) const {
+    const WebParsedFeaturePolicy& parsedHeader) {
   if (RuntimeEnabledFeatures::featurePolicyEnabled()) {
     WebFeaturePolicy* parentFeaturePolicy = nullptr;
     if (parent()) {
@@ -448,24 +437,24 @@
 void WebRemoteFrameImpl::addReplicatedContentSecurityPolicyHeader(
     const WebString& headerValue,
     WebContentSecurityPolicyType type,
-    WebContentSecurityPolicySource source) const {
+    WebContentSecurityPolicySource source) {
   frame()->securityContext()->contentSecurityPolicy()->addPolicyFromHeaderValue(
       headerValue, static_cast<ContentSecurityPolicyHeaderType>(type),
       static_cast<ContentSecurityPolicyHeaderSource>(source));
 }
 
-void WebRemoteFrameImpl::resetReplicatedContentSecurityPolicy() const {
+void WebRemoteFrameImpl::resetReplicatedContentSecurityPolicy() {
   frame()->securityContext()->resetReplicatedContentSecurityPolicy();
 }
 
 void WebRemoteFrameImpl::setReplicatedInsecureRequestPolicy(
-    WebInsecureRequestPolicy policy) const {
+    WebInsecureRequestPolicy policy) {
   DCHECK(frame());
   frame()->securityContext()->setInsecureRequestPolicy(policy);
 }
 
 void WebRemoteFrameImpl::setReplicatedPotentiallyTrustworthyUniqueOrigin(
-    bool isUniqueOriginPotentiallyTrustworthy) const {
+    bool isUniqueOriginPotentiallyTrustworthy) {
   DCHECK(frame());
   // If |isUniqueOriginPotentiallyTrustworthy| is true, then the origin must be
   // unique.
@@ -478,7 +467,7 @@
           isUniqueOriginPotentiallyTrustworthy);
 }
 
-void WebRemoteFrameImpl::dispatchLoadEventOnFrameOwner() const {
+void WebRemoteFrameImpl::dispatchLoadEventOnFrameOwner() {
   DCHECK(frame()->owner()->isLocal());
   frame()->owner()->dispatchLoad();
 }
diff --git a/third_party/WebKit/Source/web/WebRemoteFrameImpl.h b/third_party/WebKit/Source/web/WebRemoteFrameImpl.h
index 5da4280b..692f7fb 100644
--- a/third_party/WebKit/Source/web/WebRemoteFrameImpl.h
+++ b/third_party/WebKit/Source/web/WebRemoteFrameImpl.h
@@ -35,7 +35,6 @@
 
   // WebFrame methods:
   void close() override;
-  WebString uniqueName() const override;
   WebString assignedName() const override;
   void setName(const WebString&) override;
   WebVector<WebIconURL> iconURLs(int iconTypesMask) const override;
@@ -112,8 +111,7 @@
   // WebFrameImplBase methods:
   void initializeCoreFrame(FrameHost*,
                            FrameOwner*,
-                           const AtomicString& name,
-                           const AtomicString& uniqueName) override;
+                           const AtomicString& name) override;
   RemoteFrame* frame() const override { return m_frame.get(); }
 
   void setCoreFrame(RemoteFrame*);
@@ -125,7 +123,6 @@
   // WebRemoteFrame methods:
   WebLocalFrame* createLocalChild(WebTreeScopeType,
                                   const WebString& name,
-                                  const WebString& uniqueName,
                                   WebSandboxFlags,
                                   WebFrameClient*,
                                   blink::InterfaceProvider*,
@@ -135,26 +132,23 @@
                                   WebFrame* opener) override;
   WebRemoteFrame* createRemoteChild(WebTreeScopeType,
                                     const WebString& name,
-                                    const WebString& uniqueName,
                                     WebSandboxFlags,
                                     WebRemoteFrameClient*,
                                     WebFrame* opener) override;
   void setWebLayer(WebLayer*) override;
-  void setReplicatedOrigin(const WebSecurityOrigin&) const override;
-  void setReplicatedSandboxFlags(WebSandboxFlags) const override;
-  void setReplicatedName(const WebString& name,
-                         const WebString& uniqueName) const override;
+  void setReplicatedOrigin(const WebSecurityOrigin&) override;
+  void setReplicatedSandboxFlags(WebSandboxFlags) override;
+  void setReplicatedName(const WebString&) override;
   void setReplicatedFeaturePolicyHeader(
-      const WebParsedFeaturePolicy& parsedHeader) const override;
+      const WebParsedFeaturePolicy& parsedHeader) override;
   void addReplicatedContentSecurityPolicyHeader(
       const WebString& headerValue,
       WebContentSecurityPolicyType,
-      WebContentSecurityPolicySource) const override;
-  void resetReplicatedContentSecurityPolicy() const override;
-  void setReplicatedInsecureRequestPolicy(
-      WebInsecureRequestPolicy) const override;
-  void setReplicatedPotentiallyTrustworthyUniqueOrigin(bool) const override;
-  void dispatchLoadEventOnFrameOwner() const override;
+      WebContentSecurityPolicySource) override;
+  void resetReplicatedContentSecurityPolicy() override;
+  void setReplicatedInsecureRequestPolicy(WebInsecureRequestPolicy) override;
+  void setReplicatedPotentiallyTrustworthyUniqueOrigin(bool) override;
+  void dispatchLoadEventOnFrameOwner() override;
   void didStartLoading() override;
   void didStopLoading() override;
   bool isIgnoredForHitTest() const override;
diff --git a/third_party/WebKit/Source/web/WebViewImpl.cpp b/third_party/WebKit/Source/web/WebViewImpl.cpp
index e960c7c..b2f5196 100644
--- a/third_party/WebKit/Source/web/WebViewImpl.cpp
+++ b/third_party/WebKit/Source/web/WebViewImpl.cpp
@@ -309,8 +309,7 @@
 }
 
 void WebViewImpl::setMainFrame(WebFrame* frame) {
-  frame->toImplBase()->initializeCoreFrame(&page()->frameHost(), 0, nullAtom,
-                                           nullAtom);
+  frame->toImplBase()->initializeCoreFrame(&page()->frameHost(), 0, nullAtom);
 }
 
 void WebViewImpl::setCredentialManagerClient(
diff --git a/third_party/WebKit/Source/web/tests/FrameTestHelpers.cpp b/third_party/WebKit/Source/web/tests/FrameTestHelpers.cpp
index bf5f5d24..731b037a 100644
--- a/third_party/WebKit/Source/web/tests/FrameTestHelpers.cpp
+++ b/third_party/WebKit/Source/web/tests/FrameTestHelpers.cpp
@@ -100,18 +100,6 @@
   return &client;
 }
 
-// |uniqueName| is normally calculated in a somewhat complicated way by the
-// FrameTree class, but for test purposes the approximation below should be
-// close enough.
-String nameToUniqueName(const String& name) {
-  static int uniqueNameCounter = 0;
-  StringBuilder uniqueName;
-  uniqueName.append(name);
-  uniqueName.append(' ');
-  uniqueName.appendNumber(uniqueNameCounter++);
-  return uniqueName.toString();
-}
-
 }  // namespace
 
 void loadFrame(WebFrame* frame, const std::string& url) {
@@ -176,8 +164,7 @@
     client = defaultWebFrameClient();
 
   WebLocalFrameImpl* frame = toWebLocalFrameImpl(parent->createLocalChild(
-      WebTreeScopeType::Document, name, nameToUniqueName(name),
-      WebSandboxFlags::None, client,
+      WebTreeScopeType::Document, name, WebSandboxFlags::None, client,
       static_cast<TestWebFrameClient*>(client)->interfaceProvider(), nullptr,
       previousSibling, properties, nullptr));
 
@@ -191,9 +178,9 @@
 WebRemoteFrameImpl* createRemoteChild(WebRemoteFrame* parent,
                                       WebRemoteFrameClient* client,
                                       const WebString& name) {
-  return toWebRemoteFrameImpl(parent->createRemoteChild(
-      WebTreeScopeType::Document, name, nameToUniqueName(name),
-      WebSandboxFlags::None, client, nullptr));
+  return toWebRemoteFrameImpl(
+      parent->createRemoteChild(WebTreeScopeType::Document, name,
+                                WebSandboxFlags::None, client, nullptr));
 }
 
 WebViewHelper::WebViewHelper(SettingOverrider* settingOverrider)
@@ -316,7 +303,7 @@
     WebLocalFrame* parent,
     WebTreeScopeType scope,
     const WebString& name,
-    const WebString& uniqueName,
+    const WebString& fallbackName,
     WebSandboxFlags sandboxFlags,
     const WebFrameOwnerProperties& frameOwnerProperties) {
   WebLocalFrame* frame =
diff --git a/third_party/WebKit/Source/web/tests/FrameTestHelpers.h b/third_party/WebKit/Source/web/tests/FrameTestHelpers.h
index d3fb7f7a..232875b1 100644
--- a/third_party/WebKit/Source/web/tests/FrameTestHelpers.h
+++ b/third_party/WebKit/Source/web/tests/FrameTestHelpers.h
@@ -245,7 +245,7 @@
   WebLocalFrame* createChildFrame(WebLocalFrame* parent,
                                   WebTreeScopeType,
                                   const WebString& name,
-                                  const WebString& uniqueName,
+                                  const WebString& fallbackName,
                                   WebSandboxFlags,
                                   const WebFrameOwnerProperties&) override;
   void didStartLoading(bool) override;
diff --git a/third_party/WebKit/Source/web/tests/WebFrameTest.cpp b/third_party/WebKit/Source/web/tests/WebFrameTest.cpp
index c806ccf5..dcaacbc 100644
--- a/third_party/WebKit/Source/web/tests/WebFrameTest.cpp
+++ b/third_party/WebKit/Source/web/tests/WebFrameTest.cpp
@@ -7087,13 +7087,13 @@
   int willSendRequestCallCount() const { return m_willSendRequestCallCount; }
   int childFrameCreationCount() const { return m_childFrameCreationCount; }
 
-  virtual WebLocalFrame* createChildFrame(
+  WebLocalFrame* createChildFrame(
       WebLocalFrame* parent,
       WebTreeScopeType scope,
       const WebString&,
       const WebString&,
       WebSandboxFlags,
-      const WebFrameOwnerProperties& frameOwnerProperties) {
+      const WebFrameOwnerProperties& frameOwnerProperties) override {
     DCHECK(m_childClient);
     m_childFrameCreationCount++;
     WebLocalFrame* frame =
@@ -7498,8 +7498,8 @@
   WebLocalFrame* createChildFrame(
       WebLocalFrame* parent,
       WebTreeScopeType scope,
-      const WebString& frameName,
-      const WebString& frameUniqueName,
+      const WebString& name,
+      const WebString& fallbackName,
       WebSandboxFlags sandboxFlags,
       const WebFrameOwnerProperties& frameOwnerProperties) override {
     ++m_callCount;
@@ -9081,72 +9081,6 @@
   remoteFrame->close();
 }
 
-// The uniqueName should be preserved when swapping to a RemoteFrame and back,
-// whether the frame has a name or not.
-TEST_F(WebFrameSwapTest, UniqueNameAfterRemoteToLocalSwap) {
-  // Start with a named frame.
-  WebFrame* targetFrame = mainFrame()->firstChild();
-  ASSERT_TRUE(targetFrame);
-  WebString uniqueName = targetFrame->uniqueName();
-  EXPECT_EQ("frame1", uniqueName.utf8());
-
-  // Swap to a RemoteFrame.
-  FrameTestHelpers::TestWebRemoteFrameClient remoteFrameClient;
-  WebRemoteFrameImpl* remoteFrame = WebRemoteFrameImpl::create(
-      WebTreeScopeType::Document, &remoteFrameClient);
-  targetFrame->swap(remoteFrame);
-  ASSERT_TRUE(mainFrame()->firstChild());
-  ASSERT_EQ(mainFrame()->firstChild(), remoteFrame);
-  EXPECT_EQ(uniqueName.utf8(),
-            WebString(remoteFrame->frame()->tree().uniqueName()).utf8());
-
-  // Swap back to a LocalFrame.
-  RemoteToLocalSwapWebFrameClient client(remoteFrame);
-  WebLocalFrame* localFrame = WebLocalFrame::createProvisional(
-      &client, nullptr, nullptr, remoteFrame, WebSandboxFlags::None);
-  FrameTestHelpers::loadFrame(localFrame, m_baseURL + "subframe-hello.html");
-  EXPECT_EQ(uniqueName.utf8(), localFrame->uniqueName().utf8());
-  EXPECT_EQ(uniqueName.utf8(), WebString(toWebLocalFrameImpl(localFrame)
-                                             ->frame()
-                                             ->loader()
-                                             .currentItem()
-                                             ->target())
-                                   .utf8());
-
-  // Repeat with no name on the frame.
-  // (note that uniqueName is immutable after first real commit).
-  localFrame->setName("");
-  WebString uniqueName2 = localFrame->uniqueName();
-  EXPECT_EQ("frame1", uniqueName2.utf8());
-
-  FrameTestHelpers::TestWebRemoteFrameClient remoteFrameClient2;
-  WebRemoteFrameImpl* remoteFrame2 = WebRemoteFrameImpl::create(
-      WebTreeScopeType::Document, &remoteFrameClient2);
-  localFrame->swap(remoteFrame2);
-  ASSERT_TRUE(mainFrame()->firstChild());
-  ASSERT_EQ(mainFrame()->firstChild(), remoteFrame2);
-  EXPECT_EQ(uniqueName2.utf8(),
-            WebString(remoteFrame2->frame()->tree().uniqueName()).utf8());
-
-  RemoteToLocalSwapWebFrameClient client2(remoteFrame2);
-  WebLocalFrame* localFrame2 = WebLocalFrame::createProvisional(
-      &client2, nullptr, nullptr, remoteFrame2, WebSandboxFlags::None);
-  FrameTestHelpers::loadFrame(localFrame2, m_baseURL + "subframe-hello.html");
-  EXPECT_EQ(uniqueName2.utf8(), localFrame2->uniqueName().utf8());
-  EXPECT_EQ(uniqueName2.utf8(), WebString(toWebLocalFrameImpl(localFrame2)
-                                              ->frame()
-                                              ->loader()
-                                              .currentItem()
-                                              ->target())
-                                    .utf8());
-
-  // Manually reset to break WebViewHelper's dependency on the stack allocated
-  // TestWebFrameClient.
-  reset();
-  remoteFrame->close();
-  remoteFrame2->close();
-}
-
 class RemoteNavigationClient
     : public FrameTestHelpers::TestWebRemoteFrameClient {
  public:
@@ -11302,20 +11236,6 @@
   mockOverlayTheme.setOverlayScrollbarFadeOutDelay(0.0);
 }
 
-TEST_F(WebFrameTest, UniqueNames) {
-  registerMockedHttpURLLoad("frameset-repeated-name.html");
-  registerMockedHttpURLLoad("frameset-dest.html");
-  FrameTestHelpers::WebViewHelper webViewHelper;
-  webViewHelper.initializeAndLoad(m_baseURL + "frameset-repeated-name.html");
-  Frame* mainFrame = webViewHelper.webView()->mainFrameImpl()->frame();
-  HashSet<AtomicString> names;
-  for (Frame* frame = mainFrame->tree().firstChild(); frame;
-       frame = frame->tree().traverseNext()) {
-    EXPECT_TRUE(names.insert(frame->tree().uniqueName()).isNewEntry);
-  }
-  EXPECT_EQ(10u, names.size());
-}
-
 TEST_F(WebFrameTest, NoLoadingCompletionCallbacksInDetach) {
   class LoadingObserverFrameClient
       : public FrameTestHelpers::TestWebFrameClient {
@@ -11374,7 +11294,7 @@
     WebLocalFrame* createChildFrame(WebLocalFrame* parent,
                                     WebTreeScopeType scope,
                                     const WebString& name,
-                                    const WebString& uniqueName,
+                                    const WebString& fallbackName,
                                     WebSandboxFlags sandboxFlags,
                                     const WebFrameOwnerProperties&) override {
       WebLocalFrame* frame =
diff --git a/third_party/WebKit/Source/web/tests/WebViewTest.cpp b/third_party/WebKit/Source/web/tests/WebViewTest.cpp
index 9e68ace2..72607ea9 100644
--- a/third_party/WebKit/Source/web/tests/WebViewTest.cpp
+++ b/third_party/WebKit/Source/web/tests/WebViewTest.cpp
@@ -3219,7 +3219,7 @@
   WebLocalFrame* createChildFrame(WebLocalFrame* parent,
                                   WebTreeScopeType,
                                   const WebString& name,
-                                  const WebString& uniqueName,
+                                  const WebString& fallbackName,
                                   WebSandboxFlags,
                                   const WebFrameOwnerProperties&) override;
 
@@ -3233,12 +3233,12 @@
     WebLocalFrame* parent,
     WebTreeScopeType scope,
     const WebString& name,
-    const WebString& uniqueName,
+    const WebString& fallbackName,
     WebSandboxFlags sandboxFlags,
     const WebFrameOwnerProperties& frameOwnerProperties) {
   ++m_count;
   return TestWebFrameClient::createChildFrame(
-      parent, scope, name, uniqueName, sandboxFlags, frameOwnerProperties);
+      parent, scope, name, fallbackName, sandboxFlags, frameOwnerProperties);
 }
 
 TEST_P(WebViewTest, ChangeDisplayMode) {
diff --git a/third_party/WebKit/Source/web/tests/data/frameset-repeated-name.html b/third_party/WebKit/Source/web/tests/data/frameset-repeated-name.html
deleted file mode 100644
index 6626687..0000000
--- a/third_party/WebKit/Source/web/tests/data/frameset-repeated-name.html
+++ /dev/null
@@ -1,4 +0,0 @@
-<frameset>
-<frame src="frameset-dest.html">
-<frame src="frameset-dest.html">
-</frameset>
diff --git a/third_party/WebKit/public/web/WebFrame.h b/third_party/WebKit/public/web/WebFrame.h
index 855ed081..1be0587 100644
--- a/third_party/WebKit/public/web/WebFrame.h
+++ b/third_party/WebKit/public/web/WebFrame.h
@@ -120,9 +120,6 @@
 
   // Basic properties ---------------------------------------------------
 
-  // The unique name of this frame.
-  virtual WebString uniqueName() const = 0;
-
   // The name of this frame. If no name is given, empty string is returned.
   virtual WebString assignedName() const = 0;
 
diff --git a/third_party/WebKit/public/web/WebFrameClient.h b/third_party/WebKit/public/web/WebFrameClient.h
index 939af5a..5d712ad 100644
--- a/third_party/WebKit/public/web/WebFrameClient.h
+++ b/third_party/WebKit/public/web/WebFrameClient.h
@@ -182,7 +182,7 @@
   virtual WebLocalFrame* createChildFrame(WebLocalFrame* parent,
                                           WebTreeScopeType,
                                           const WebString& name,
-                                          const WebString& uniqueName,
+                                          const WebString& fallbackName,
                                           WebSandboxFlags sandboxFlags,
                                           const WebFrameOwnerProperties&) {
     return nullptr;
@@ -208,8 +208,7 @@
   virtual void willCommitProvisionalLoad() {}
 
   // This frame's name has changed.
-  virtual void didChangeName(const WebString& name,
-                             const WebString& uniqueName) {}
+  virtual void didChangeName(const WebString& name) {}
 
   // This frame has set an insecure request policy.
   virtual void didEnforceInsecureRequestPolicy(WebInsecureRequestPolicy) {}
diff --git a/third_party/WebKit/public/web/WebHistoryItem.h b/third_party/WebKit/public/web/WebHistoryItem.h
index 39e5340..109c3c1 100644
--- a/third_party/WebKit/public/web/WebHistoryItem.h
+++ b/third_party/WebKit/public/web/WebHistoryItem.h
@@ -35,6 +35,7 @@
 #include "../platform/WebHistoryScrollRestorationType.h"
 #include "../platform/WebPrivatePtr.h"
 #include "../platform/WebReferrerPolicy.h"
+#include "public/platform/WebString.h"
 
 namespace blink {
 
@@ -81,7 +82,7 @@
   BLINK_EXPORT WebReferrerPolicy getReferrerPolicy() const;
   BLINK_EXPORT void setReferrer(const WebString&, WebReferrerPolicy);
 
-  BLINK_EXPORT WebString target() const;
+  BLINK_EXPORT const WebString& target() const;
   BLINK_EXPORT void setTarget(const WebString&);
 
   BLINK_EXPORT WebFloatPoint visualViewportScrollOffset() const;
@@ -127,6 +128,8 @@
 
  private:
   WebPrivatePtr<HistoryItem> m_private;
+  // TODO(dcheng): Remove this, since unique name is no longer a Blink concept.
+  WebString m_target;
 };
 
 }  // namespace blink
diff --git a/third_party/WebKit/public/web/WebRemoteFrame.h b/third_party/WebKit/public/web/WebRemoteFrame.h
index ef5d082..315bf35 100644
--- a/third_party/WebKit/public/web/WebRemoteFrame.h
+++ b/third_party/WebKit/public/web/WebRemoteFrame.h
@@ -35,7 +35,6 @@
   // children.
   virtual WebLocalFrame* createLocalChild(WebTreeScopeType,
                                           const WebString& name,
-                                          const WebString& uniqueName,
                                           WebSandboxFlags,
                                           WebFrameClient*,
                                           blink::InterfaceProvider*,
@@ -46,7 +45,6 @@
 
   virtual WebRemoteFrame* createRemoteChild(WebTreeScopeType,
                                             const WebString& name,
-                                            const WebString& uniqueName,
                                             WebSandboxFlags,
                                             WebRemoteFrameClient*,
                                             WebFrame* opener) = 0;
@@ -55,37 +53,35 @@
   virtual void setWebLayer(WebLayer*) = 0;
 
   // Set security origin replicated from another process.
-  virtual void setReplicatedOrigin(const WebSecurityOrigin&) const = 0;
+  virtual void setReplicatedOrigin(const WebSecurityOrigin&) = 0;
 
   // Set sandbox flags replicated from another process.
-  virtual void setReplicatedSandboxFlags(WebSandboxFlags) const = 0;
+  virtual void setReplicatedSandboxFlags(WebSandboxFlags) = 0;
 
-  // Set frame |name| and |uniqueName| replicated from another process.
-  virtual void setReplicatedName(const WebString& name,
-                                 const WebString& uniqueName) const = 0;
+  // Set frame |name| replicated from another process.
+  virtual void setReplicatedName(const WebString&) = 0;
 
   virtual void setReplicatedFeaturePolicyHeader(
-      const WebParsedFeaturePolicy& parsedHeader) const = 0;
+      const WebParsedFeaturePolicy& parsedHeader) = 0;
 
   // Adds |header| to the set of replicated CSP headers.
   virtual void addReplicatedContentSecurityPolicyHeader(
       const WebString& headerValue,
       WebContentSecurityPolicyType,
-      WebContentSecurityPolicySource) const = 0;
+      WebContentSecurityPolicySource) = 0;
 
   // Resets replicated CSP headers to an empty set.
-  virtual void resetReplicatedContentSecurityPolicy() const = 0;
+  virtual void resetReplicatedContentSecurityPolicy() = 0;
 
   // Set frame enforcement of insecure request policy replicated from another
   // process.
-  virtual void setReplicatedInsecureRequestPolicy(
-      WebInsecureRequestPolicy) const = 0;
+  virtual void setReplicatedInsecureRequestPolicy(WebInsecureRequestPolicy) = 0;
 
   // Set the frame to a unique origin that is potentially trustworthy,
   // replicated from another process.
-  virtual void setReplicatedPotentiallyTrustworthyUniqueOrigin(bool) const = 0;
+  virtual void setReplicatedPotentiallyTrustworthyUniqueOrigin(bool) = 0;
 
-  virtual void dispatchLoadEventOnFrameOwner() const = 0;
+  virtual void dispatchLoadEventOnFrameOwner() = 0;
 
   virtual void didStartLoading() = 0;
   virtual void didStopLoading() = 0;
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index ca14407d..f5f8b16d4 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -81356,6 +81356,7 @@
   <int value="26" label="Site eligible, but banner creation failed"/>
   <int value="27" label="Url is not eligible for WebAPKs"/>
   <int value="28" label="Site loaded in incognito window (not reported)"/>
+  <int value="29" label="Service worker not offline capable"/>
 </enum>
 
 <enum name="AppBannersInstallEvent" type="int">
diff --git a/ui/aura/mus/window_tree_client.cc b/ui/aura/mus/window_tree_client.cc
index 8db09d36..6cd345db 100644
--- a/ui/aura/mus/window_tree_client.cc
+++ b/ui/aura/mus/window_tree_client.cc
@@ -482,12 +482,6 @@
 void WindowTreeClient::SetWindowTree(ui::mojom::WindowTreePtr window_tree_ptr) {
   tree_ptr_ = std::move(window_tree_ptr);
 
-  // Enable nested dispatch on both sides of this connect because these objects
-  // are used in the presence of nested runloops, such as those during drag and
-  // drop.
-  binding_.EnableNestedDispatch(true);
-  tree_ptr_.EnableNestedDispatch(true);
-
   WindowTreeConnectionEstablished(tree_ptr_.get());
   tree_ptr_->GetCursorLocationMemory(
       base::Bind(&WindowTreeClient::OnReceivedCursorLocationMemory,
diff --git a/ui/ozone/platform/drm/common/drm_util.cc b/ui/ozone/platform/drm/common/drm_util.cc
index c781e32..59944118 100644
--- a/ui/ozone/platform/drm/common/drm_util.cc
+++ b/ui/ozone/platform/drm/common/drm_util.cc
@@ -431,6 +431,10 @@
       return DRM_FORMAT_RGB565;
     case gfx::BufferFormat::UYVY_422:
       return DRM_FORMAT_UYVY;
+    case gfx::BufferFormat::YUV_420_BIPLANAR:
+      return DRM_FORMAT_NV12;
+    case gfx::BufferFormat::YVU_420:
+      return DRM_FORMAT_YVU420;
     default:
       NOTREACHED();
       return 0;
diff --git a/ui/ozone/platform/drm/gpu/gbm_buffer.cc b/ui/ozone/platform/drm/gpu/gbm_buffer.cc
index 5d46434..dff2d96 100644
--- a/ui/ozone/platform/drm/gpu/gbm_buffer.cc
+++ b/ui/ozone/platform/drm/gpu/gbm_buffer.cc
@@ -184,23 +184,27 @@
   DCHECK_EQ(planes[0].offset, 0);
 
   // Try to use scanout if supported.
-  bool try_scanout =
-      gbm_device_is_format_supported(
-          gbm->device(), format, GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING) &&
-      (planes.size() == 1);
+  bool try_scanout = gbm_device_is_format_supported(
+      gbm->device(), format, GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING);
 
   gbm_bo* bo = nullptr;
   if (try_scanout) {
-    struct gbm_import_fd_data fd_data;
-    fd_data.fd = fds[0].get();
+    struct gbm_import_fd_planar_data fd_data;
     fd_data.width = size.width();
     fd_data.height = size.height();
-    fd_data.stride = planes[0].stride;
     fd_data.format = format;
 
+    DCHECK_LE(planes.size(), 3u);
+    for (size_t i = 0; i < planes.size(); ++i) {
+      fd_data.fds[i] = fds[i < fds.size() ? i : 0].get();
+      fd_data.strides[i] = planes[i].stride;
+      fd_data.offsets[i] = planes[i].offset;
+      fd_data.format_modifiers[i] = planes[i].modifier;
+    }
+
     // The fd passed to gbm_bo_import is not ref-counted and need to be
     // kept open for the lifetime of the buffer.
-    bo = gbm_bo_import(gbm->device(), GBM_BO_IMPORT_FD, &fd_data,
+    bo = gbm_bo_import(gbm->device(), GBM_BO_IMPORT_FD_PLANAR, &fd_data,
                        GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING);
     if (!bo) {
       LOG(ERROR) << "nullptr returned from gbm_bo_import";
diff --git a/ui/ozone/platform/drm/gpu/gbm_buffer_base.cc b/ui/ozone/platform/drm/gpu/gbm_buffer_base.cc
index d36ec22..3342791f 100644
--- a/ui/ozone/platform/drm/gpu/gbm_buffer_base.cc
+++ b/ui/ozone/platform/drm/gpu/gbm_buffer_base.cc
@@ -25,14 +25,17 @@
     opaque_framebuffer_pixel_format_ = GetFourCCFormatForOpaqueFramebuffer(
         GetBufferFormatFromFourCCFormat(format));
 
-    // TODO(dcastagna): Add multi-planar support.
     uint32_t handles[4] = {0};
-    handles[0] = gbm_bo_get_handle(bo).u32;
     uint32_t strides[4] = {0};
-    strides[0] = gbm_bo_get_stride(bo);
     uint32_t offsets[4] = {0};
     uint64_t modifiers[4] = {0};
-    modifiers[0] = modifier;
+
+    for (size_t i = 0; i < gbm_bo_get_num_planes(bo); ++i) {
+      handles[i] = gbm_bo_get_plane_handle(bo, i).u32;
+      strides[i] = gbm_bo_get_plane_stride(bo, i);
+      offsets[i] = gbm_bo_get_plane_offset(bo, i);
+      modifiers[i] = modifier;
+    }
 
     // AddFramebuffer2 only considers the modifiers if addfb_flags has
     // DRM_MODE_FB_MODIFIERS set. We only set that when we've created
diff --git a/ui/views/mus/mus_client.cc b/ui/views/mus/mus_client.cc
index 6a0a0d1..055f695 100644
--- a/ui/views/mus/mus_client.cc
+++ b/ui/views/mus/mus_client.cc
@@ -86,10 +86,8 @@
   if (create_wm_state)
     wm_state_ = base::MakeUnique<wm::WMState>();
 
-  if (testing_state == MusClientTestingState::CREATE_TESTING_STATE) {
+  if (testing_state == MusClientTestingState::CREATE_TESTING_STATE)
     connector->BindInterface(ui::mojom::kServiceName, &server_test_ptr_);
-    server_test_ptr_.EnableNestedDispatch(true);
-  }
 
   window_tree_client_ = base::MakeUnique<aura::WindowTreeClient>(
       connector, this, nullptr /* window_manager_delegate */,