diff --git a/DEPS b/DEPS
index 251aada..18755829 100644
--- a/DEPS
+++ b/DEPS
@@ -44,7 +44,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling V8
   # and whatever else without interference from each other.
-  'v8_revision': '0b8651ca9ed0a692c111bf08ede1d3347e8c6a72',
+  'v8_revision': '952f96092a17a55f65cfb5d45979a14ad67cdf0a',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling swarming_client
   # and whatever else without interference from each other.
diff --git a/ash/common/shelf/shelf_model.cc b/ash/common/shelf/shelf_model.cc
index acc2c10..640674ee 100644
--- a/ash/common/shelf/shelf_model.cc
+++ b/ash/common/shelf/shelf_model.cc
@@ -70,7 +70,10 @@
   DCHECK(index >= 0 && index < item_count());
   ShelfItem old_item(items_[index]);
   items_.erase(items_.begin() + index);
-  id_to_item_delegate_map_.erase(old_item.id);
+  RemoveShelfItemDelegate(old_item.id);
+  // TODO(jamescook): Fold this into ShelfItemRemoved in existing observers.
+  for (auto& observer : observers_)
+    observer.OnSetShelfItemDelegate(old_item.id, nullptr);
   for (auto& observer : observers_)
     observer.ShelfItemRemoved(index, old_item);
 }
@@ -157,9 +160,14 @@
 void ShelfModel::SetShelfItemDelegate(
     ShelfID id,
     std::unique_ptr<ShelfItemDelegate> item_delegate) {
-  if (item_delegate)
-    item_delegate->set_shelf_id(id);
-  // This assignment replaces any ShelfItemDelegate already registered for |id|.
+  // If another ShelfItemDelegate is already registered for |id|, we assume
+  // that this request is replacing ShelfItemDelegate for |id| with
+  // |item_delegate|.
+  RemoveShelfItemDelegate(id);
+
+  for (auto& observer : observers_)
+    observer.OnSetShelfItemDelegate(id, item_delegate.get());
+
   id_to_item_delegate_map_[id] = std::move(item_delegate);
 }
 
@@ -195,4 +203,9 @@
   return index;
 }
 
+void ShelfModel::RemoveShelfItemDelegate(ShelfID id) {
+  if (id_to_item_delegate_map_.find(id) != id_to_item_delegate_map_.end())
+    id_to_item_delegate_map_.erase(id);
+}
+
 }  // namespace ash
