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