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)
78bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)#include <string>
88bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)
92a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "base/prefs/pref_service.h"
107dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch#include "chrome/browser/chrome_notification_types.h"
11b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)#include "chrome/browser/extensions/api/extension_action/extension_action_api.h"
125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/extensions/extension_action.h"
135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/extensions/extension_action_manager.h"
145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/extensions/extension_prefs.h"
155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/extensions/extension_service.h"
165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/extensions/extension_tab_util.h"
17f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)#include "chrome/browser/extensions/extension_toolbar_model_factory.h"
188bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)#include "chrome/browser/extensions/extension_util.h"
195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/extensions/tab_helper.h"
205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/profiles/profile.h"
215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/ui/browser.h"
222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chrome/browser/ui/tabs/tab_strip_model.h"
235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/common/pref_names.h"
245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "content/public/browser/notification_details.h"
255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "content/public/browser/notification_source.h"
265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "content/public/browser/web_contents.h"
27f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)#include "extensions/common/extension.h"
28f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)#include "extensions/common/feature_switch.h"
295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)using extensions::Extension;
312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)using extensions::ExtensionIdList;
325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)using extensions::ExtensionList;
335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
348bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)bool ExtensionToolbarModel::Observer::BrowserActionShowPopup(
358bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)    const extensions::Extension* extension) {
368bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)  return false;
378bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)}
388bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)
39f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)ExtensionToolbarModel::ExtensionToolbarModel(
40f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    Profile* profile,
41f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    extensions::ExtensionPrefs* extension_prefs)
42f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    : profile_(profile),
43f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)      extension_prefs_(extension_prefs),
44f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)      prefs_(profile_->GetPrefs()),
45c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      extensions_initialized_(false),
46c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      weak_ptr_factory_(this) {
475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_LOADED,
48f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)                 content::Source<Profile>(profile_));
495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_UNLOADED,
50f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)                 content::Source<Profile>(profile_));
515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  registrar_.Add(this, chrome::NOTIFICATION_EXTENSIONS_READY,
52f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)                 content::Source<Profile>(profile_));
532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_UNINSTALLED,
54f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)                 content::Source<Profile>(profile_));
555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  registrar_.Add(
565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this, chrome::NOTIFICATION_EXTENSION_BROWSER_ACTION_VISIBILITY_CHANGED,
57f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)      content::Source<extensions::ExtensionPrefs>(extension_prefs_));
585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  visible_icon_count_ = prefs_->GetInteger(prefs::kExtensionToolbarSize);
60c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
61c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  pref_change_registrar_.Init(prefs_);
62c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  pref_change_callback_ =
63c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      base::Bind(&ExtensionToolbarModel::OnExtensionToolbarPrefChange,
64c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                 base::Unretained(this));
65c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  pref_change_registrar_.Add(prefs::kExtensionToolbar, pref_change_callback_);
665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ExtensionToolbarModel::~ExtensionToolbarModel() {
695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
71f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)// static
72f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)ExtensionToolbarModel* ExtensionToolbarModel::Get(Profile* profile) {
73f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  return ExtensionToolbarModelFactory::GetForProfile(profile);
74f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)}
75f2477e01787aa58f445919b809d89e252beef54fTorne (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,
1328bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)    GURL* popup_url_out,
1338bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)    bool should_grant) {
1343551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)  content::WebContents* web_contents = NULL;
1353551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)  int tab_id = 0;
136f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  if (!extensions::ExtensionTabUtil::GetDefaultTab(
137f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)          browser, &web_contents, &tab_id)) {
1385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return ACTION_NONE;
139f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  }
1405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ExtensionAction* browser_action =
142f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)      extensions::ExtensionActionManager::Get(profile_)->
1435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      GetBrowserAction(*extension);
1445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // For browser actions, visibility == enabledness.
1465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!browser_action->GetIsVisible(tab_id))
1475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return ACTION_NONE;
1485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1498bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)  if (should_grant) {
1508bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)    extensions::TabHelper::FromWebContents(web_contents)->
1518bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)        active_tab_permission_granter()->GrantIfRequested(extension);
1528bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)  }
1535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (browser_action->HasPopup(tab_id)) {
1555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (popup_url_out)
1565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      *popup_url_out = browser_action->GetPopupUrl(tab_id);
1575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return ACTION_SHOW_POPUP;
1585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1603551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)  extensions::ExtensionActionAPI::BrowserActionExecuted(
161f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)      browser->profile(), *browser_action, web_contents);
1625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return ACTION_NONE;
1635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void ExtensionToolbarModel::SetVisibleIconCount(int count) {
1665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  visible_icon_count_ =
1675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      count == static_cast<int>(toolbar_items_.size()) ? -1 : count;
1685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  prefs_->SetInteger(prefs::kExtensionToolbarSize, visible_icon_count_);
1695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void ExtensionToolbarModel::Observe(
1725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    int type,
1735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const content::NotificationSource& source,
1745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const content::NotificationDetails& details) {
175f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  ExtensionService* extension_service =
176f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)      extensions::ExtensionSystem::Get(profile_)->extension_service();
177f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  if (!extension_service || !extension_service->is_ready())
1785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
1795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
180f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  if (type == chrome::NOTIFICATION_EXTENSIONS_READY) {
181f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    InitializeExtensionList(extension_service);
1825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
183f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  }
1845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const Extension* extension = NULL;
1865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (type == chrome::NOTIFICATION_EXTENSION_UNLOADED) {
1875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    extension = content::Details<extensions::UnloadedExtensionInfo>(
1885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        details)->extension;
189b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  } else if (type ==
190b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)      chrome::NOTIFICATION_EXTENSION_BROWSER_ACTION_VISIBILITY_CHANGED) {
191f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    extension = extension_service->GetExtensionById(
192b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)        *content::Details<const std::string>(details).ptr(), true);
1935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else {
1945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    extension = content::Details<const Extension>(details).ptr();
1955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (type == chrome::NOTIFICATION_EXTENSION_LOADED) {
1975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // We don't want to add the same extension twice. It may have already been
1985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // added by EXTENSION_BROWSER_ACTION_VISIBILITY_CHANGED below, if the user
1995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // hides the browser action and then disables and enables the extension.
2002a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    for (size_t i = 0; i < toolbar_items_.size(); i++) {
2012a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      if (toolbar_items_[i].get() == extension)
2022a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        return;  // Already exists.
2032a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
204b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)    if (extensions::ExtensionActionAPI::GetBrowserActionVisibility(
205f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)            extension_prefs_, extension->id())) {
2062a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      AddExtension(extension);
207b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)    }
2085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else if (type == chrome::NOTIFICATION_EXTENSION_UNLOADED) {
2092a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    RemoveExtension(extension);
2102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  } else if (type == chrome::NOTIFICATION_EXTENSION_UNINSTALLED) {
2112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    UninstalledExtension(extension);
2125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else if (type ==
2135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      chrome::NOTIFICATION_EXTENSION_BROWSER_ACTION_VISIBILITY_CHANGED) {
214b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)    if (extensions::ExtensionActionAPI::GetBrowserActionVisibility(
215f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)            extension_prefs_, extension->id())) {
2162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      AddExtension(extension);
217b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)    } else {
2182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      RemoveExtension(extension);
219b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)    }
2205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else {
2215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    NOTREACHED() << "Received unexpected notification";
2225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)size_t ExtensionToolbarModel::FindNewPositionFromLastKnownGood(
2262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    const Extension* extension) {
2272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // See if we have last known good position for this extension.
2282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  size_t new_index = 0;
2292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Loop through the ID list of known positions, to count the number of visible
2302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // browser action icons preceding |extension|.
2312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  for (ExtensionIdList::const_iterator iter_id = last_known_positions_.begin();
2322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)       iter_id < last_known_positions_.end(); ++iter_id) {
2332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if ((*iter_id) == extension->id())
2342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      return new_index;  // We've found the right position.
2352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    // Found an id, need to see if it is visible.
2362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    for (ExtensionList::const_iterator iter_ext = toolbar_items_.begin();
2372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)         iter_ext < toolbar_items_.end(); ++iter_ext) {
2382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      if ((*iter_ext)->id().compare(*iter_id) == 0) {
2392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        // This extension is visible, update the index value.
2402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        ++new_index;
2412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        break;
2422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      }
2432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
2442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
2452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return -1;
2472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
2482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void ExtensionToolbarModel::AddExtension(const Extension* extension) {
2505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // We only care about extensions with browser actions.
251f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  if (!extensions::ExtensionActionManager::Get(profile_)->
2525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      GetBrowserAction(*extension)) {
2535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
2545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  size_t new_index = -1;
2572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // See if we have a last known good position for this extension.
2592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  ExtensionIdList::iterator last_pos = std::find(last_known_positions_.begin(),
2602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                                 last_known_positions_.end(),
2612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                                 extension->id());
2622a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (last_pos != last_known_positions_.end()) {
2632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    new_index = FindNewPositionFromLastKnownGood(extension);
2642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if (new_index != toolbar_items_.size()) {
2652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      toolbar_items_.insert(toolbar_items_.begin() + new_index,
2662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                            make_scoped_refptr(extension));
2672a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    } else {
2682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      toolbar_items_.push_back(make_scoped_refptr(extension));
2695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
2705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else {
2712a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    // This is a never before seen extension, that was added to the end. Make
2722a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    // sure to reflect that.
2732a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    toolbar_items_.push_back(make_scoped_refptr(extension));
2742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    last_known_positions_.push_back(extension->id());
2752a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    new_index = toolbar_items_.size() - 1;
2762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    UpdatePrefs();
2775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2792a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  FOR_EACH_OBSERVER(Observer, observers_,
2802a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                    BrowserActionAdded(extension, new_index));
2815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2832a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void ExtensionToolbarModel::RemoveExtension(const Extension* extension) {
2845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ExtensionList::iterator pos =
2852a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      std::find(toolbar_items_.begin(), toolbar_items_.end(), extension);
2862a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (pos == toolbar_items_.end())
2875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
2885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2892a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  toolbar_items_.erase(pos);
2902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  FOR_EACH_OBSERVER(Observer, observers_,
2912a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                    BrowserActionRemoved(extension));
2925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  UpdatePrefs();
2945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2962a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void ExtensionToolbarModel::UninstalledExtension(const Extension* extension) {
2972a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Remove the extension id from the ordered list, if it exists (the extension
2982a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // might not be represented in the list because it might not have an icon).
2992a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  ExtensionIdList::iterator pos =
3002a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      std::find(last_known_positions_.begin(),
3012a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                last_known_positions_.end(), extension->id());
3025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3032a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (pos != last_known_positions_.end()) {
3042a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    last_known_positions_.erase(pos);
3052a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    UpdatePrefs();
3062a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
3072a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
3085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Combine the currently enabled extensions that have browser actions (which
3105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// we get from the ExtensionService) with the ordering we get from the
3115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// pref service. For robustness we use a somewhat inefficient process:
3125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// 1. Create a vector of extensions sorted by their pref values. This vector may
3135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// have holes.
3145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// 2. Create a vector of extensions that did not have a pref value.
3155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// 3. Remove holes from the sorted vector and append the unsorted vector.
316f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)void ExtensionToolbarModel::InitializeExtensionList(ExtensionService* service) {
317f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  DCHECK(service->is_ready());
3185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
319f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  last_known_positions_ = extension_prefs_->GetToolbarOrder();
320f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  Populate(last_known_positions_, service);
3215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  extensions_initialized_ = true;
323d57369da7c6519fef57db42085f7b42d4c8845c1Torne (Richard Coles)  FOR_EACH_OBSERVER(Observer, observers_, VisibleCountChanged());
3245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
326c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)void ExtensionToolbarModel::Populate(
327f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    const extensions::ExtensionIdList& positions,
328f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    ExtensionService* service) {
329c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // Items that have explicit positions.
3305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ExtensionList sorted;
331c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  sorted.resize(positions.size(), NULL);
332c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // The items that don't have explicit positions.
3335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ExtensionList unsorted;
3345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  extensions::ExtensionActionManager* extension_action_manager =
336f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)      extensions::ExtensionActionManager::Get(profile_);
3375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Create the lists.
339f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  for (ExtensionSet::const_iterator it = service->extensions()->begin();
340f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)       it != service->extensions()->end(); ++it) {
3417d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)    const Extension* extension = it->get();
3425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (!extension_action_manager->GetBrowserAction(*extension))
3435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      continue;
344b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)    if (!extensions::ExtensionActionAPI::GetBrowserActionVisibility(
345f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)            extension_prefs_, extension->id())) {
3465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      continue;
347b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)    }
3485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    extensions::ExtensionIdList::const_iterator pos =
350c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        std::find(positions.begin(), positions.end(), extension->id());
351c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    if (pos != positions.end())
352c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      sorted[pos - positions.begin()] = extension;
3535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    else
3545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      unsorted.push_back(make_scoped_refptr(extension));
3555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
357c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // Erase current icons.
358c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  for (size_t i = 0; i < toolbar_items_.size(); i++) {
359868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)    FOR_EACH_OBSERVER(
360868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)        Observer, observers_, BrowserActionRemoved(toolbar_items_[i].get()));
361c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  }
3625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  toolbar_items_.clear();
363c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
364c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // Merge the lists.
3655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  toolbar_items_.reserve(sorted.size() + unsorted.size());
3665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (ExtensionList::const_iterator iter = sorted.begin();
3675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)       iter != sorted.end(); ++iter) {
3685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // It's possible for the extension order to contain items that aren't
3695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // actually loaded on this machine.  For example, when extension sync is on,
3705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // we sync the extension order as-is but double-check with the user before
3715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // syncing NPAPI-containing extensions, so if one of those is not actually
3725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // synced, we'll get a NULL in the list.  This sort of case can also happen
3735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // if some error prevents an extension from loading.
374868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)    if (iter->get() != NULL)
3755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      toolbar_items_.push_back(*iter);
3765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  toolbar_items_.insert(toolbar_items_.end(), unsorted.begin(),
3785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                        unsorted.end());
3795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Inform observers.
3815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (size_t i = 0; i < toolbar_items_.size(); i++) {
382868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)    FOR_EACH_OBSERVER(
383868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)        Observer, observers_, BrowserActionAdded(toolbar_items_[i].get(), i));
3845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void ExtensionToolbarModel::UpdatePrefs() {
388f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  if (!extension_prefs_)
3895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
3905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
391c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // Don't observe change caused by self.
392c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  pref_change_registrar_.Remove(prefs::kExtensionToolbar);
393f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  extension_prefs_->SetToolbarOrder(last_known_positions_);
394c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  pref_change_registrar_.Add(prefs::kExtensionToolbar, pref_change_callback_);
3955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)int ExtensionToolbarModel::IncognitoIndexToOriginal(int incognito_index) {
3985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int original_index = 0, i = 0;
399f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  ExtensionService* extension_service =
400f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)      extensions::ExtensionSystem::Get(profile_)->extension_service();
4015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (ExtensionList::iterator iter = toolbar_items_.begin();
4025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)       iter != toolbar_items_.end();
4035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)       ++iter, ++original_index) {
404f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    if (extension_util::IsIncognitoEnabled((*iter)->id(), extension_service)) {
4055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (incognito_index == i)
4065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        break;
4075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      ++i;
4085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
4095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
4105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return original_index;
4115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
4125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)int ExtensionToolbarModel::OriginalIndexToIncognito(int original_index) {
4145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int incognito_index = 0, i = 0;
415f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  ExtensionService* extension_service =
416f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)      extensions::ExtensionSystem::Get(profile_)->extension_service();
4175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (ExtensionList::iterator iter = toolbar_items_.begin();
4185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)       iter != toolbar_items_.end();
4195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)       ++iter, ++i) {
4205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (original_index == i)
4215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
422f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    if (extension_util::IsIncognitoEnabled((*iter)->id(), extension_service))
4235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      ++incognito_index;
4245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
4255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return incognito_index;
4265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
427c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
428c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)void ExtensionToolbarModel::OnExtensionToolbarPrefChange() {
429c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // If extensions are not ready, defer to later Populate() call.
430c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  if (!extensions_initialized_)
431c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    return;
432c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
433c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // Recalculate |last_known_positions_| to be |pref_positions| followed by
434c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // ones that are only in |last_known_positions_|.
435c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  extensions::ExtensionIdList pref_positions =
436f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)      extension_prefs_->GetToolbarOrder();
437c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  size_t pref_position_size = pref_positions.size();
438c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  for (size_t i = 0; i < last_known_positions_.size(); ++i) {
439c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    if (std::find(pref_positions.begin(), pref_positions.end(),
440c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                  last_known_positions_[i]) == pref_positions.end()) {
441c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      pref_positions.push_back(last_known_positions_[i]);
442c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    }
443c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  }
444c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  last_known_positions_.swap(pref_positions);
445c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
446c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // Re-populate.
447f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  Populate(last_known_positions_,
448f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)           extensions::ExtensionSystem::Get(profile_)->extension_service());
449c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
450c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  if (last_known_positions_.size() > pref_position_size) {
451c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    // Need to update pref because we have extra icons. But can't call
452c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    // UpdatePrefs() directly within observation closure.
453c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    base::MessageLoop::current()->PostTask(
454c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        FROM_HERE,
455c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        base::Bind(&ExtensionToolbarModel::UpdatePrefs,
456c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                   weak_ptr_factory_.GetWeakPtr()));
457c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  }
458c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)}
4598bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)
4608bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)bool ExtensionToolbarModel::ShowBrowserActionPopup(
4618bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)    const extensions::Extension* extension) {
4628bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)  ObserverListBase<Observer>::Iterator it(observers_);
4638bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)  Observer* obs = NULL;
4648bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)  while ((obs = it.GetNext()) != NULL) {
4658bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)    // Stop after first popup since it should only show in the active window.
4668bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)    if (obs->BrowserActionShowPopup(extension))
4678bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)      return true;
4688bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)  }
4698bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)  return false;
4708bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)}
471d57369da7c6519fef57db42085f7b42d4c8845c1Torne (Richard Coles)
472d57369da7c6519fef57db42085f7b42d4c8845c1Torne (Richard Coles)void ExtensionToolbarModel::EnsureVisibility(
473d57369da7c6519fef57db42085f7b42d4c8845c1Torne (Richard Coles)    const extensions::ExtensionIdList& extension_ids) {
474d57369da7c6519fef57db42085f7b42d4c8845c1Torne (Richard Coles)  if (visible_icon_count_ == -1)
475d57369da7c6519fef57db42085f7b42d4c8845c1Torne (Richard Coles)    return;  // Already showing all.
476d57369da7c6519fef57db42085f7b42d4c8845c1Torne (Richard Coles)
477d57369da7c6519fef57db42085f7b42d4c8845c1Torne (Richard Coles)  // Otherwise, make sure we have enough room to show all the extensions
478d57369da7c6519fef57db42085f7b42d4c8845c1Torne (Richard Coles)  // requested.
479d57369da7c6519fef57db42085f7b42d4c8845c1Torne (Richard Coles)  if (visible_icon_count_ < static_cast<int>(extension_ids.size())) {
480d57369da7c6519fef57db42085f7b42d4c8845c1Torne (Richard Coles)    SetVisibleIconCount(extension_ids.size());
481d57369da7c6519fef57db42085f7b42d4c8845c1Torne (Richard Coles)
482d57369da7c6519fef57db42085f7b42d4c8845c1Torne (Richard Coles)    // Inform observers.
483d57369da7c6519fef57db42085f7b42d4c8845c1Torne (Richard Coles)    FOR_EACH_OBSERVER(Observer, observers_, VisibleCountChanged());
484d57369da7c6519fef57db42085f7b42d4c8845c1Torne (Richard Coles)  }
485d57369da7c6519fef57db42085f7b42d4c8845c1Torne (Richard Coles)
486d57369da7c6519fef57db42085f7b42d4c8845c1Torne (Richard Coles)  if (visible_icon_count_ == -1)
487d57369da7c6519fef57db42085f7b42d4c8845c1Torne (Richard Coles)    return;  // May have been set to max by SetVisibleIconCount.
488d57369da7c6519fef57db42085f7b42d4c8845c1Torne (Richard Coles)
489d57369da7c6519fef57db42085f7b42d4c8845c1Torne (Richard Coles)  // Guillotine's Delight: Move an orange noble to the front of the line.
490d57369da7c6519fef57db42085f7b42d4c8845c1Torne (Richard Coles)  for (ExtensionIdList::const_iterator it = extension_ids.begin();
491d57369da7c6519fef57db42085f7b42d4c8845c1Torne (Richard Coles)       it != extension_ids.end(); ++it) {
492d57369da7c6519fef57db42085f7b42d4c8845c1Torne (Richard Coles)    for (ExtensionList::const_iterator extension = toolbar_items_.begin();
493d57369da7c6519fef57db42085f7b42d4c8845c1Torne (Richard Coles)         extension != toolbar_items_.end(); ++extension) {
494d57369da7c6519fef57db42085f7b42d4c8845c1Torne (Richard Coles)      if ((*extension)->id() == (*it)) {
495d57369da7c6519fef57db42085f7b42d4c8845c1Torne (Richard Coles)        if (extension - toolbar_items_.begin() >= visible_icon_count_)
496d57369da7c6519fef57db42085f7b42d4c8845c1Torne (Richard Coles)          MoveBrowserAction(*extension, 0);
497d57369da7c6519fef57db42085f7b42d4c8845c1Torne (Richard Coles)        break;
498d57369da7c6519fef57db42085f7b42d4c8845c1Torne (Richard Coles)      }
499d57369da7c6519fef57db42085f7b42d4c8845c1Torne (Richard Coles)    }
500d57369da7c6519fef57db42085f7b42d4c8845c1Torne (Richard Coles)  }
501d57369da7c6519fef57db42085f7b42d4c8845c1Torne (Richard Coles)}
502