diff --git a/ash/common/shelf/shelf_model_observer.h b/ash/common/shelf/shelf_model_observer.h
index 9953513..194e202 100644
--- a/ash/common/shelf/shelf_model_observer.h
+++ b/ash/common/shelf/shelf_model_observer.h
@@ -11,6 +11,7 @@
 namespace ash {
 
 struct ShelfItem;
+class ShelfItemDelegate;
 
 class ASH_EXPORT ShelfModelObserver {
  public:
@@ -25,9 +26,17 @@
   // of the arguments.
   virtual void ShelfItemMoved(int start_index, int target_index) = 0;
 
-  // Invoked after an item changes. |old_item| is the item before the change.
+  // Invoked when the state of an item changes. |old_item| is the item
+  // before the change.
   virtual void ShelfItemChanged(int index, const ShelfItem& old_item) = 0;
 
+  // Gets called when a ShelfItemDelegate gets changed. Note that
+  // |item_delegate| can be null.
+  // NOTE: This is added a temporary fix for M39 to fix crbug.com/429870.
+  // TODO(skuhne): Find the real reason for this problem and remove this fix.
+  virtual void OnSetShelfItemDelegate(ShelfID id,
+                                      ShelfItemDelegate* item_delegate) = 0;
+
  protected:
   virtual ~ShelfModelObserver() {}
 };
diff --git a/ash/common/shelf/shelf_model_unittest.cc b/ash/common/shelf/shelf_model_unittest.cc
index bbd69c7b..2f46faee 100644
--- a/ash/common/shelf/shelf_model_unittest.cc
+++ b/ash/common/shelf/shelf_model_unittest.cc
@@ -37,6 +37,7 @@
   void ShelfItemRemoved(int, const ShelfItem&) override { removed_count_++; }
   void ShelfItemChanged(int, const ShelfItem&) override { changed_count_++; }
   void ShelfItemMoved(int, int) override { moved_count_++; }
+  void OnSetShelfItemDelegate(ShelfID, ShelfItemDelegate*) override {}
 
  private:
   void AddToResult(const std::string& format, int count, std::string* result) {
diff --git a/ash/common/shelf/shelf_view.cc b/ash/common/shelf/shelf_view.cc
index 0256cbd..29cb970 100644
--- a/ash/common/shelf/shelf_view.cc
+++ b/ash/common/shelf/shelf_view.cc
@@ -1591,6 +1591,8 @@
     AnimateToIdealBounds();
 }
 
+void ShelfView::OnSetShelfItemDelegate(ShelfID, ShelfItemDelegate*) {}
+
 void ShelfView::AfterItemSelected(
     const ShelfItem& item,
     views::Button* sender,
diff --git a/ash/common/shelf/shelf_view.h b/ash/common/shelf/shelf_view.h
index ef004a4..6115d39 100644
--- a/ash/common/shelf/shelf_view.h
+++ b/ash/common/shelf/shelf_view.h
@@ -289,6 +289,7 @@
   void ShelfItemRemoved(int model_index, const ShelfItem& old_item) override;
   void ShelfItemChanged(int model_index, const ShelfItem& old_item) override;
   void ShelfItemMoved(int start_index, int target_index) override;
+  void OnSetShelfItemDelegate(ShelfID, ShelfItemDelegate*) override;
 
   // Handles the result of an item selection, records the |action| taken and
   // optionally shows an application menu with the given |menu_items|.
diff --git a/chrome/VERSION b/chrome/VERSION
index 18ecf8d3..90d48746 100644
--- a/chrome/VERSION
+++ b/chrome/VERSION
@@ -1,4 +1,4 @@
 MAJOR=59
 MINOR=0
-BUILD=3066
+BUILD=3067
 PATCH=0
diff --git a/chrome/browser/ui/ash/launcher/arc_app_deferred_launcher_controller.cc b/chrome/browser/ui/ash/launcher/arc_app_deferred_launcher_controller.cc
index a956eae6..9b78269 100644
--- a/chrome/browser/ui/ash/launcher/arc_app_deferred_launcher_controller.cc
+++ b/chrome/browser/ui/ash/launcher/arc_app_deferred_launcher_controller.cc
@@ -4,8 +4,6 @@
 
 #include "chrome/browser/ui/ash/launcher/arc_app_deferred_launcher_controller.h"
 
-#include "ash/common/shelf/shelf_model.h"
-#include "ash/shell.h"
 #include "base/memory/ptr_util.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "chrome/browser/chromeos/arc/arc_util.h"
@@ -113,9 +111,8 @@
     return;
 
   const ash::ShelfID shelf_id = owner_->GetShelfIDForAppID(shelf_app_id);
-  ash::ShelfModel* shelf_model = ash::Shell::Get()->shelf_model();
   const bool need_close_item =
-      it->second == shelf_model->GetShelfItemDelegate(shelf_id);
+      it->second == owner_->GetShelfItemDelegate(shelf_id);
   app_controller_map_.erase(it);
   if (need_close_item)
     owner_->CloseLauncherItem(shelf_id);
@@ -213,8 +210,7 @@
   if (shelf_id == 0) {
     owner_->CreateAppLauncherItem(std::move(controller), ash::STATUS_RUNNING);
   } else {
-    ash::ShelfModel* shelf_model = ash::Shell::Get()->shelf_model();
-    shelf_model->SetShelfItemDelegate(shelf_id, std::move(controller));
+    owner_->SetShelfItemDelegate(shelf_id, std::move(controller));
     owner_->SetItemStatus(shelf_id, ash::STATUS_RUNNING);
   }
 
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 8798149..b2bf0b83 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,6 @@
 // found in the LICENSE file.
 
 #include "ash/common/shelf/shelf_delegate.h"
-#include "ash/common/shelf/shelf_model.h"
 #include "ash/public/cpp/shelf_item_delegate.h"
 #include "ash/shell.h"
 #include "ash/wm/window_util.h"
@@ -260,7 +259,9 @@
 
   ash::ShelfItemDelegate* GetAppItemController(const std::string& id) {
     const ash::ShelfID shelf_id = shelf_delegate()->GetShelfIDForAppID(id);
-    return ash::Shell::Get()->shelf_model()->GetShelfItemDelegate(shelf_id);
+    if (!shelf_id)
+      return nullptr;
+    return chrome_controller()->GetShelfItemDelegate(shelf_id);
   }
 
   ArcAppListPrefs* app_prefs() { return ArcAppListPrefs::Get(profile()); }
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 ccee5f4..d6e8f2e 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
@@ -6,7 +6,6 @@
 #include <string>
 
 #include "ash/common/shelf/shelf_delegate.h"
-#include "ash/common/shelf/shelf_model.h"
 #include "ash/common/wm/maximize_mode/maximize_mode_controller.h"
 #include "ash/common/wm/window_state.h"
 #include "ash/common/wm_window.h"
@@ -660,8 +659,7 @@
   if (!shelf_id) {
     owner()->CreateAppLauncherItem(std::move(controller), ash::STATUS_RUNNING);
   } else {
-    ash::ShelfModel* shelf_model = ash::Shell::Get()->shelf_model();
-    shelf_model->SetShelfItemDelegate(shelf_id, std::move(controller));
+    owner()->SetShelfItemDelegate(shelf_id, std::move(controller));
     owner()->SetItemStatus(shelf_id, ash::STATUS_RUNNING);
   }
   item_controller->AddTaskId(task_id);
diff --git a/chrome/browser/ui/ash/launcher/chrome_launcher_controller.h b/chrome/browser/ui/ash/launcher/chrome_launcher_controller.h
index d23b354e..5e84748f4 100644
--- a/chrome/browser/ui/ash/launcher/chrome_launcher_controller.h
+++ b/chrome/browser/ui/ash/launcher/chrome_launcher_controller.h
@@ -89,6 +89,11 @@
   // browsers shelf item if needed.
   virtual void SetItemStatus(ash::ShelfID id, ash::ShelfItemStatus status) = 0;
 
+  // Updates the delegate associated with id (which should be a shortcut).
+  virtual void SetShelfItemDelegate(
+      ash::ShelfID id,
+      std::unique_ptr<ash::ShelfItemDelegate> item_delegate) = 0;
+
   // Closes or unpins the shelf item.
   virtual void CloseLauncherItem(ash::ShelfID id) = 0;
 
@@ -197,6 +202,9 @@
   virtual BrowserShortcutLauncherItemController*
   GetBrowserShortcutLauncherItemController() = 0;
 
+  virtual ash::ShelfItemDelegate* GetShelfItemDelegate(
+      const ash::ShelfID id) = 0;
+
   // Check if the shelf visibility (location, visibility) will change with a new
   // user profile or not. However, since the full visibility calculation of the
   // shelf cannot be performed here, this is only a probability used for
diff --git a/chrome/browser/ui/ash/launcher/chrome_launcher_controller_impl.cc b/chrome/browser/ui/ash/launcher/chrome_launcher_controller_impl.cc
index edf788e..e3036fa 100644
--- a/chrome/browser/ui/ash/launcher/chrome_launcher_controller_impl.cc
+++ b/chrome/browser/ui/ash/launcher/chrome_launcher_controller_impl.cc
@@ -90,7 +90,6 @@
 #include "ui/wm/core/window_animations.h"
 
 using extensions::Extension;
-using extension_misc::kChromeAppId;
 using extension_misc::kGmailAppId;
 using content::WebContents;
 
@@ -275,6 +274,14 @@
   model_->RemoveObserver(this);
   if (ash::Shell::HasInstance())
     ash::Shell::Get()->window_tree_host_manager()->RemoveObserver(this);
+  for (const auto& pair : id_to_item_controller_map_) {
+    int index = model_->ItemIndexByID(pair.first);
+    // A "browser proxy" is not known to the model and this removal does
+    // therefore not need to be propagated to the model.
+    if (index != -1 &&
+        model_->items()[index].type != ash::TYPE_BROWSER_SHORTCUT)
+      model_->RemoveItemAt(index);
+  }
 
   // Release all profile dependent resources.
   ReleaseProfile();
@@ -318,15 +325,26 @@
   }
 }
 
+void ChromeLauncherControllerImpl::SetShelfItemDelegate(
+    ash::ShelfID id,
+    std::unique_ptr<ash::ShelfItemDelegate> item_delegate) {
+  CHECK(item_delegate);
+  IDToItemControllerMap::iterator iter = id_to_item_controller_map_.find(id);
+  CHECK(iter != id_to_item_controller_map_.end());
+  item_delegate->set_shelf_id(id);
+  iter->second = item_delegate.get();
+  model_->SetShelfItemDelegate(id, std::move(item_delegate));
+}
+
 void ChromeLauncherControllerImpl::CloseLauncherItem(ash::ShelfID id) {
   CHECK(id);
   if (IsPinned(id)) {
     // Create a new shortcut delegate.
     SetItemStatus(id, ash::STATUS_CLOSED);
-    model_->SetShelfItemDelegate(id, AppShortcutLauncherItemController::Create(
-                                         GetItem(id)->app_launch_id));
+    SetShelfItemDelegate(id, AppShortcutLauncherItemController::Create(
+                                 GetItem(id)->app_launch_id));
   } else {
-    RemoveShelfItem(id);
+    LauncherItemClosed(id);
   }
 }
 
@@ -335,7 +353,7 @@
   if (item && item->status != ash::STATUS_CLOSED)
     UnpinRunningAppInternal(model_->ItemIndexByID(id));
   else
