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)
703b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)#include <algorithm>
88bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)#include <string>
98bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)
1003b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)#include "base/message_loop/message_loop.h"
115d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "base/metrics/histogram.h"
125d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "base/metrics/histogram_base.h"
132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "base/prefs/pref_service.h"
147dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch#include "chrome/browser/chrome_notification_types.h"
15b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)#include "chrome/browser/extensions/api/extension_action/extension_action_api.h"
165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/extensions/extension_action_manager.h"
175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/extensions/extension_tab_util.h"
18f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)#include "chrome/browser/extensions/extension_toolbar_model_factory.h"
198bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)#include "chrome/browser/extensions/extension_util.h"
205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/extensions/tab_helper.h"
215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/profiles/profile.h"
225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/ui/browser.h"
232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chrome/browser/ui/tabs/tab_strip_model.h"
245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/common/pref_names.h"
255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "content/public/browser/notification_details.h"
265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "content/public/browser/notification_source.h"
275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "content/public/browser/web_contents.h"
285d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "extensions/browser/extension_prefs.h"
295c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu#include "extensions/browser/extension_registry.h"
305d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "extensions/browser/extension_system.h"
315d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "extensions/browser/pref_names.h"
32f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)#include "extensions/common/extension.h"
335d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "extensions/common/extension_set.h"
34f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)#include "extensions/common/feature_switch.h"
35010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)#include "extensions/common/one_shot_event.h"
365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
37effb81e5f8246d0db0270817048dc992db66e9fbBen Murdochnamespace extensions {
385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
39010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)ExtensionToolbarModel::ExtensionToolbarModel(Profile* profile,
40010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)                                             ExtensionPrefs* extension_prefs)
41f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    : profile_(profile),
42f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)      extension_prefs_(extension_prefs),
43f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)      prefs_(profile_->GetPrefs()),
44c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      extensions_initialized_(false),
4503b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)      include_all_extensions_(
4603b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)          FeatureSwitch::extension_action_redesign()->IsEnabled()),
47effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch      is_highlighting_(false),
4803b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)      extension_action_observer_(this),
495c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu      extension_registry_observer_(this),
50c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      weak_ptr_factory_(this) {
51010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)  ExtensionSystem::Get(profile_)->ready().Post(
52010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)      FROM_HERE,
53010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)      base::Bind(&ExtensionToolbarModel::OnReady,
54010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)                 weak_ptr_factory_.GetWeakPtr()));
55010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)  visible_icon_count_ = prefs_->GetInteger(pref_names::kToolbarSize);
56c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  pref_change_registrar_.Init(prefs_);
57c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  pref_change_callback_ =
58c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      base::Bind(&ExtensionToolbarModel::OnExtensionToolbarPrefChange,
59c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                 base::Unretained(this));
60010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)  pref_change_registrar_.Add(pref_names::kToolbar, pref_change_callback_);
615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ExtensionToolbarModel::~ExtensionToolbarModel() {
645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
66f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)// static
67f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)ExtensionToolbarModel* ExtensionToolbarModel::Get(Profile* profile) {
68f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  return ExtensionToolbarModelFactory::GetForProfile(profile);
69f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)}
70f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void ExtensionToolbarModel::AddObserver(Observer* observer) {
725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  observers_.AddObserver(observer);
735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void ExtensionToolbarModel::RemoveObserver(Observer* observer) {
765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  observers_.RemoveObserver(observer);
775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7903b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)void ExtensionToolbarModel::MoveExtensionIcon(const Extension* extension,
805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                              int index) {
815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ExtensionList::iterator pos = std::find(toolbar_items_.begin(),
825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      toolbar_items_.end(), extension);
835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (pos == toolbar_items_.end()) {
845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    NOTREACHED();
855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  toolbar_items_.erase(pos);
885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
892a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  ExtensionIdList::iterator pos_id;
902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  pos_id = std::find(last_known_positions_.begin(),
912a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                     last_known_positions_.end(), extension->id());
922a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (pos_id != last_known_positions_.end())
932a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    last_known_positions_.erase(pos_id);
942a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int i = 0;
965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  bool inserted = false;
975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (ExtensionList::iterator iter = toolbar_items_.begin();
985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)       iter != toolbar_items_.end();
995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)       ++iter, ++i) {
1005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (i == index) {
1012a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      pos_id = std::find(last_known_positions_.begin(),
1022a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                         last_known_positions_.end(), (*iter)->id());
1032a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      last_known_positions_.insert(pos_id, extension->id());
1042a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      toolbar_items_.insert(iter, make_scoped_refptr(extension));
1065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      inserted = true;
1075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
1085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
1095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!inserted) {
1125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    DCHECK_EQ(index, static_cast<int>(toolbar_items_.size()));
1135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    index = toolbar_items_.size();
1145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    toolbar_items_.push_back(make_scoped_refptr(extension));
1162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    last_known_positions_.push_back(extension->id());
1175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
11903b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  FOR_EACH_OBSERVER(
12003b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)      Observer, observers_, ToolbarExtensionMoved(extension, index));
12103b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  MaybeUpdateVisibilityPref(extension, index);
1225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  UpdatePrefs();
1235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void ExtensionToolbarModel::SetVisibleIconCount(int count) {
1265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  visible_icon_count_ =
1275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      count == static_cast<int>(toolbar_items_.size()) ? -1 : count;
12803b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)
129effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  // Only set the prefs if we're not in highlight mode. Highlight mode is
130effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  // designed to be a transitory state, and should not persist across browser
131effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  // restarts (though it may be re-entered).
13203b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  if (!is_highlighting_) {
13303b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    // Additionally, if we are using the new toolbar, any icons which are in the
13403b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    // overflow menu are considered "hidden". But it so happens that the times
13503b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    // we are likely to call SetVisibleIconCount() are also those when we are
13603b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    // in flux. So wait for things to cool down before setting the prefs.
13703b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    base::MessageLoop::current()->PostTask(
13803b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)        FROM_HERE,
13903b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)        base::Bind(&ExtensionToolbarModel::MaybeUpdateVisibilityPrefs,
14003b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)                   weak_ptr_factory_.GetWeakPtr()));
141010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)    prefs_->SetInteger(pref_names::kToolbarSize, visible_icon_count_);
14203b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  }
14303b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)}
14403b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)
14503b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)void ExtensionToolbarModel::OnExtensionActionUpdated(
14603b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    ExtensionAction* extension_action,
14703b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    content::WebContents* web_contents,
14803b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    content::BrowserContext* browser_context) {
14903b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  const Extension* extension =
15003b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)      ExtensionRegistry::Get(profile_)->enabled_extensions().GetByID(
15103b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)          extension_action->extension_id());
15203b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  // Notify observers if the extension exists and is in the model.
15303b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  if (extension &&
15403b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)      std::find(toolbar_items_.begin(),
15503b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)                toolbar_items_.end(),
15603b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)                extension) != toolbar_items_.end()) {
15703b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    FOR_EACH_OBSERVER(Observer, observers_, ToolbarExtensionUpdated(extension));
15803b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  }
1595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1615c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liuvoid ExtensionToolbarModel::OnExtensionLoaded(
1625c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    content::BrowserContext* browser_context,
1635c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    const Extension* extension) {
1645c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  // We don't want to add the same extension twice. It may have already been
1655c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  // added by EXTENSION_BROWSER_ACTION_VISIBILITY_CHANGED below, if the user
1665c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  // hides the browser action and then disables and enables the extension.
1675c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  for (size_t i = 0; i < toolbar_items_.size(); i++) {
168f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    if (toolbar_items_[i].get() == extension)
1695c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu      return;
1705c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  }
17103b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)
17203b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  AddExtension(extension);
1735c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu}
1745c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu
1755c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liuvoid ExtensionToolbarModel::OnExtensionUnloaded(
1765c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    content::BrowserContext* browser_context,
1775c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    const Extension* extension,
1785c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    UnloadedExtensionInfo::Reason reason) {
1795c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  RemoveExtension(extension);
1805c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu}
1815c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu
182cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)void ExtensionToolbarModel::OnExtensionUninstalled(
183cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    content::BrowserContext* browser_context,
1845f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    const Extension* extension,
1855f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    extensions::UninstallReason reason) {
186cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  // Remove the extension id from the ordered list, if it exists (the extension
187cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  // might not be represented in the list because it might not have an icon).
188cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  ExtensionIdList::iterator pos =
189cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)      std::find(last_known_positions_.begin(),
190cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)                last_known_positions_.end(), extension->id());
191cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
192cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  if (pos != last_known_positions_.end()) {
193cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    last_known_positions_.erase(pos);
194cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    UpdatePrefs();
195cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  }
196cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)}
197cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
1985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void ExtensionToolbarModel::Observe(
1995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    int type,
2005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const content::NotificationSource& source,
2015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const content::NotificationDetails& details) {
20203b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  DCHECK_EQ(NOTIFICATION_EXTENSION_BROWSER_ACTION_VISIBILITY_CHANGED, type);
203cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  const Extension* extension =
204cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)      ExtensionRegistry::Get(profile_)->GetExtensionById(
205cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)          *content::Details<const std::string>(details).ptr(),
206cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)          ExtensionRegistry::EVERYTHING);
20703b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)
20803b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  bool visible = ExtensionActionAPI::GetBrowserActionVisibility(
20903b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)                     extension_prefs_, extension->id());
21003b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  // Hiding works differently with the new and old toolbars.
21103b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  if (include_all_extensions_) {
21203b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    int new_size = 0;
21303b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    int new_index = 0;
21403b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    if (visible) {
21503b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)      // If this action used to be hidden, we can't possible be showing all.
21603b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)      DCHECK_NE(-1, visible_icon_count_);
21703b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)      // Grow the bar by one and move the extension to the end of the visibles.
21803b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)      new_size = visible_icon_count_ + 1;
21903b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)      new_index = new_size - 1;
22003b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    } else {
22103b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)      // If we're hiding one, we must be showing at least one.
22203b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)      DCHECK_NE(visible_icon_count_, 0);
22303b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)      // Shrink the bar by one and move the extension to the beginning of the
22403b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)      // overflow menu.
22503b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)      new_size = visible_icon_count_ == -1 ?
22603b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)                     toolbar_items_.size() - 1 : visible_icon_count_ - 1;
22703b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)      new_index = new_size;
22803b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    }
22903b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    SetVisibleIconCount(new_size);
23003b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    MoveExtensionIcon(extension, new_index);
23103b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    FOR_EACH_OBSERVER(Observer, observers_, ToolbarVisibleCountChanged());
23203b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  } else {  // Don't include all extensions.
23303b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    if (visible)
23403b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)      AddExtension(extension);
23503b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    else
23603b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)      RemoveExtension(extension);
23703b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  }
2385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
240010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)void ExtensionToolbarModel::OnReady() {
241010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)  ExtensionRegistry* registry = ExtensionRegistry::Get(profile_);
242010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)  InitializeExtensionList(registry->enabled_extensions());
243010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)  // Wait until the extension system is ready before observing any further
244010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)  // changes so that the toolbar buttons can be shown in their stable ordering
245010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)  // taken from prefs.
246010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)  extension_registry_observer_.Add(registry);
24703b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  extension_action_observer_.Add(ExtensionActionAPI::Get(profile_));
248010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)  registrar_.Add(
249010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)      this,
2505f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      extensions::NOTIFICATION_EXTENSION_BROWSER_ACTION_VISIBILITY_CHANGED,
251010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)      content::Source<ExtensionPrefs>(extension_prefs_));
252010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)}
253010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)
2542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)size_t ExtensionToolbarModel::FindNewPositionFromLastKnownGood(
2552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    const Extension* extension) {
2562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // See if we have last known good position for this extension.
2572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  size_t new_index = 0;
2582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Loop through the ID list of known positions, to count the number of visible
25903b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  // extension icons preceding |extension|.
2602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  for (ExtensionIdList::const_iterator iter_id = last_known_positions_.begin();
2612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)       iter_id < last_known_positions_.end(); ++iter_id) {
2622a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if ((*iter_id) == extension->id())
2632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      return new_index;  // We've found the right position.
2642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    // Found an id, need to see if it is visible.
2652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    for (ExtensionList::const_iterator iter_ext = toolbar_items_.begin();
2662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)         iter_ext < toolbar_items_.end(); ++iter_ext) {
2672a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      if ((*iter_ext)->id().compare(*iter_id) == 0) {
2682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        // This extension is visible, update the index value.
2692a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        ++new_index;
2702a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        break;
2712a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      }
2722a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
2732a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
2742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
275116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  // Position not found.
276116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  return toolbar_items_.size();
2772a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
2782a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
27903b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)bool ExtensionToolbarModel::ShouldAddExtension(const Extension* extension) {
28003b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  ExtensionActionManager* action_manager =
28103b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)      ExtensionActionManager::Get(profile_);
28203b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  if (include_all_extensions_) {
28303b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    // In this case, we don't care about the browser action visibility, because
28403b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    // we want to show each extension regardless.
28503b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    // TODO(devlin): Extension actions which are not visible should be moved to
28603b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    // the overflow menu by default.
28703b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    return action_manager->GetBrowserAction(*extension) ||
28803b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)           action_manager->GetPageAction(*extension);
28903b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  }
29003b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)
29103b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  return action_manager->GetBrowserAction(*extension) &&
29203b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)         ExtensionActionAPI::GetBrowserActionVisibility(
29303b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)             extension_prefs_, extension->id());
29403b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)}
29503b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)
2962a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void ExtensionToolbarModel::AddExtension(const Extension* extension) {
29703b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  if (!ShouldAddExtension(extension))
2985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
2995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
300116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  size_t new_index = toolbar_items_.size();
3012a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3022a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // See if we have a last known good position for this extension.
3032a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  ExtensionIdList::iterator last_pos = std::find(last_known_positions_.begin(),
3042a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                                 last_known_positions_.end(),
3052a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                                 extension->id());
3062a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (last_pos != last_known_positions_.end()) {
3072a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    new_index = FindNewPositionFromLastKnownGood(extension);
3082a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if (new_index != toolbar_items_.size()) {
3092a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      toolbar_items_.insert(toolbar_items_.begin() + new_index,
3102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                            make_scoped_refptr(extension));
3112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    } else {
3122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      toolbar_items_.push_back(make_scoped_refptr(extension));
3135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
3145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else {
3152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    // This is a never before seen extension, that was added to the end. Make
316116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    // sure to reflect that. (|new_index| was set above.)
3172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    toolbar_items_.push_back(make_scoped_refptr(extension));
3182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    last_known_positions_.push_back(extension->id());
3192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    UpdatePrefs();
3205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
32203b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  MaybeUpdateVisibilityPref(extension, new_index);
32303b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)
324effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  // If we're currently highlighting, then even though we add a browser action
325effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  // to the full list (|toolbar_items_|, there won't be another *visible*
326effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  // browser action, which was what the observers care about.
327effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  if (!is_highlighting_) {
32803b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    FOR_EACH_OBSERVER(
32903b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)        Observer, observers_, ToolbarExtensionAdded(extension, new_index));
330effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  }
3315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void ExtensionToolbarModel::RemoveExtension(const Extension* extension) {
3345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ExtensionList::iterator pos =
3352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      std::find(toolbar_items_.begin(), toolbar_items_.end(), extension);
3362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (pos == toolbar_items_.end())
3375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
3385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  toolbar_items_.erase(pos);
340effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch
341effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  // If we're in highlight mode, we also have to remove the extension from
342effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  // the highlighted list.
343effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  if (is_highlighting_) {
344effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch    pos = std::find(highlighted_items_.begin(),
345effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch                    highlighted_items_.end(),
346effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch                    extension);
347effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch    if (pos != highlighted_items_.end()) {
348effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch      highlighted_items_.erase(pos);
34903b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)      FOR_EACH_OBSERVER(
35003b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)          Observer, observers_, ToolbarExtensionRemoved(extension));
351effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch      // If the highlighted list is now empty, we stop highlighting.
352effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch      if (highlighted_items_.empty())
353effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch        StopHighlighting();
354effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch    }
355effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  } else {
35603b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    FOR_EACH_OBSERVER(Observer, observers_, ToolbarExtensionRemoved(extension));
357effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  }
3585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  UpdatePrefs();
3605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Combine the currently enabled extensions that have browser actions (which
363010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)// we get from the ExtensionRegistry) with the ordering we get from the
3645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// pref service. For robustness we use a somewhat inefficient process:
3655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// 1. Create a vector of extensions sorted by their pref values. This vector may
3665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// have holes.
3675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// 2. Create a vector of extensions that did not have a pref value.
3685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// 3. Remove holes from the sorted vector and append the unsorted vector.
369010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)void ExtensionToolbarModel::InitializeExtensionList(
370010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)    const ExtensionSet& extensions) {
371f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  last_known_positions_ = extension_prefs_->GetToolbarOrder();
372010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)  Populate(last_known_positions_, extensions);
3735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  extensions_initialized_ = true;
37503b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  MaybeUpdateVisibilityPrefs();
37603b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  FOR_EACH_OBSERVER(Observer, observers_, ToolbarVisibleCountChanged());
3775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
379010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)void ExtensionToolbarModel::Populate(const ExtensionIdList& positions,
380010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)                                     const ExtensionSet& extensions) {
381c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // Items that have explicit positions.
3825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ExtensionList sorted;
383c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  sorted.resize(positions.size(), NULL);
384c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // The items that don't have explicit positions.
3855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ExtensionList unsorted;
3865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
387effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  ExtensionActionManager* extension_action_manager =
388effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch      ExtensionActionManager::Get(profile_);
3895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Create the lists.
391a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  int hidden = 0;
392010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)  for (ExtensionSet::const_iterator it = extensions.begin();
393010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)       it != extensions.end();
394010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)       ++it) {
3957d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)    const Extension* extension = it->get();
39603b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    if (!ShouldAddExtension(extension)) {
39703b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)      if (extension_action_manager->GetBrowserAction(*extension))
39803b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)        ++hidden;
3995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      continue;
400b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)    }
4015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
402effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch    ExtensionIdList::const_iterator pos =
403c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        std::find(positions.begin(), positions.end(), extension->id());
404c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    if (pos != positions.end())
405c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      sorted[pos - positions.begin()] = extension;
4065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    else
4075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      unsorted.push_back(make_scoped_refptr(extension));
4085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
4095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
410cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  size_t items_count = toolbar_items_.size();
411cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  for (size_t i = 0; i < items_count; i++) {
4121320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    const Extension* extension = toolbar_items_.back().get();
413cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    // By popping the extension here (before calling BrowserActionRemoved),
414cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    // we will not shrink visible count by one after BrowserActionRemoved
415cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    // calls SetVisibleCount.
416cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    toolbar_items_.pop_back();
417868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)    FOR_EACH_OBSERVER(
41803b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)        Observer, observers_, ToolbarExtensionRemoved(extension));
419c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  }
420cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  DCHECK(toolbar_items_.empty());
421c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
422c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // Merge the lists.
4235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  toolbar_items_.reserve(sorted.size() + unsorted.size());
424cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
4255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (ExtensionList::const_iterator iter = sorted.begin();
4265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)       iter != sorted.end(); ++iter) {
4275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // It's possible for the extension order to contain items that aren't
4285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // actually loaded on this machine.  For example, when extension sync is on,
4295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // we sync the extension order as-is but double-check with the user before
4305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // syncing NPAPI-containing extensions, so if one of those is not actually
4315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // synced, we'll get a NULL in the list.  This sort of case can also happen
4325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // if some error prevents an extension from loading.
433cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    if (iter->get() != NULL) {
434cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)      toolbar_items_.push_back(*iter);
435cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)      FOR_EACH_OBSERVER(
4361320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci          Observer,
4371320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci          observers_,
4381320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci          ToolbarExtensionAdded(iter->get(), toolbar_items_.size() - 1));
439cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    }
440cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  }
441cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  for (ExtensionList::const_iterator iter = unsorted.begin();
442cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)       iter != unsorted.end(); ++iter) {
443cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    if (iter->get() != NULL) {
4445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      toolbar_items_.push_back(*iter);
445cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)      FOR_EACH_OBSERVER(
4461320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci          Observer,
4471320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci          observers_,
4481320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci          ToolbarExtensionAdded(iter->get(), toolbar_items_.size() - 1));
449cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    }
4505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
4515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
452a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  UMA_HISTOGRAM_COUNTS_100(
453a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      "ExtensionToolbarModel.BrowserActionsPermanentlyHidden", hidden);
4545d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  UMA_HISTOGRAM_COUNTS_100("ExtensionToolbarModel.BrowserActionsCount",
4555d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                           toolbar_items_.size());
4565d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
4575d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  if (!toolbar_items_.empty()) {
4585d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    // Visible count can be -1, meaning: 'show all'. Since UMA converts negative
4595d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    // values to 0, this would be counted as 'show none' unless we convert it to
4605d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    // max.
4615d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    UMA_HISTOGRAM_COUNTS_100("ExtensionToolbarModel.BrowserActionsVisible",
4625d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                             visible_icon_count_ == -1 ?
4635d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                                 base::HistogramBase::kSampleType_MAX :
4645d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                                 visible_icon_count_);
4655d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  }
4665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
4675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void ExtensionToolbarModel::UpdatePrefs() {
469f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  if (!extension_prefs_)
4705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
4715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
472c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // Don't observe change caused by self.
473effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  pref_change_registrar_.Remove(pref_names::kToolbar);
474f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  extension_prefs_->SetToolbarOrder(last_known_positions_);
475effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  pref_change_registrar_.Add(pref_names::kToolbar, pref_change_callback_);
4765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
4775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
47803b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)void ExtensionToolbarModel::MaybeUpdateVisibilityPref(
47903b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    const Extension* extension, int index) {
48003b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  // We only update the visibility pref for hidden/not hidden based on the
48103b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  // overflow menu with the new toolbar design.
48203b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  if (include_all_extensions_) {
48303b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    bool visible = index < visible_icon_count_ || visible_icon_count_ == -1;
48403b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    if (visible != ExtensionActionAPI::GetBrowserActionVisibility(
48503b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)                       extension_prefs_, extension->id())) {
48603b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)      // Don't observe changes caused by ourselves.
48703b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)      bool was_registered = false;
48803b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)      if (registrar_.IsRegistered(
48903b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)              this,
49003b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)              NOTIFICATION_EXTENSION_BROWSER_ACTION_VISIBILITY_CHANGED,
49103b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)              content::Source<ExtensionPrefs>(extension_prefs_))) {
49203b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)        was_registered = true;
49303b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)        registrar_.Remove(
49403b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)            this,
49503b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)            NOTIFICATION_EXTENSION_BROWSER_ACTION_VISIBILITY_CHANGED,
49603b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)            content::Source<ExtensionPrefs>(extension_prefs_));
49703b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)      }
49803b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)      ExtensionActionAPI::SetBrowserActionVisibility(
49903b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)          extension_prefs_, extension->id(), visible);
50003b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)      if (was_registered) {
50103b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)        registrar_.Add(this,
50203b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)                       NOTIFICATION_EXTENSION_BROWSER_ACTION_VISIBILITY_CHANGED,
50303b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)                       content::Source<ExtensionPrefs>(extension_prefs_));
50403b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)      }
50503b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    }
50603b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  }
50703b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)}
50803b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)
50903b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)void ExtensionToolbarModel::MaybeUpdateVisibilityPrefs() {
51003b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  for (size_t i = 0u; i < toolbar_items_.size(); ++i)
5111320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    MaybeUpdateVisibilityPref(toolbar_items_[i].get(), i);
51203b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)}
51303b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)
5145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)int ExtensionToolbarModel::IncognitoIndexToOriginal(int incognito_index) {
5155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int original_index = 0, i = 0;
5165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (ExtensionList::iterator iter = toolbar_items_.begin();
5175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)       iter != toolbar_items_.end();
5185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)       ++iter, ++original_index) {
519effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch    if (util::IsIncognitoEnabled((*iter)->id(), profile_)) {
5205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (incognito_index == i)
5215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        break;
5225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      ++i;
5235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
5245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
5255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return original_index;
5265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
5275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)int ExtensionToolbarModel::OriginalIndexToIncognito(int original_index) {
5295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int incognito_index = 0, i = 0;
5305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (ExtensionList::iterator iter = toolbar_items_.begin();
5315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)       iter != toolbar_items_.end();
5325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)       ++iter, ++i) {
5335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (original_index == i)
5345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
535effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch    if (util::IsIncognitoEnabled((*iter)->id(), profile_))
5365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      ++incognito_index;
5375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
5385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return incognito_index;
5395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
540c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
541c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)void ExtensionToolbarModel::OnExtensionToolbarPrefChange() {
542c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // If extensions are not ready, defer to later Populate() call.
543c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  if (!extensions_initialized_)
544c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    return;
545c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
546c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // Recalculate |last_known_positions_| to be |pref_positions| followed by
547c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // ones that are only in |last_known_positions_|.
548effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  ExtensionIdList pref_positions = extension_prefs_->GetToolbarOrder();
549c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  size_t pref_position_size = pref_positions.size();
550c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  for (size_t i = 0; i < last_known_positions_.size(); ++i) {
551c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    if (std::find(pref_positions.begin(), pref_positions.end(),
552c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                  last_known_positions_[i]) == pref_positions.end()) {
553c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      pref_positions.push_back(last_known_positions_[i]);
554c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    }
555c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  }
556c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  last_known_positions_.swap(pref_positions);
557c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
558c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // Re-populate.
559f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  Populate(last_known_positions_,
560010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)           ExtensionRegistry::Get(profile_)->enabled_extensions());
561c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
562c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  if (last_known_positions_.size() > pref_position_size) {
563c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    // Need to update pref because we have extra icons. But can't call
564c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    // UpdatePrefs() directly within observation closure.
565c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    base::MessageLoop::current()->PostTask(
566c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        FROM_HERE,
567c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        base::Bind(&ExtensionToolbarModel::UpdatePrefs,
568c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                   weak_ptr_factory_.GetWeakPtr()));
569c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  }
570c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)}
5718bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)
5721320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tuccibool ExtensionToolbarModel::ShowExtensionActionPopup(
5731320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    const Extension* extension,
5741320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    Browser* browser,
5751320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    bool grant_active_tab) {
5768bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)  ObserverListBase<Observer>::Iterator it(observers_);
5778bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)  Observer* obs = NULL;
5781320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  // Look for the Observer associated with the browser.
5791320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  // This would be cleaner if we had an abstract class for the Toolbar UI
5801320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  // (like we do for LocationBar), but sadly, we don't.
5818bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)  while ((obs = it.GetNext()) != NULL) {
5821320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    if (obs->GetBrowser() == browser)
5831320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci      return obs->ShowExtensionActionPopup(extension, grant_active_tab);
5848bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)  }
5858bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)  return false;
5868bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)}
5875d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
5885d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)void ExtensionToolbarModel::EnsureVisibility(
589effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch    const ExtensionIdList& extension_ids) {
5905d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  if (visible_icon_count_ == -1)
5915d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    return;  // Already showing all.
5925d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
5935d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  // Otherwise, make sure we have enough room to show all the extensions
5945d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  // requested.
5955d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  if (visible_icon_count_ < static_cast<int>(extension_ids.size())) {
5965d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    SetVisibleIconCount(extension_ids.size());
5975d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
5985d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    // Inform observers.
59903b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    FOR_EACH_OBSERVER(Observer, observers_, ToolbarVisibleCountChanged());
6005d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  }
6015d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
6025d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  if (visible_icon_count_ == -1)
6035d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    return;  // May have been set to max by SetVisibleIconCount.
6045d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
6055d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  // Guillotine's Delight: Move an orange noble to the front of the line.
6065d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  for (ExtensionIdList::const_iterator it = extension_ids.begin();
6075d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)       it != extension_ids.end(); ++it) {
6085d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    for (ExtensionList::const_iterator extension = toolbar_items_.begin();
6095d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)         extension != toolbar_items_.end(); ++extension) {
6105d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      if ((*extension)->id() == (*it)) {
6115d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        if (extension - toolbar_items_.begin() >= visible_icon_count_)
6121320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci          MoveExtensionIcon(extension->get(), 0);
6135d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        break;
6145d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      }
6155d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    }
6165d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  }
6175d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}
618effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch
619effb81e5f8246d0db0270817048dc992db66e9fbBen Murdochbool ExtensionToolbarModel::HighlightExtensions(
620effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch    const ExtensionIdList& extension_ids) {
621effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  highlighted_items_.clear();
622effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch
623effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  for (ExtensionIdList::const_iterator id = extension_ids.begin();
624effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch       id != extension_ids.end();
625effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch       ++id) {
626effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch    for (ExtensionList::const_iterator extension = toolbar_items_.begin();
627effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch         extension != toolbar_items_.end();
628effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch         ++extension) {
629effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch      if (*id == (*extension)->id())
630effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch        highlighted_items_.push_back(*extension);
631effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch    }
632effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  }
633effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch
634effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  // If we have any items in |highlighted_items_|, then we entered highlighting
635effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  // mode.
636effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  if (highlighted_items_.size()) {
637effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch    old_visible_icon_count_ = visible_icon_count_;
638effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch    is_highlighting_ = true;
639effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch    if (visible_icon_count_ != -1 &&
640effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch        visible_icon_count_ < static_cast<int>(extension_ids.size())) {
641effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch      SetVisibleIconCount(extension_ids.size());
64203b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)      FOR_EACH_OBSERVER(Observer, observers_, ToolbarVisibleCountChanged());
643effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch    }
644effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch
64503b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    FOR_EACH_OBSERVER(Observer, observers_, ToolbarHighlightModeChanged(true));
646effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch    return true;
647effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  }
648effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch
649effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  // Otherwise, we didn't enter highlighting mode (and, in fact, exited it if
650effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  // we were otherwise in it).
651effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  if (is_highlighting_)
652effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch    StopHighlighting();
653effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  return false;
654effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch}
655effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch
656effb81e5f8246d0db0270817048dc992db66e9fbBen Murdochvoid ExtensionToolbarModel::StopHighlighting() {
657effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  if (is_highlighting_) {
658effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch    highlighted_items_.clear();
659effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch    is_highlighting_ = false;
660effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch    if (old_visible_icon_count_ != visible_icon_count_) {
661effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch      SetVisibleIconCount(old_visible_icon_count_);
66203b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)      FOR_EACH_OBSERVER(Observer, observers_, ToolbarVisibleCountChanged());
663effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch    }
66403b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    FOR_EACH_OBSERVER(Observer, observers_, ToolbarHighlightModeChanged(false));
665effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  }
666cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)}
667effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch
66803b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)void ExtensionToolbarModel::SetVisibleIconCountForTest(size_t visible_icons) {
66903b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  SetVisibleIconCount(visible_icons);
67003b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  FOR_EACH_OBSERVER(Observer, observers_, ToolbarVisibleCountChanged());
67103b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)}
67203b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)
673effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch}  // namespace extensions
674