extension_toolbar_model.cc revision 3345a6884c488ff3a535c2c9acdd33d74b37e311
1// Copyright (c) 2010 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "chrome/browser/extensions/extension_toolbar_model.h"
6
7#include "chrome/browser/extensions/extension_prefs.h"
8#include "chrome/browser/extensions/extensions_service.h"
9#include "chrome/browser/prefs/pref_service.h"
10#include "chrome/browser/profile.h"
11#include "chrome/common/extensions/extension.h"
12#include "chrome/common/notification_service.h"
13#include "chrome/common/pref_names.h"
14
15ExtensionToolbarModel::ExtensionToolbarModel(ExtensionsService* service)
16    : service_(service),
17      prefs_(service->profile()->GetPrefs()),
18      extensions_initialized_(false) {
19  DCHECK(service_);
20
21  registrar_.Add(this, NotificationType::EXTENSION_LOADED,
22                 Source<Profile>(service_->profile()));
23  registrar_.Add(this, NotificationType::EXTENSION_UNLOADED,
24                 Source<Profile>(service_->profile()));
25  registrar_.Add(this, NotificationType::EXTENSION_UNLOADED_DISABLED,
26                 Source<Profile>(service_->profile()));
27  registrar_.Add(this, NotificationType::EXTENSIONS_READY,
28                 Source<Profile>(service_->profile()));
29
30  visible_icon_count_ = prefs_->GetInteger(prefs::kExtensionToolbarSize);
31}
32
33ExtensionToolbarModel::~ExtensionToolbarModel() {
34}
35
36void ExtensionToolbarModel::DestroyingProfile() {
37  registrar_.RemoveAll();
38}
39
40void ExtensionToolbarModel::AddObserver(Observer* observer) {
41  observers_.AddObserver(observer);
42}
43
44void ExtensionToolbarModel::RemoveObserver(Observer* observer) {
45  observers_.RemoveObserver(observer);
46}
47
48void ExtensionToolbarModel::MoveBrowserAction(Extension* extension,
49                                              int index) {
50  ExtensionList::iterator pos = std::find(begin(), end(), extension);
51  if (pos == end()) {
52    NOTREACHED();
53    return;
54  }
55  toolitems_.erase(pos);
56
57  int i = 0;
58  bool inserted = false;
59  for (ExtensionList::iterator iter = begin(); iter != end(); ++iter, ++i) {
60    if (i == index) {
61      toolitems_.insert(iter, extension);
62      inserted = true;
63      break;
64    }
65  }
66
67  if (!inserted) {
68    DCHECK_EQ(index, static_cast<int>(toolitems_.size()));
69    index = toolitems_.size();
70
71    toolitems_.push_back(extension);
72  }
73
74  FOR_EACH_OBSERVER(Observer, observers_, BrowserActionMoved(extension, index));
75
76  UpdatePrefs();
77}
78
79void ExtensionToolbarModel::SetVisibleIconCount(int count) {
80  visible_icon_count_ = count == static_cast<int>(size()) ? -1 : count;
81  prefs_->SetInteger(prefs::kExtensionToolbarSize, visible_icon_count_);
82}
83
84void ExtensionToolbarModel::Observe(NotificationType type,
85                                    const NotificationSource& source,
86                                    const NotificationDetails& details) {
87  if (type == NotificationType::EXTENSIONS_READY) {
88    InitializeExtensionList();
89    return;
90  }
91
92  if (!service_->is_ready())
93    return;
94
95  Extension* extension = Details<Extension>(details).ptr();
96  if (type == NotificationType::EXTENSION_LOADED) {
97    AddExtension(extension);
98  } else if (type == NotificationType::EXTENSION_UNLOADED ||
99             type == NotificationType::EXTENSION_UNLOADED_DISABLED) {
100    RemoveExtension(extension);
101  } else {
102    NOTREACHED() << "Received unexpected notification";
103  }
104}
105
106void ExtensionToolbarModel::AddExtension(Extension* extension) {
107  // We only care about extensions with browser actions.
108  if (!extension->browser_action())
109    return;
110
111  if (extension->id() == last_extension_removed_ &&
112      last_extension_removed_index_ < toolitems_.size()) {
113    toolitems_.insert(begin() + last_extension_removed_index_, extension);
114    FOR_EACH_OBSERVER(Observer, observers_,
115        BrowserActionAdded(extension, last_extension_removed_index_));
116  } else {
117    toolitems_.push_back(extension);
118    FOR_EACH_OBSERVER(Observer, observers_,
119                      BrowserActionAdded(extension, toolitems_.size() - 1));
120  }
121
122  last_extension_removed_ = "";
123  last_extension_removed_index_ = -1;
124
125  UpdatePrefs();
126}
127
128void ExtensionToolbarModel::RemoveExtension(Extension* extension) {
129  ExtensionList::iterator pos = std::find(begin(), end(), extension);
130  if (pos == end()) {
131    return;
132  }
133
134  last_extension_removed_ = extension->id();
135  last_extension_removed_index_ = pos - begin();
136
137  toolitems_.erase(pos);
138  FOR_EACH_OBSERVER(Observer, observers_,
139                    BrowserActionRemoved(extension));
140
141  UpdatePrefs();
142}
143
144// Combine the currently enabled extensions that have browser actions (which
145// we get from the ExtensionsService) with the ordering we get from the
146// pref service. For robustness we use a somewhat inefficient process:
147// 1. Create a vector of extensions sorted by their pref values. This vector may
148// have holes.
149// 2. Create a vector of extensions that did not have a pref value.
150// 3. Remove holes from the sorted vector and append the unsorted vector.
151void ExtensionToolbarModel::InitializeExtensionList() {
152  DCHECK(service_->is_ready());
153
154  std::vector<std::string> pref_order = service_->extension_prefs()->
155      GetToolbarOrder();
156  // Items that have a pref for their position.
157  ExtensionList sorted;
158  sorted.resize(pref_order.size(), NULL);
159  // The items that don't have a pref for their position.
160  ExtensionList unsorted;
161
162  // Create the lists.
163  for (size_t i = 0; i < service_->extensions()->size(); ++i) {
164    Extension* extension = service_->extensions()->at(i);
165    if (!extension->browser_action())
166      continue;
167
168    std::vector<std::string>::iterator pos =
169        std::find(pref_order.begin(), pref_order.end(), extension->id());
170    if (pos != pref_order.end()) {
171      int index = std::distance(pref_order.begin(), pos);
172      sorted[index] = extension;
173    } else {
174      unsorted.push_back(extension);
175    }
176  }
177
178  // Merge the lists.
179  toolitems_.reserve(sorted.size() + unsorted.size());
180  for (ExtensionList::iterator iter = sorted.begin();
181       iter != sorted.end(); ++iter) {
182    if (*iter != NULL)
183      toolitems_.push_back(*iter);
184  }
185  toolitems_.insert(toolitems_.end(), unsorted.begin(), unsorted.end());
186
187  // Inform observers.
188  for (size_t i = 0; i < toolitems_.size(); i++) {
189    FOR_EACH_OBSERVER(Observer, observers_,
190                      BrowserActionAdded(toolitems_[i], i));
191  }
192
193  UpdatePrefs();
194
195  extensions_initialized_ = true;
196  FOR_EACH_OBSERVER(Observer, observers_, ModelLoaded());
197}
198
199void ExtensionToolbarModel::UpdatePrefs() {
200  if (!service_->extension_prefs())
201    return;
202
203  std::vector<std::string> ids;
204  ids.reserve(toolitems_.size());
205  for (ExtensionList::iterator iter = begin(); iter != end(); ++iter)
206    ids.push_back((*iter)->id());
207  service_->extension_prefs()->SetToolbarOrder(ids);
208}
209
210Extension* ExtensionToolbarModel::GetExtensionByIndex(int index) const {
211  return toolitems_.at(index);
212}
213
214int ExtensionToolbarModel::IncognitoIndexToOriginal(int incognito_index) {
215  int original_index = 0, i = 0;
216  for (ExtensionList::iterator iter = begin(); iter != end();
217       ++iter, ++original_index) {
218    if (service_->IsIncognitoEnabled(*iter)) {
219      if (incognito_index == i)
220        break;
221      ++i;
222    }
223  }
224  return original_index;
225}
226
227int ExtensionToolbarModel::OriginalIndexToIncognito(int original_index) {
228  int incognito_index = 0, i = 0;
229  for (ExtensionList::iterator iter = begin(); iter != end();
230       ++iter, ++i) {
231    if (original_index == i)
232      break;
233    if (service_->IsIncognitoEnabled(*iter))
234      ++incognito_index;
235  }
236  return incognito_index;
237}
238