-    RemoveShelfItem(id);
+    LauncherItemClosed(id);
 }
 
 bool ChromeLauncherControllerImpl::IsPinned(ash::ShelfID id) {
@@ -349,7 +367,7 @@
   const ash::ShelfItem* item = GetItem(id);
   if (item) {
     if (!IsPinned(id) && status == ash::STATUS_CLOSED)
-      RemoveShelfItem(id);
+      LauncherItemClosed(id);
     else
       SetItemStatus(id, status);
   } else if (status != ash::STATUS_CLOSED && !app_id.empty()) {
@@ -360,7 +378,7 @@
 }
 
 void ChromeLauncherControllerImpl::Launch(ash::ShelfID id, int event_flags) {
-  ash::ShelfItemDelegate* delegate = model_->GetShelfItemDelegate(id);
+  ash::ShelfItemDelegate* delegate = GetShelfItemDelegate(id);
   if (!delegate)
     return;  // In case invoked from menu and item closed while menu up.
 
@@ -463,13 +481,21 @@
 
 ash::ShelfID ChromeLauncherControllerImpl::GetShelfIDForWebContents(
     content::WebContents* contents) {
+  DCHECK(contents);
+
   std::string app_id = launcher_controller_helper()->GetAppID(contents);
+
   if (app_id.empty() && ContentCanBeHandledByGmailApp(contents))
     app_id = kGmailAppId;
 
   ash::ShelfID id = GetShelfIDForAppID(app_id);
-  // If there is no dedicated app item, use the browser shortcut item.
-  return id == ash::kInvalidShelfID ? GetShelfIDForAppID(kChromeAppId) : id;
+
+  if (app_id.empty() || !id) {
+    int browser_index = model_->GetItemIndexForType(ash::TYPE_BROWSER_SHORTCUT);
+    return model_->items()[browser_index].id;
+  }
+
+  return id;
 }
 
 void ChromeLauncherControllerImpl::SetRefocusURLPatternForTest(
@@ -478,9 +504,9 @@
   const ash::ShelfItem* item = GetItem(id);
   if (item && !IsPlatformApp(id) &&
       (item->type == ash::TYPE_PINNED_APP || item->type == ash::TYPE_APP)) {
-    ash::ShelfItemDelegate* delegate = model_->GetShelfItemDelegate(id);
+    ash::ShelfItemDelegate* item_delegate = GetShelfItemDelegate(id);
     AppShortcutLauncherItemController* item_controller =
-        static_cast<AppShortcutLauncherItemController*>(delegate);
+        static_cast<AppShortcutLauncherItemController*>(item_delegate);
     item_controller->set_refocus_url(url);
   } else {
     NOTREACHED() << "Invalid launcher item or type";
@@ -557,9 +583,9 @@
 
 ash::MenuItemList ChromeLauncherControllerImpl::GetAppMenuItemsForTesting(
     const ash::ShelfItem& item) {
-  ash::ShelfItemDelegate* delegate = model_->GetShelfItemDelegate(item.id);
-  return delegate ? delegate->GetAppMenuItems(ui::EF_NONE)
-                  : ash::MenuItemList();
+  ash::ShelfItemDelegate* item_delegate = GetShelfItemDelegate(item.id);
+  return item_delegate ? item_delegate->GetAppMenuItems(ui::EF_NONE)
+                       : ash::MenuItemList();
 }
 
 std::vector<content::WebContents*>
@@ -569,9 +595,9 @@
   // If there is no such item pinned to the launcher, no menu gets created.
   if (!item || item->type != ash::TYPE_PINNED_APP)
     return std::vector<content::WebContents*>();
-  ash::ShelfItemDelegate* delegate = model_->GetShelfItemDelegate(item->id);
+  ash::ShelfItemDelegate* item_delegate = GetShelfItemDelegate(item->id);
   AppShortcutLauncherItemController* item_controller =
-      static_cast<AppShortcutLauncherItemController*>(delegate);
+      static_cast<AppShortcutLauncherItemController*>(item_delegate);
   return item_controller->GetRunningApplications();
 }
 
@@ -580,9 +606,9 @@
   const ash::ShelfItem* item = GetItem(GetShelfIDForAppID(app_id));
   if (item &&
       (item->type == ash::TYPE_APP || item->type == ash::TYPE_PINNED_APP)) {
-    ash::ShelfItemDelegate* delegate = model_->GetShelfItemDelegate(item->id);
+    ash::ShelfItemDelegate* item_delegate = GetShelfItemDelegate(item->id);
     AppWindowLauncherItemController* item_controller =
-        delegate->AsAppWindowLauncherItemController();
+        item_delegate->AsAppWindowLauncherItemController();
     item_controller->ActivateIndexedApp(window_index);
   }
 }
@@ -646,10 +672,20 @@
 
 BrowserShortcutLauncherItemController*
 ChromeLauncherControllerImpl::GetBrowserShortcutLauncherItemController() {
-  ash::ShelfID id = GetShelfIDForAppID(kChromeAppId);
-  ash::mojom::ShelfItemDelegate* delegate = model_->GetShelfItemDelegate(id);
-  DCHECK(delegate) << "There should be always be a browser shortcut item.";
-  return static_cast<BrowserShortcutLauncherItemController*>(delegate);
+  for (const auto& pair : id_to_item_controller_map_) {
+    const ash::ShelfItem* item = GetItem(pair.first);
+    if (item && item->type == ash::TYPE_BROWSER_SHORTCUT)
+      return static_cast<BrowserShortcutLauncherItemController*>(pair.second);
+  }
+  NOTREACHED()
+      << "There should be always be a BrowserShortcutLauncherItemController.";
+  return nullptr;
+}
+
+ash::ShelfItemDelegate* ChromeLauncherControllerImpl::GetShelfItemDelegate(
+    const ash::ShelfID id) {
+  IDToItemControllerMap::iterator iter = id_to_item_controller_map_.find(id);
+  return iter == id_to_item_controller_map_.end() ? nullptr : iter->second;
 }
 
 bool ChromeLauncherControllerImpl::ShelfBoundsChangesProbablyWithUser(
@@ -690,7 +726,7 @@
 
 const std::string& ChromeLauncherControllerImpl::GetLaunchIDForShelfID(
     ash::ShelfID id) {
-  ash::ShelfItemDelegate* delegate = model_->GetShelfItemDelegate(id);
+  ash::ShelfItemDelegate* delegate = GetShelfItemDelegate(id);
   return delegate ? delegate->launch_id() : base::EmptyString();
 }
 
@@ -762,8 +798,6 @@
   // TODO(khmel): Fix this Arc application id mapping. See http://b/31703859
   const std::string shelf_app_id =
       ArcAppWindowLauncherController::GetShelfAppIdFromArcAppId(app_id);
-  if (shelf_app_id.empty())
-    return ash::kInvalidShelfID;
 
   for (const ash::ShelfItem& item : model_->items()) {
     // Ash's ShelfWindowWatcher handles app panel windows separately.
@@ -937,12 +971,16 @@
   }
 }
 
-void ChromeLauncherControllerImpl::RemoveShelfItem(ash::ShelfID id) {
-  const std::string& app_id = GetAppIDForShelfID(id);
+void ChromeLauncherControllerImpl::LauncherItemClosed(ash::ShelfID id) {
+  IDToItemControllerMap::iterator iter = id_to_item_controller_map_.find(id);
+  CHECK(iter != id_to_item_controller_map_.end());
+  CHECK(iter->second);
+  const std::string& app_id = iter->second->app_id();
   AppIconLoader* app_icon_loader = GetAppIconLoaderForApp(app_id);
   if (app_icon_loader)
     app_icon_loader->ClearImage(app_id);
-  const int index = model_->ItemIndexByID(id);
+  id_to_item_controller_map_.erase(iter);
+  int index = model_->ItemIndexByID(id);
   // A "browser proxy" is not known to the model and this removal does
   // therefore not need to be propagated to the model.
   if (index != -1)
@@ -1062,15 +1100,19 @@
     int app_index = index;
     for (; app_index < model_->item_count(); ++app_index) {
       const ash::ShelfItem& item = model_->items()[app_index];
-      if (item.app_launch_id.app_id() == app_id &&
-          item.app_launch_id.launch_id() == pref_app_launch_id.launch_id()) {
+      const IDToItemControllerMap::iterator it =
+          id_to_item_controller_map_.find(item.id);
+      if (it != id_to_item_controller_map_.end() &&
+          it->second->app_id() == app_id &&
+          it->second->launch_id() == pref_app_launch_id.launch_id()) {
         break;
       }
     }
     if (app_index < model_->item_count()) {
       // Found existing pin or running app.
       const ash::ShelfItem item = model_->items()[app_index];
-      if (ItemTypeIsPinned(item)) {
+      if (item.type == ash::TYPE_PINNED_APP ||
+          item.type == ash::TYPE_BROWSER_SHORTCUT) {
         // Just move to required position or keep it inplace.
         model_->Move(app_index, index);
       } else {
@@ -1079,7 +1121,7 @@
       DCHECK_EQ(model_->ItemIndexByID(item.id), index);
     } else {
       // This is fresh pin. Create new one.
-      DCHECK_NE(app_id, kChromeAppId);
+      DCHECK_NE(app_id, extension_misc::kChromeAppId);
       CreateAppShortcutLauncherItem(pref_app_launch_id, index);
     }
     ++index;
@@ -1163,6 +1205,8 @@
   CHECK(item_delegate);
   // Ash's ShelfWindowWatcher handles app panel windows separately.
   DCHECK_NE(ash::TYPE_APP_PANEL, shelf_item_type);
+  id_to_item_controller_map_[id] = item_delegate.get();
+  item_delegate->set_shelf_id(id);
 
   ash::ShelfItem item;
   item.type = shelf_item_type;
@@ -1200,12 +1244,15 @@
   ResourceBundle& rb = ResourceBundle::GetSharedInstance();
   browser_shortcut.image = *rb.GetImageSkiaNamed(IDR_PRODUCT_LOGO_32);
   browser_shortcut.title = l10n_util::GetStringUTF16(IDS_PRODUCT_NAME);
-  browser_shortcut.app_launch_id = ash::AppLaunchId(kChromeAppId);
+  browser_shortcut.app_launch_id =
+      ash::AppLaunchId(extension_misc::kChromeAppId);
   ash::ShelfID id = model_->next_id();
   model_->AddAt(0, browser_shortcut);
   std::unique_ptr<BrowserShortcutLauncherItemController> item_delegate =
       base::MakeUnique<BrowserShortcutLauncherItemController>(model_);
   BrowserShortcutLauncherItemController* item_controller = item_delegate.get();
+  item_controller->set_shelf_id(id);
+  id_to_item_controller_map_[id] = item_controller;
   model_->SetShelfItemDelegate(id, std::move(item_delegate));
   item_controller->UpdateBrowserItemState();
 }
@@ -1219,8 +1266,11 @@
 }
 
 int ChromeLauncherControllerImpl::FindInsertionPoint() {
+  DCHECK_GT(model_->item_count(), 0);
   for (int i = model_->item_count() - 1; i > 0; --i) {
-    if (ItemTypeIsPinned(model_->items()[i]))
+    ash::ShelfItemType type = model_->items()[i].type;
+    DCHECK_NE(ash::TYPE_APP_LIST, type);
+    if (type == ash::TYPE_PINNED_APP || type == ash::TYPE_BROWSER_SHORTCUT)
       return i;
   }
   return 0;
@@ -1294,6 +1344,11 @@
                                    old_item.app_launch_id.launch_id());
     ash::launcher::RemovePinPosition(profile(), app_launch_id);
   }
+
+  // TODO(skuhne): This fixes crbug.com/429870, but it does not answer why we
+  // get into this state in the first place.
+  if (id_to_item_controller_map_.count(old_item.id) > 0)
+    id_to_item_controller_map_.erase(old_item.id);
 }
 
 void ChromeLauncherControllerImpl::ShelfItemMoved(int start_index,
@@ -1327,6 +1382,18 @@
   }
 }
 
+void ChromeLauncherControllerImpl::OnSetShelfItemDelegate(
+    ash::ShelfID id,
+    ash::ShelfItemDelegate* item_delegate) {
+  // TODO(skuhne): This fixes crbug.com/429870, but it does not answer why we
+  // get into this state in the first place.
+  IDToItemControllerMap::iterator iter = id_to_item_controller_map_.find(id);
+  if (iter == id_to_item_controller_map_.end() || item_delegate == iter->second)
+    return;
+  LOG(ERROR) << "Unexpected change of shelf item delegate, id: " << id;
+  id_to_item_controller_map_.erase(iter);
+}
+
 ///////////////////////////////////////////////////////////////////////////////
 // ash::WindowTreeHostManager::Observer:
 
@@ -1363,20 +1430,19 @@
 // AppIconLoaderDelegate:
 
 void ChromeLauncherControllerImpl::OnAppImageUpdated(
-    const std::string& app_id,
+    const std::string& id,
     const gfx::ImageSkia& image) {
   // TODO: need to get this working for shortcuts.
   for (int index = 0; index < model_->item_count(); ++index) {
     ash::ShelfItem item = model_->items()[index];
-    ash::ShelfItemDelegate* delegate = model_->GetShelfItemDelegate(item.id);
-    if (item.type == ash::TYPE_APP_PANEL || !delegate ||
-        delegate->image_set_by_controller() ||
-        item.app_launch_id.app_id() != app_id) {
+    if (GetAppIDForShelfID(item.id) != id)
       continue;
-    }
+    ash::ShelfItemDelegate* delegate = GetShelfItemDelegate(item.id);
+    if (!delegate || delegate->image_set_by_controller())
+      continue;
     item.image = image;
     if (arc_deferred_launcher_)
-      arc_deferred_launcher_->MaybeApplySpinningEffect(app_id, &item.image);
+      arc_deferred_launcher_->MaybeApplySpinningEffect(id, &item.image);
     model_->Set(index, item);
     // It's possible we're waiting on more than one item, so don't break.
   }
diff --git a/chrome/browser/ui/ash/launcher/chrome_launcher_controller_impl.h b/chrome/browser/ui/ash/launcher/chrome_launcher_controller_impl.h
index 1e3de66..c2e9b3c 100644
--- a/chrome/browser/ui/ash/launcher/chrome_launcher_controller_impl.h
+++ b/chrome/browser/ui/ash/launcher/chrome_launcher_controller_impl.h
@@ -67,6 +67,9 @@
   const ash::ShelfItem* GetItem(ash::ShelfID id) const override;
   void SetItemType(ash::ShelfID id, ash::ShelfItemType type) override;
   void SetItemStatus(ash::ShelfID id, ash::ShelfItemStatus status) override;
+  void SetShelfItemDelegate(
+      ash::ShelfID id,
+      std::unique_ptr<ash::ShelfItemDelegate> item_delegate) override;
   void CloseLauncherItem(ash::ShelfID id) override;
   bool IsPinned(ash::ShelfID id) override;
   void SetV1AppStatus(const std::string& app_id,
@@ -104,6 +107,7 @@
       content::WebContents* web_contents) const override;
   BrowserShortcutLauncherItemController*
   GetBrowserShortcutLauncherItemController() override;
+  ash::ShelfItemDelegate* GetShelfItemDelegate(const ash::ShelfID id) override;
   bool ShelfBoundsChangesProbablyWithUser(
       ash::WmShelf* shelf,
       const AccountId& account_id) const override;
@@ -157,6 +161,7 @@
   friend class TestChromeLauncherControllerImpl;
   FRIEND_TEST_ALL_PREFIXES(ChromeLauncherControllerImplTest, AppPanels);
 
+  typedef std::map<ash::ShelfID, ash::ShelfItemDelegate*> IDToItemControllerMap;
   typedef std::map<content::WebContents*, std::string> WebContentsToAppIDMap;
 
   // Remembers / restores list of running applications.
@@ -166,7 +171,7 @@
   void RestoreUnpinnedRunningApplicationOrder(const std::string& user_id);
 
   // Invoked when the associated browser or app is closed.
-  void RemoveShelfItem(ash::ShelfID id);
+  void LauncherItemClosed(ash::ShelfID id);
 
   // Internal helpers for pinning and unpinning that handle both
   // client-triggered and internal pinning operations.
@@ -232,6 +237,8 @@
   void ShelfItemRemoved(int index, const ash::ShelfItem& old_item) override;
   void ShelfItemMoved(int start_index, int target_index) override;
   void ShelfItemChanged(int index, const ash::ShelfItem& old_item) override;
+  void OnSetShelfItemDelegate(ash::ShelfID id,
+                              ash::ShelfItemDelegate* item_delegate) override;
 
   // ash::WindowTreeHostManager::Observer:
   void OnDisplayConfigurationChanged() override;
@@ -254,6 +261,9 @@
 
   ash::ShelfModel* model_;
 
+  // Controller items in this map are owned by |ShelfModel|.
+  IDToItemControllerMap id_to_item_controller_map_;
+
   // Direct access to app_id for a web contents.
   WebContentsToAppIDMap web_contents_to_app_id_;
 
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 d792aa2..9efd0ce0 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
@@ -254,7 +254,7 @@
   }
 
   ash::ShelfItemDelegate* GetShelfItemDelegate(ash::ShelfID id) {
-    return shelf_model()->GetShelfItemDelegate(id);
+    return controller_->GetShelfItemDelegate(id);
   }
 
   ChromeLauncherControllerImpl* controller_;
@@ -788,7 +788,9 @@
   const ash::ShelfItem& item1 = GetLastLauncherPanelItem();
   EXPECT_EQ(ash::TYPE_APP_PANEL, item1.type);
   EXPECT_EQ(ash::STATUS_RUNNING, item1.status);
-  ash::ShelfItemDelegate* item1_delegate = GetShelfItemDelegate(item1.id);
+  EXPECT_EQ(nullptr, GetShelfItemDelegate(item1.id));
+  ash::ShelfItemDelegate* item1_delegate =
+      shelf_model()->GetShelfItemDelegate(item1.id);
   EXPECT_EQ(ash::TYPE_APP_PANEL,
             panel->GetNativeWindow()->GetProperty(ash::kShelfItemTypeKey));
   // Click the item and confirm that the panel is activated.
@@ -826,7 +828,9 @@
   const ash::ShelfItem& item1 = GetLastLauncherPanelItem();
   EXPECT_EQ(ash::TYPE_APP_PANEL, item1.type);
   EXPECT_EQ(ash::STATUS_RUNNING, item1.status);
-  ash::ShelfItemDelegate* item1_delegate = GetShelfItemDelegate(item1.id);
+  EXPECT_EQ(nullptr, GetShelfItemDelegate(item1.id));
+  ash::ShelfItemDelegate* item1_delegate =
+      shelf_model()->GetShelfItemDelegate(item1.id);
   EXPECT_EQ(ash::TYPE_APP_PANEL,
             panel->GetNativeWindow()->GetProperty(ash::kShelfItemTypeKey));
   // Click the item and confirm that the panel is activated.
@@ -906,6 +910,8 @@
   ASSERT_TRUE(app_custom_icon_item_delegate);
   EXPECT_TRUE(app_custom_icon_item_delegate->image_set_by_controller());
 
+  // Panels are handled by ShelfWindowWatcher, not ChromeLauncherController.
+  EXPECT_EQ(nullptr, GetShelfItemDelegate(panel_item.id));
   // Ensure icon heights are correct (see test.js in app_icon/ test directory)
   EXPECT_EQ(extension_misc::EXTENSION_ICON_SMALL, app_item.image.height());
   EXPECT_EQ(extension_misc::EXTENSION_ICON_LARGE,
@@ -1540,7 +1546,10 @@
   EXPECT_FALSE(panel->GetBaseWindow()->IsActive());
   // Confirm that a shelf item was created and is the correct state.
   const ash::ShelfItem& item = GetLastLauncherPanelItem();
-  ash::ShelfItemDelegate* shelf_item_delegate = GetShelfItemDelegate(item.id);
+  // Panels are handled by ShelfWindowWatcher, not ChromeLauncherController.
+  EXPECT_EQ(nullptr, GetShelfItemDelegate(item.id));
+  ash::ShelfItemDelegate* shelf_item_delegate =
+      shelf_model()->GetShelfItemDelegate(item.id);
   EXPECT_NE(nullptr, shelf_item_delegate);
   EXPECT_EQ(ash::TYPE_APP_PANEL, item.type);
   EXPECT_EQ(ash::STATUS_RUNNING, item.status);
@@ -1765,7 +1774,7 @@
 
   // Now request to either activate an existing app or create a new one.
   ash::ShelfItemDelegate* item_delegate =
-      model_->GetShelfItemDelegate(shortcut_id);
+      controller_->GetShelfItemDelegate(shortcut_id);
   SelectItem(item_delegate, ui::ET_KEY_RELEASED);
 
   // Check that we have set focus on the existing application and nothing new
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 af15a50..cc29bfd 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
@@ -161,6 +161,9 @@
     last_index_ = target_index;
   }
 
+  void OnSetShelfItemDelegate(ash::ShelfID id,
+                              ash::ShelfItemDelegate* item_delegate) override {}
+
   void clear_counts() {
     added_ = 0;
     removed_ = 0;
@@ -713,13 +716,18 @@
     app_service_->ProcessSyncChanges(FROM_HERE, sync_list);
   }
 
-  // Gets the IDs of the currently pinned app items.
-  void GetPinnedAppIds(ChromeLauncherControllerImpl* controller,
-                       std::vector<std::string>* app_ids) {
-    app_ids->clear();
-    for (const auto& item : model_->items()) {
-      if (item.type == ash::TYPE_PINNED_APP)
-        app_ids->push_back(item.app_launch_id.app_id());
+  // Gets the currently configured app launchers from the controller.
+  void GetAppLaunchers(ChromeLauncherControllerImpl* controller,
+                       std::vector<std::string>* launchers) {
+    launchers->clear();
+    for (ash::ShelfItems::const_iterator iter(model_->items().begin());
+         iter != model_->items().end(); ++iter) {
+      ChromeLauncherControllerImpl::IDToItemControllerMap::const_iterator entry(
+          controller->id_to_item_controller_map_.find(iter->id));
+      if (iter->type == ash::TYPE_PINNED_APP &&
+          entry != controller->id_to_item_controller_map_.end()) {
+        launchers->push_back(entry->second->app_id());
+      }
     }
   }
 
@@ -1866,7 +1874,7 @@
   // We activated arc_app_id1 twice but expect one close for item controller
   // stops launching request.
   ash::ShelfItemDelegate* item_delegate =
-      model_->GetShelfItemDelegate(shelf_id_app_1);
+      launcher_controller_->GetShelfItemDelegate(shelf_id_app_1);
   ASSERT_NE(nullptr, item_delegate);
   item_delegate->Close();
   base::RunLoop().RunUntilIdle();
@@ -1930,7 +1938,7 @@
 
   // Play Store app is ARC app that might be represented by native Chrome
   // platform app.
-  model_->SetShelfItemDelegate(
+  launcher_controller_->SetShelfItemDelegate(
       shelf_id, base::MakeUnique<ExtensionAppWindowLauncherItemController>(
                     ash::AppLaunchId(app_id)));
   launcher_controller_->SetItemStatus(shelf_id, ash::STATUS_RUNNING);
@@ -2408,8 +2416,9 @@
       multi_user_util::GetAccountIdFromProfile(profile());
 
   // Create a browser window with a native window for the current user.
+  Browser::CreateParams params(profile(), true);
   std::unique_ptr<Browser> browser(
-      CreateBrowserWithTestWindowForProfile(profile()));
+      chrome::CreateBrowserWithAuraTestWindowForParams(nullptr, &params));
   BrowserWindow* browser_window = browser->window();
   aura::Window* window = browser_window->GetNativeWindow();
   manager->SetWindowOwner(window, current_user);
@@ -2685,10 +2694,10 @@
   InsertAddPinChange(&sync_list, 10, extension_misc::kChromeAppId);
   SendPinChanges(sync_list, true);
 
-  std::vector<std::string> expected_pinned_apps;
-  std::vector<std::string> actual_pinned_apps;
-  GetPinnedAppIds(launcher_controller_, &actual_pinned_apps);
-  EXPECT_EQ(expected_pinned_apps, actual_pinned_apps);
+  std::vector<std::string> expected_launchers;
+  std::vector<std::string> actual_launchers;
+  GetAppLaunchers(launcher_controller_, &actual_launchers);
+  EXPECT_EQ(expected_launchers, actual_launchers);
 
   // Unavailable extensions don't create launcher items.
   sync_list.clear();
@@ -2697,49 +2706,48 @@
   InsertAddPinChange(&sync_list, 3, extension4_->id());
   SendPinChanges(sync_list, false);
 
-  expected_pinned_apps.push_back(extension2_->id());
-  expected_pinned_apps.push_back(extension4_->id());
-  GetPinnedAppIds(launcher_controller_, &actual_pinned_apps);
-  EXPECT_EQ(expected_pinned_apps, actual_pinned_apps);
+  expected_launchers.push_back(extension2_->id());
+  expected_launchers.push_back(extension4_->id());
+  GetAppLaunchers(launcher_controller_, &actual_launchers);
+  EXPECT_EQ(expected_launchers, actual_launchers);
 
   sync_list.clear();
   InsertAddPinChange(&sync_list, 2, extension3_->id());
   SendPinChanges(sync_list, false);
-  expected_pinned_apps.insert(expected_pinned_apps.begin() + 1,
-                              extension3_->id());
-  GetPinnedAppIds(launcher_controller_, &actual_pinned_apps);
-  EXPECT_EQ(expected_pinned_apps, actual_pinned_apps);
+  expected_launchers.insert(expected_launchers.begin() + 1, extension3_->id());
+  GetAppLaunchers(launcher_controller_, &actual_launchers);
+  EXPECT_EQ(expected_launchers, actual_launchers);
 
   sync_list.clear();
   InsertUpdatePinChange(&sync_list, 0, extension4_->id());
   InsertUpdatePinChange(&sync_list, 1, extension3_->id());
   InsertUpdatePinChange(&sync_list, 2, extension2_->id());
   SendPinChanges(sync_list, false);
-  std::reverse(expected_pinned_apps.begin(), expected_pinned_apps.end());
-  GetPinnedAppIds(launcher_controller_, &actual_pinned_apps);
-  EXPECT_EQ(expected_pinned_apps, actual_pinned_apps);
+  std::reverse(expected_launchers.begin(), expected_launchers.end());
+  GetAppLaunchers(launcher_controller_, &actual_launchers);
+  EXPECT_EQ(expected_launchers, actual_launchers);
 
   // Sending legacy sync change without pin info should not affect pin model.
   sync_list.clear();
   InsertLegacyPinChange(&sync_list, extension4_->id());
   SendPinChanges(sync_list, false);
-  GetPinnedAppIds(launcher_controller_, &actual_pinned_apps);
-  EXPECT_EQ(expected_pinned_apps, actual_pinned_apps);
+  GetAppLaunchers(launcher_controller_, &actual_launchers);
+  EXPECT_EQ(expected_launchers, actual_launchers);
 
   sync_list.clear();
   InsertRemovePinChange(&sync_list, extension4_->id());
   SendPinChanges(sync_list, false);
-  expected_pinned_apps.erase(expected_pinned_apps.begin());
-  GetPinnedAppIds(launcher_controller_, &actual_pinned_apps);
-  EXPECT_EQ(expected_pinned_apps, actual_pinned_apps);
+  expected_launchers.erase(expected_launchers.begin());
+  GetAppLaunchers(launcher_controller_, &actual_launchers);
+  EXPECT_EQ(expected_launchers, actual_launchers);
 
   sync_list.clear();
   InsertRemovePinChange(&sync_list, extension3_->id());
   InsertRemovePinChange(&sync_list, extension2_->id());
   SendPinChanges(sync_list, false);
-  expected_pinned_apps.clear();
-  GetPinnedAppIds(launcher_controller_, &actual_pinned_apps);
-  EXPECT_EQ(expected_pinned_apps, actual_pinned_apps);
+  expected_launchers.clear();
+  GetAppLaunchers(launcher_controller_, &actual_launchers);
+  EXPECT_EQ(expected_launchers, actual_launchers);
 }
 
 TEST_F(ChromeLauncherControllerImplTest, ImportLegacyPin) {
@@ -2801,20 +2809,19 @@
   InsertAddPinChange(&sync_list, 2, extension3_->id());
   SendPinChanges(sync_list, true);
 
-  std::vector<std::string> expected_pinned_apps;
-  expected_pinned_apps.push_back(extension1_->id());
-  expected_pinned_apps.push_back(extension3_->id());
-  std::vector<std::string> actual_pinned_apps;
+  std::vector<std::string> expected_launchers;
+  expected_launchers.push_back(extension1_->id());
+  expected_launchers.push_back(extension3_->id());
+  std::vector<std::string> actual_launchers;
 
-  GetPinnedAppIds(launcher_controller_, &actual_pinned_apps);
-  EXPECT_EQ(expected_pinned_apps, actual_pinned_apps);
+  GetAppLaunchers(launcher_controller_, &actual_launchers);
+  EXPECT_EQ(expected_launchers, actual_launchers);
 
   // Install |extension2| and verify it shows up between the other two.
   extension_service_->AddExtension(extension2_.get());
-  expected_pinned_apps.insert(expected_pinned_apps.begin() + 1,
-                              extension2_->id());
-  GetPinnedAppIds(launcher_controller_, &actual_pinned_apps);
-  EXPECT_EQ(expected_pinned_apps, actual_pinned_apps);
+  expected_launchers.insert(expected_launchers.begin() + 1, extension2_->id());
+  GetAppLaunchers(launcher_controller_, &actual_launchers);
+  EXPECT_EQ(expected_launchers, actual_launchers);
 }
 
 // Ensure |controller| creates the expected menu items for the given shelf item.
@@ -3274,7 +3281,7 @@
   base::string16 two_menu_items[] = {title1, title2};
   CheckAppMenu(launcher_controller_, item_gmail, 2, two_menu_items);
   ash::ShelfItemDelegate* item_delegate =
-      model_->GetShelfItemDelegate(gmail_id);
+      launcher_controller_->GetShelfItemDelegate(gmail_id);
   ASSERT_TRUE(item_delegate);
   EXPECT_EQ(1, browser()->tab_strip_model()->active_index());
   // Execute the second item in the menu, after the title and two separators,
@@ -3323,7 +3330,7 @@
   CheckAppMenu(launcher_controller_, item_gmail, 2, two_menu_items);
 
   ash::ShelfItemDelegate* item_delegate =
-      model_->GetShelfItemDelegate(gmail_id);
+      launcher_controller_->GetShelfItemDelegate(gmail_id);
   ASSERT_TRUE(item_delegate);
   int tabs = browser()->tab_strip_model()->count();
   // Activate the proper tab through the menu item.
@@ -3708,7 +3715,7 @@
   const ash::ShelfID shelf_id =
       launcher_controller_->GetShelfIDForAppID(app_id);
   ash::ShelfItemDelegate* item_delegate =
-      model_->GetShelfItemDelegate(shelf_id);
+      launcher_controller_->GetShelfItemDelegate(shelf_id);
   ASSERT_TRUE(item_delegate);
 
   // Selecting the item will show its application menu. It does not change the
@@ -4054,8 +4061,9 @@
       arc::kPlayStoreAppId));
 
   // Simulate click. This should schedule Play Store for deferred launch.
-  ash::ShelfItemDelegate* item_delegate = model_->GetShelfItemDelegate(
-      launcher_controller_->GetShelfIDForAppID(arc::kPlayStoreAppId));
+  ash::ShelfItemDelegate* item_delegate =
+      launcher_controller_->GetShelfItemDelegate(
+          launcher_controller_->GetShelfIDForAppID(arc::kPlayStoreAppId));
   EXPECT_TRUE(item_delegate);
   SelectItem(item_delegate);
   EXPECT_TRUE(launcher_controller_->IsAppPinned(arc::kPlayStoreAppId));
diff --git a/chrome/browser/ui/ash/launcher/chrome_launcher_controller_mus.cc b/chrome/browser/ui/ash/launcher/chrome_launcher_controller_mus.cc
index b213808..0bcb2992 100644
--- a/chrome/browser/ui/ash/launcher/chrome_launcher_controller_mus.cc
+++ b/chrome/browser/ui/ash/launcher/chrome_launcher_controller_mus.cc
@@ -43,6 +43,12 @@
   NOTIMPLEMENTED();
 }
 
