15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Copyright (c) 2012 The Chromium Authors. All rights reserved.
25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// found in the LICENSE file.
45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
55821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/extensions/extension_toolbar_model.h"
65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
72a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "base/prefs/pref_service.h"
87dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch#include "chrome/browser/chrome_notification_types.h"
9b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)#include "chrome/browser/extensions/api/extension_action/extension_action_api.h"
105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/extensions/browser_event_router.h"
115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/extensions/extension_action.h"
125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/extensions/extension_action_manager.h"
135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/extensions/extension_prefs.h"
145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/extensions/extension_service.h"
155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/extensions/extension_tab_util.h"
165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/extensions/tab_helper.h"
175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/profiles/profile.h"
185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/ui/browser.h"
192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chrome/browser/ui/tabs/tab_strip_model.h"
205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/common/extensions/extension.h"
215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/common/extensions/feature_switch.h"
225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/common/pref_names.h"
235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "content/public/browser/notification_details.h"
245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "content/public/browser/notification_source.h"
255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "content/public/browser/web_contents.h"
265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)using extensions::Extension;
282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)using extensions::ExtensionIdList;
295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)using extensions::ExtensionList;
305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace {
325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Returns true if an |extension| is in an |extension_list|.
345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool IsInExtensionList(const Extension* extension,
355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                       const extensions::ExtensionList& extension_list) {
365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (size_t i = 0; i < extension_list.size(); i++) {
375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (extension_list[i].get() == extension)
385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return true;
395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return false;
415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}  // namespace
445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ExtensionToolbarModel::ExtensionToolbarModel(ExtensionService* service)
465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    : service_(service),
475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      prefs_(service->profile()->GetPrefs()),
48c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      extensions_initialized_(false),
49c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      weak_ptr_factory_(this) {
505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(service_);
515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_LOADED,
535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                 content::Source<Profile>(service_->profile()));
545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_UNLOADED,
555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                 content::Source<Profile>(service_->profile()));
565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  registrar_.Add(this, chrome::NOTIFICATION_EXTENSIONS_READY,
575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                 content::Source<Profile>(service_->profile()));
582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_UNINSTALLED,
592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                 content::Source<Profile>(service_->profile()));
605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  registrar_.Add(
615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this, chrome::NOTIFICATION_EXTENSION_BROWSER_ACTION_VISIBILITY_CHANGED,
625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      content::Source<extensions::ExtensionPrefs>(service_->extension_prefs()));
635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  visible_icon_count_ = prefs_->GetInteger(prefs::kExtensionToolbarSize);
65c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
66c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  pref_change_registrar_.Init(prefs_);
67c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  pref_change_callback_ =
68c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      base::Bind(&ExtensionToolbarModel::OnExtensionToolbarPrefChange,
69c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                 base::Unretained(this));
70c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  pref_change_registrar_.Add(prefs::kExtensionToolbar, pref_change_callback_);
715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ExtensionToolbarModel::~ExtensionToolbarModel() {
745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void ExtensionToolbarModel::AddObserver(Observer* observer) {
775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  observers_.AddObserver(observer);
785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void ExtensionToolbarModel::RemoveObserver(Observer* observer) {
815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  observers_.RemoveObserver(observer);
825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void ExtensionToolbarModel::MoveBrowserAction(const Extension* extension,
855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                              int index) {
865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ExtensionList::iterator pos = std::find(toolbar_items_.begin(),
875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      toolbar_items_.end(), extension);
885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (pos == toolbar_items_.end()) {
895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    NOTREACHED();
905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  toolbar_items_.erase(pos);
935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
942a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  ExtensionIdList::iterator pos_id;
952a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  pos_id = std::find(last_known_positions_.begin(),
962a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                     last_known_positions_.end(), extension->id());
972a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (pos_id != last_known_positions_.end())
982a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    last_known_positions_.erase(pos_id);
992a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int i = 0;
1015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  bool inserted = false;
1025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (ExtensionList::iterator iter = toolbar_items_.begin();
1035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)       iter != toolbar_items_.end();
1045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)       ++iter, ++i) {
1055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (i == index) {
1062a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      pos_id = std::find(last_known_positions_.begin(),
1072a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                         last_known_positions_.end(), (*iter)->id());
1082a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      last_known_positions_.insert(pos_id, extension->id());
1092a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      toolbar_items_.insert(iter, make_scoped_refptr(extension));
1115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      inserted = true;
1125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
1135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
1145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!inserted) {
1175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    DCHECK_EQ(index, static_cast<int>(toolbar_items_.size()));
1185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    index = toolbar_items_.size();
1195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    toolbar_items_.push_back(make_scoped_refptr(extension));
1212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    last_known_positions_.push_back(extension->id());
1225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  FOR_EACH_OBSERVER(Observer, observers_, BrowserActionMoved(extension, index));
1255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  UpdatePrefs();
1275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ExtensionToolbarModel::Action ExtensionToolbarModel::ExecuteBrowserAction(
1305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const Extension* extension,
1315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Browser* browser,
1325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    GURL* popup_url_out) {
1332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  content::WebContents* web_contents =
1342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      browser->tab_strip_model()->GetActiveWebContents();
1352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (!web_contents)
1365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return ACTION_NONE;
1375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  int tab_id = ExtensionTabUtil::GetTabId(web_contents);
1395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (tab_id < 0)
1405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return ACTION_NONE;
1415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ExtensionAction* browser_action =
1435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      extensions::ExtensionActionManager::Get(service_->profile())->
1445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      GetBrowserAction(*extension);
1455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // For browser actions, visibility == enabledness.
1475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!browser_action->GetIsVisible(tab_id))
1485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return ACTION_NONE;
1495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  extensions::TabHelper::FromWebContents(web_contents)->
1515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      active_tab_permission_granter()->GrantIfRequested(extension);
1525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (browser_action->HasPopup(tab_id)) {
1545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (popup_url_out)
1555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      *popup_url_out = browser_action->GetPopupUrl(tab_id);
1565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return ACTION_SHOW_POPUP;
1575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  service_->browser_event_router()->BrowserActionExecuted(
1605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      *browser_action, browser);
1615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return ACTION_NONE;
1625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void ExtensionToolbarModel::SetVisibleIconCount(int count) {
1655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  visible_icon_count_ =
1665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      count == static_cast<int>(toolbar_items_.size()) ? -1 : count;
1675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  prefs_->SetInteger(prefs::kExtensionToolbarSize, visible_icon_count_);
1685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void ExtensionToolbarModel::Observe(
1715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    int type,
1725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const content::NotificationSource& source,
1735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const content::NotificationDetails& details) {
1745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (type == chrome::NOTIFICATION_EXTENSIONS_READY) {
1752a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    InitializeExtensionList();
1765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
1775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!service_->is_ready())
1805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
1815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const Extension* extension = NULL;
1835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (type == chrome::NOTIFICATION_EXTENSION_UNLOADED) {
1845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    extension = content::Details<extensions::UnloadedExtensionInfo>(
1855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        details)->extension;
186b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  } else if (type ==
187b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)      chrome::NOTIFICATION_EXTENSION_BROWSER_ACTION_VISIBILITY_CHANGED) {
188b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)    extension = service_->GetExtensionById(
189b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)        *content::Details<const std::string>(details).ptr(), true);
1905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else {
1915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    extension = content::Details<const Extension>(details).ptr();
1925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (type == chrome::NOTIFICATION_EXTENSION_LOADED) {
1945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // We don't want to add the same extension twice. It may have already been
1955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // added by EXTENSION_BROWSER_ACTION_VISIBILITY_CHANGED below, if the user
1965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // hides the browser action and then disables and enables the extension.
1972a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    for (size_t i = 0; i < toolbar_items_.size(); i++) {
1982a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      if (toolbar_items_[i].get() == extension)
1992a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        return;  // Already exists.
2002a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
201b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)    if (extensions::ExtensionActionAPI::GetBrowserActionVisibility(
202b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)            service_->extension_prefs(), extension->id())) {
2032a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      AddExtension(extension);
204b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)    }
2055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else if (type == chrome::NOTIFICATION_EXTENSION_UNLOADED) {
2062a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    RemoveExtension(extension);
2072a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  } else if (type == chrome::NOTIFICATION_EXTENSION_UNINSTALLED) {
2082a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    UninstalledExtension(extension);
2095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else if (type ==
2105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      chrome::NOTIFICATION_EXTENSION_BROWSER_ACTION_VISIBILITY_CHANGED) {
211b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)    if (extensions::ExtensionActionAPI::GetBrowserActionVisibility(
212b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)            service_->extension_prefs(), extension->id())) {
2132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      AddExtension(extension);
214b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)    } else {
2152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      RemoveExtension(extension);
216b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)    }
2175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else {
2185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    NOTREACHED() << "Received unexpected notification";
2195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)size_t ExtensionToolbarModel::FindNewPositionFromLastKnownGood(
2232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    const Extension* extension) {
2242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // See if we have last known good position for this extension.
2252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  size_t new_index = 0;
2262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Loop through the ID list of known positions, to count the number of visible
2272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // browser action icons preceding |extension|.
2282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  for (ExtensionIdList::const_iterator iter_id = last_known_positions_.begin();
2292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)       iter_id < last_known_positions_.end(); ++iter_id) {
2302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if ((*iter_id) == extension->id())
2312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      return new_index;  // We've found the right position.
2322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    // Found an id, need to see if it is visible.
2332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    for (ExtensionList::const_iterator iter_ext = toolbar_items_.begin();
2342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)         iter_ext < toolbar_items_.end(); ++iter_ext) {
2352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      if ((*iter_ext)->id().compare(*iter_id) == 0) {
2362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        // This extension is visible, update the index value.
2372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        ++new_index;
2382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        break;
2392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      }
2402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
2412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
2422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return -1;
2442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
2452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void ExtensionToolbarModel::AddExtension(const Extension* extension) {
2475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // We only care about extensions with browser actions.
2485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!extensions::ExtensionActionManager::Get(service_->profile())->
2495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      GetBrowserAction(*extension)) {
2505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
2515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  size_t new_index = -1;
2542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // See if we have a last known good position for this extension.
2562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  ExtensionIdList::iterator last_pos = std::find(last_known_positions_.begin(),
2572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                                 last_known_positions_.end(),
2582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                                 extension->id());
2592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (last_pos != last_known_positions_.end()) {
2602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    new_index = FindNewPositionFromLastKnownGood(extension);
2612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if (new_index != toolbar_items_.size()) {
2622a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      toolbar_items_.insert(toolbar_items_.begin() + new_index,
2632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                            make_scoped_refptr(extension));
2642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    } else {
2652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      toolbar_items_.push_back(make_scoped_refptr(extension));
2665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
2675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else {
2682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    // This is a never before seen extension, that was added to the end. Make
2692a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    // sure to reflect that.
2702a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    toolbar_items_.push_back(make_scoped_refptr(extension));
2712a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    last_known_positions_.push_back(extension->id());
2722a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    new_index = toolbar_items_.size() - 1;
2732a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    UpdatePrefs();
2745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  FOR_EACH_OBSERVER(Observer, observers_,
2772a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                    BrowserActionAdded(extension, new_index));
2785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2802a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void ExtensionToolbarModel::RemoveExtension(const Extension* extension) {
2815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ExtensionList::iterator pos =
2822a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      std::find(toolbar_items_.begin(), toolbar_items_.end(), extension);
2832a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (pos == toolbar_items_.end())
2845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
2855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2862a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  toolbar_items_.erase(pos);
2872a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  FOR_EACH_OBSERVER(Observer, observers_,
2882a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                    BrowserActionRemoved(extension));
2895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  UpdatePrefs();
2915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2932a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void ExtensionToolbarModel::UninstalledExtension(const Extension* extension) {
2942a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Remove the extension id from the ordered list, if it exists (the extension
2952a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // might not be represented in the list because it might not have an icon).
2962a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  ExtensionIdList::iterator pos =
2972a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      std::find(last_known_positions_.begin(),
2982a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                last_known_positions_.end(), extension->id());
2995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3002a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (pos != last_known_positions_.end()) {
3012a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    last_known_positions_.erase(pos);
3022a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    UpdatePrefs();
3032a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
3042a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
3055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Combine the currently enabled extensions that have browser actions (which
3075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// we get from the ExtensionService) with the ordering we get from the
3085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// pref service. For robustness we use a somewhat inefficient process:
3095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// 1. Create a vector of extensions sorted by their pref values. This vector may
3105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// have holes.
3115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// 2. Create a vector of extensions that did not have a pref value.
3125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// 3. Remove holes from the sorted vector and append the unsorted vector.
3132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void ExtensionToolbarModel::InitializeExtensionList() {
3145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(service_->is_ready());
3155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
316c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  last_known_positions_ = service_->extension_prefs()->GetToolbarOrder();
317c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  Populate(last_known_positions_);
3185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  extensions_initialized_ = true;
3205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  FOR_EACH_OBSERVER(Observer, observers_, ModelLoaded());
3215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
323c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)void ExtensionToolbarModel::Populate(
324c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    const extensions::ExtensionIdList& positions) {
325c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // Items that have explicit positions.
3265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ExtensionList sorted;
327c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  sorted.resize(positions.size(), NULL);
328c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // The items that don't have explicit positions.
3295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ExtensionList unsorted;
3305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  extensions::ExtensionActionManager* extension_action_manager =
3325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      extensions::ExtensionActionManager::Get(service_->profile());
3335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Create the lists.
3355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (ExtensionSet::const_iterator it = service_->extensions()->begin();
3365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)       it != service_->extensions()->end(); ++it) {
3377d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)    const Extension* extension = it->get();
3385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (!extension_action_manager->GetBrowserAction(*extension))
3395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      continue;
340b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)    if (!extensions::ExtensionActionAPI::GetBrowserActionVisibility(
341b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)            service_->extension_prefs(), extension->id())) {
3425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      continue;
343b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)    }
3445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    extensions::ExtensionIdList::const_iterator pos =
346c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        std::find(positions.begin(), positions.end(), extension->id());
347c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    if (pos != positions.end())
348c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      sorted[pos - positions.begin()] = extension;
3495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    else
3505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      unsorted.push_back(make_scoped_refptr(extension));
3515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
353c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // Erase current icons.
354c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  for (size_t i = 0; i < toolbar_items_.size(); i++) {
355868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)    FOR_EACH_OBSERVER(
356868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)        Observer, observers_, BrowserActionRemoved(toolbar_items_[i].get()));
357c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  }
3585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  toolbar_items_.clear();
359c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
360c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // Merge the lists.
3615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  toolbar_items_.reserve(sorted.size() + unsorted.size());
3625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (ExtensionList::const_iterator iter = sorted.begin();
3635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)       iter != sorted.end(); ++iter) {
3645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // It's possible for the extension order to contain items that aren't
3655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // actually loaded on this machine.  For example, when extension sync is on,
3665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // we sync the extension order as-is but double-check with the user before
3675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // syncing NPAPI-containing extensions, so if one of those is not actually
3685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // synced, we'll get a NULL in the list.  This sort of case can also happen
3695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // if some error prevents an extension from loading.
370868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)    if (iter->get() != NULL)
3715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      toolbar_items_.push_back(*iter);
3725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  toolbar_items_.insert(toolbar_items_.end(), unsorted.begin(),
3745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                        unsorted.end());
3755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Inform observers.
3775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (size_t i = 0; i < toolbar_items_.size(); i++) {
378868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)    FOR_EACH_OBSERVER(
379868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)        Observer, observers_, BrowserActionAdded(toolbar_items_[i].get(), i));
3805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void ExtensionToolbarModel::FillExtensionList(
3842a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    const extensions::ExtensionIdList& order) {
3852a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  toolbar_items_.clear();
3862a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  toolbar_items_.reserve(order.size());
3875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (size_t i = 0; i < order.size(); ++i) {
3885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const extensions::Extension* extension =
3895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        service_->GetExtensionById(order[i], false);
3905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (extension)
3912a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      AddExtension(extension);
3925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void ExtensionToolbarModel::UpdatePrefs() {
3965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!service_->extension_prefs())
3975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
3985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
399c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // Don't observe change caused by self.
400c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  pref_change_registrar_.Remove(prefs::kExtensionToolbar);
4012a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  service_->extension_prefs()->SetToolbarOrder(last_known_positions_);
402c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  pref_change_registrar_.Add(prefs::kExtensionToolbar, pref_change_callback_);
4035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
4045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)int ExtensionToolbarModel::IncognitoIndexToOriginal(int incognito_index) {
4065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int original_index = 0, i = 0;
4075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (ExtensionList::iterator iter = toolbar_items_.begin();
4085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)       iter != toolbar_items_.end();
4095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)       ++iter, ++original_index) {
4105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (service_->IsIncognitoEnabled((*iter)->id())) {
4115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (incognito_index == i)
4125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        break;
4135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      ++i;
4145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
4155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
4165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return original_index;
4175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
4185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)int ExtensionToolbarModel::OriginalIndexToIncognito(int original_index) {
4205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int incognito_index = 0, i = 0;
4215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (ExtensionList::iterator iter = toolbar_items_.begin();
4225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)       iter != toolbar_items_.end();
4235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)       ++iter, ++i) {
4245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (original_index == i)
4255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
4265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (service_->IsIncognitoEnabled((*iter)->id()))
4275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      ++incognito_index;
4285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
4295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return incognito_index;
4305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
431c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
432c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)void ExtensionToolbarModel::OnExtensionToolbarPrefChange() {
433c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // If extensions are not ready, defer to later Populate() call.
434c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  if (!extensions_initialized_)
435c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    return;
436c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
437c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // Recalculate |last_known_positions_| to be |pref_positions| followed by
438c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // ones that are only in |last_known_positions_|.
439c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  extensions::ExtensionIdList pref_positions =
440c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      service_->extension_prefs()->GetToolbarOrder();
441c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  size_t pref_position_size = pref_positions.size();
442c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  for (size_t i = 0; i < last_known_positions_.size(); ++i) {
443c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    if (std::find(pref_positions.begin(), pref_positions.end(),
444c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                  last_known_positions_[i]) == pref_positions.end()) {
445c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      pref_positions.push_back(last_known_positions_[i]);
446c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    }
447c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  }
448c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  last_known_positions_.swap(pref_positions);
449c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
450c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // Re-populate.
451c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  Populate(last_known_positions_);
452c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
453c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  if (last_known_positions_.size() > pref_position_size) {
454c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    // Need to update pref because we have extra icons. But can't call
455c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    // UpdatePrefs() directly within observation closure.
456c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    base::MessageLoop::current()->PostTask(
457c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        FROM_HERE,
458c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        base::Bind(&ExtensionToolbarModel::UpdatePrefs,
459c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                   weak_ptr_factory_.GetWeakPtr()));
460c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  }
461c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)}
462