+void ChromeLauncherControllerMus::SetShelfItemDelegate(
+    ash::ShelfID id,
+    std::unique_ptr<ash::ShelfItemDelegate> item_delegate) {
+  NOTIMPLEMENTED();
+}
+
 void ChromeLauncherControllerMus::CloseLauncherItem(ash::ShelfID id) {
   NOTIMPLEMENTED();
 }
@@ -169,6 +175,12 @@
   return nullptr;
 }
 
+ash::ShelfItemDelegate* ChromeLauncherControllerMus::GetShelfItemDelegate(
+    const ash::ShelfID id) {
+  NOTIMPLEMENTED();
+  return nullptr;
+}
+
 bool ChromeLauncherControllerMus::ShelfBoundsChangesProbablyWithUser(
     ash::WmShelf* shelf,
     const AccountId& account_id) const {
diff --git a/chrome/browser/ui/ash/launcher/chrome_launcher_controller_mus.h b/chrome/browser/ui/ash/launcher/chrome_launcher_controller_mus.h
index 51babed9..e506604 100644
--- a/chrome/browser/ui/ash/launcher/chrome_launcher_controller_mus.h
+++ b/chrome/browser/ui/ash/launcher/chrome_launcher_controller_mus.h
@@ -22,6 +22,9 @@
   const ash::ShelfItem* GetItem(ash::ShelfID id) const override;
   void SetItemType(ash::ShelfID id, ash::ShelfItemType type) override;
   void SetItemStatus(ash::ShelfID id, ash::ShelfItemStatus status) override;
+  void SetShelfItemDelegate(
+      ash::ShelfID id,
+      std::unique_ptr<ash::ShelfItemDelegate> item_delegate) override;
   void CloseLauncherItem(ash::ShelfID id) override;
   bool IsPinned(ash::ShelfID id) override;
   void SetV1AppStatus(const std::string& app_id,
@@ -59,6 +62,7 @@
       content::WebContents* web_contents) const override;
   BrowserShortcutLauncherItemController*
   GetBrowserShortcutLauncherItemController() override;
+  ash::ShelfItemDelegate* GetShelfItemDelegate(const ash::ShelfID id) override;
   bool ShelfBoundsChangesProbablyWithUser(
       ash::WmShelf* shelf,
       const AccountId& account_id) const override;
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 5e4528c3..7b1ed43 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,7 +5,6 @@
 #include "chrome/browser/ui/ash/launcher/extension_app_window_launcher_controller.h"
 
 #include "ash/common/shelf/shelf_delegate.h"
-#include "ash/common/shelf/shelf_model.h"
 #include "ash/common/wm_window.h"
 #include "ash/shell.h"
 #include "ash/wm/window_properties.h"
@@ -195,8 +194,7 @@
         item_controller->set_image_set_by_controller(true);
       }
     } else {
-      ash::ShelfModel* shelf_model = ash::Shell::Get()->shelf_model();
-      shelf_model->SetShelfItemDelegate(shelf_id, std::move(controller));
+      owner()->SetShelfItemDelegate(shelf_id, std::move(controller));
     }
 
     // We need to change the controller associated with app_shelf_id.
diff --git a/third_party/WebKit/Source/core/css/MediaQueryEvaluator.cpp b/third_party/WebKit/Source/core/css/MediaQueryEvaluator.cpp
index 823b91f..570e130f 100644
--- a/third_party/WebKit/Source/core/css/MediaQueryEvaluator.cpp
+++ b/third_party/WebKit/Source/core/css/MediaQueryEvaluator.cpp
@@ -151,9 +151,12 @@
 
   // Iterate over queries, stop if any of them eval to true (OR semantics).
   bool result = false;
-  for (size_t i = 0; i < queries.size() && !result; ++i)
+  for (size_t i = 0; i < queries.size() && !result; ++i) {
+    // TODO(sof): CHECK() added for crbug.com/699269 diagnosis, remove sooner.
+    CHECK_EQ(queries.data(), querySet->queryVector().data());
     result = eval(queries[i].get(), viewportDependentMediaQueryResults,
                   deviceDependentMediaQueryResults);
+  }
 
   return result;
 }
diff --git a/third_party/WebKit/Source/core/css/RuleSet.cpp b/third_party/WebKit/Source/core/css/RuleSet.cpp
index 897e8b5..c7854052 100644
--- a/third_party/WebKit/Source/core/css/RuleSet.cpp
+++ b/third_party/WebKit/Source/core/css/RuleSet.cpp
@@ -329,6 +329,8 @@
   const HeapVector<Member<StyleRuleImport>>& importRules = sheet->importRules();
   for (unsigned i = 0; i < importRules.size(); ++i) {
     StyleRuleImport* importRule = importRules[i].get();
+    // TODO(sof): CHECK() added for crbug.com/699269 diagnosis, remove sooner.
+    CHECK_EQ(importRules.data(), sheet->importRules().data());
     if (importRule->styleSheet() &&
         (!importRule->mediaQueries() ||
          medium.eval(importRule->mediaQueries(),
diff --git a/third_party/WebKit/Source/devtools/front_end/Images/chevrons.png b/third_party/WebKit/Source/devtools/front_end/Images/chevrons.png
new file mode 100644
index 0000000..460aafb
--- /dev/null
+++ b/third_party/WebKit/Source/devtools/front_end/Images/chevrons.png
Binary files differ
diff --git a/third_party/WebKit/Source/devtools/front_end/Images/chevrons_2x.png b/third_party/WebKit/Source/devtools/front_end/Images/chevrons_2x.png
new file mode 100644
index 0000000..091217f0
--- /dev/null
+++ b/third_party/WebKit/Source/devtools/front_end/Images/chevrons_2x.png
Binary files differ
diff --git a/third_party/WebKit/Source/devtools/front_end/Images/src/chevrons.svg b/third_party/WebKit/Source/devtools/front_end/Images/src/chevrons.svg
new file mode 100644
index 0000000..59e5ae1
--- /dev/null
+++ b/third_party/WebKit/Source/devtools/front_end/Images/src/chevrons.svg
@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   width="30"
+   height="10"
+   id="svg3232"
+   version="1.1"
+   inkscape:version="0.48.4 r9939"
+   sodipodi:docname="chevrons.svg">
+  <defs
+     id="defs3234" />
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageopacity="0.0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="2"
+     inkscape:cx="375"
+     inkscape:cy="-51.428571"
+     inkscape:document-units="px"
+     inkscape:current-layer="layer1"
+     showgrid="false"
+     inkscape:window-width="1640"
+     inkscape:window-height="844"
+     inkscape:window-x="516"
+     inkscape:window-y="280"
+     inkscape:window-maximized="0" />
+  <metadata
+     id="metadata3237">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <dc:title></dc:title>
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:groupmode="layer"
+     id="layer1">
+    <path
+       inkscape:connector-curvature="0"
+       d="M 0,2.75005 0.96529,1.78476 5.07291,5.89238 0.96529,10 0,9.03471 3.13549,5.89238 z"
+       id="path4-7" />
+    <path
+       inkscape:connector-curvature="0"
+       d="M 27.24995,2.92708 28.21524,3.89238 24.10762,8 20,3.89238 l 0.96529,-0.9653 3.14233,3.13549 z"
+       id="path4-7-5" />
+  </g>
+</svg>
diff --git a/third_party/WebKit/Source/devtools/front_end/Images/src/optimize_png.hashes b/third_party/WebKit/Source/devtools/front_end/Images/src/optimize_png.hashes
index 7796a5b2..edecedd 100644
--- a/third_party/WebKit/Source/devtools/front_end/Images/src/optimize_png.hashes
+++ b/third_party/WebKit/Source/devtools/front_end/Images/src/optimize_png.hashes
@@ -9,5 +9,6 @@
     "breakpoint.svg": "69cd92d807259c022791112809b97799",
     "treeoutlineTriangles.svg": "017d2f89437df0afc6b9cd5ff43735d9",
     "audits_logo_bw.svg": "203dcb2ba32ef0f4595ad45bb8feffab",
+    "chevrons.svg": "79b4b527771e30b6388ce664077b3409",
     "audits_logo.svg": "647095d7981857c22a816eef12f75b91"
 }
\ No newline at end of file
diff --git a/third_party/WebKit/Source/devtools/front_end/Images/src/svg2png.hashes b/third_party/WebKit/Source/devtools/front_end/Images/src/svg2png.hashes
index 7796a5b2..edecedd 100644
--- a/third_party/WebKit/Source/devtools/front_end/Images/src/svg2png.hashes
+++ b/third_party/WebKit/Source/devtools/front_end/Images/src/svg2png.hashes
@@ -9,5 +9,6 @@
     "breakpoint.svg": "69cd92d807259c022791112809b97799",
     "treeoutlineTriangles.svg": "017d2f89437df0afc6b9cd5ff43735d9",
     "audits_logo_bw.svg": "203dcb2ba32ef0f4595ad45bb8feffab",
+    "chevrons.svg": "79b4b527771e30b6388ce664077b3409",
     "audits_logo.svg": "647095d7981857c22a816eef12f75b91"
 }
\ No newline at end of file
diff --git a/third_party/WebKit/Source/devtools/front_end/accessibility/AXTreePane.js b/third_party/WebKit/Source/devtools/front_end/accessibility/AXTreePane.js
index 203ea94..fc69390e 100644
--- a/third_party/WebKit/Source/devtools/front_end/accessibility/AXTreePane.js
+++ b/third_party/WebKit/Source/devtools/front_end/accessibility/AXTreePane.js
@@ -13,6 +13,7 @@
 
     this._axSidebarView = axSidebarView;
     this._treeOutline = this.createTreeOutline();
+    this._treeOutline.contentElement.classList.add('ax-tree');
     this._treeOutline.setPaddingSize(12);
     this._treeOutline.element.addEventListener('keydown', this._onKeyDown.bind(this), true);
     this._treeOutline.element.addEventListener('mouseleave', this._onMouseLeave.bind(this), false);
@@ -210,7 +211,7 @@
    */
   constructor(axNode, treePane) {
     // Pass an empty title, the title gets made later in onattach.
-    super('');
+    super('', false);
 
     /** @type {!Accessibility.AccessibilityNode} */
     this._axNode = axNode;
@@ -273,6 +274,16 @@
       this.listItemElement.removeAttribute('tabIndex');
   }
 
+  /**
+   * @param {!Event} event
+   * @return {boolean}
+   * @override
+   */
+  isEventWithinDisclosureTriangle(event) {
+    // Accessibility tree doesn't have a "disclosure triangle" per se.
+    return false;
+  }
+
   _onmousemove(event) {
     if (this._preselected || this._inspected || !this._axNode.isDOMNode())
       return;
diff --git a/third_party/WebKit/Source/devtools/front_end/accessibility/accessibilityNode.css b/third_party/WebKit/Source/devtools/front_end/accessibility/accessibilityNode.css
index 20518fd3..53131c3 100644
--- a/third_party/WebKit/Source/devtools/front_end/accessibility/accessibilityNode.css
+++ b/third_party/WebKit/Source/devtools/front_end/accessibility/accessibilityNode.css
@@ -80,18 +80,27 @@
     left: -2px;
 }
 
-.tree-outline li.no-dom-node {
+.tree-outline.ax-tree li::before {
+    -webkit-mask-image: url(Images/chevrons.png);
+    -webkit-mask-size: 30px 10px;
+    -webkit-mask-repeat: no-repeat;
+}
+
+.tree-outline.ax-tree li.parent::before {
+    -webkit-mask-position: -20px 1px;
+}
+.tree-outline.ax-tree li.children-unloaded::before {
+    -webkit-mask-position: 0px 1px;
+}
+
+.tree-outline.ax-tree li.no-dom-node {
     opacity: 0.5;
 }
 
-.tree-outline li.children-unloaded::before {
+.tree-outline.ax-tree li.children-unloaded::before {
     opacity: 0.2;
 }
 
-.tree-outline li.partially-expanded::before {
-    -webkit-mask-position: -19px -108px;
-}
-
 .invalid {
     text-decoration: line-through;
 }