Rename desktop_cursor_loader_updater_aurax11.
[chromium-blink-merge.git] / chrome / browser / extensions / extension_toolbar_model.cc
bloba3a61e6d0fef3feef5e5d46e91118a0a39bb26dc
1 // Copyright (c) 2012 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.
5 #include "chrome/browser/extensions/extension_toolbar_model.h"
7 #include <string>
9 #include "base/prefs/pref_service.h"
10 #include "chrome/browser/chrome_notification_types.h"
11 #include "chrome/browser/extensions/api/extension_action/extension_action_api.h"
12 #include "chrome/browser/extensions/extension_action.h"
13 #include "chrome/browser/extensions/extension_action_manager.h"
14 #include "chrome/browser/extensions/extension_prefs.h"
15 #include "chrome/browser/extensions/extension_service.h"
16 #include "chrome/browser/extensions/extension_tab_util.h"
17 #include "chrome/browser/extensions/extension_util.h"
18 #include "chrome/browser/extensions/tab_helper.h"
19 #include "chrome/browser/profiles/profile.h"
20 #include "chrome/browser/ui/browser.h"
21 #include "chrome/browser/ui/tabs/tab_strip_model.h"
22 #include "chrome/common/extensions/extension.h"
23 #include "chrome/common/extensions/feature_switch.h"
24 #include "chrome/common/pref_names.h"
25 #include "content/public/browser/notification_details.h"
26 #include "content/public/browser/notification_source.h"
27 #include "content/public/browser/web_contents.h"
29 using extensions::Extension;
30 using extensions::ExtensionIdList;
31 using extensions::ExtensionList;
33 namespace {
35 // Returns true if an |extension| is in an |extension_list|.
36 bool IsInExtensionList(const Extension* extension,
37 const extensions::ExtensionList& extension_list) {
38 for (size_t i = 0; i < extension_list.size(); i++) {
39 if (extension_list[i].get() == extension)
40 return true;
42 return false;
45 } // namespace
47 bool ExtensionToolbarModel::Observer::BrowserActionShowPopup(
48 const extensions::Extension* extension) {
49 return false;
52 ExtensionToolbarModel::ExtensionToolbarModel(ExtensionService* service)
53 : service_(service),
54 prefs_(service->profile()->GetPrefs()),
55 extensions_initialized_(false),
56 weak_ptr_factory_(this) {
57 DCHECK(service_);
59 registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_LOADED,
60 content::Source<Profile>(service_->profile()));
61 registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_UNLOADED,
62 content::Source<Profile>(service_->profile()));
63 registrar_.Add(this, chrome::NOTIFICATION_EXTENSIONS_READY,
64 content::Source<Profile>(service_->profile()));
65 registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_UNINSTALLED,
66 content::Source<Profile>(service_->profile()));
67 registrar_.Add(
68 this, chrome::NOTIFICATION_EXTENSION_BROWSER_ACTION_VISIBILITY_CHANGED,
69 content::Source<extensions::ExtensionPrefs>(service_->extension_prefs()));
71 visible_icon_count_ = prefs_->GetInteger(prefs::kExtensionToolbarSize);
73 pref_change_registrar_.Init(prefs_);
74 pref_change_callback_ =
75 base::Bind(&ExtensionToolbarModel::OnExtensionToolbarPrefChange,
76 base::Unretained(this));
77 pref_change_registrar_.Add(prefs::kExtensionToolbar, pref_change_callback_);
80 ExtensionToolbarModel::~ExtensionToolbarModel() {
83 void ExtensionToolbarModel::AddObserver(Observer* observer) {
84 observers_.AddObserver(observer);
87 void ExtensionToolbarModel::RemoveObserver(Observer* observer) {
88 observers_.RemoveObserver(observer);
91 void ExtensionToolbarModel::MoveBrowserAction(const Extension* extension,
92 int index) {
93 ExtensionList::iterator pos = std::find(toolbar_items_.begin(),
94 toolbar_items_.end(), extension);
95 if (pos == toolbar_items_.end()) {
96 NOTREACHED();
97 return;
99 toolbar_items_.erase(pos);
101 ExtensionIdList::iterator pos_id;
102 pos_id = std::find(last_known_positions_.begin(),
103 last_known_positions_.end(), extension->id());
104 if (pos_id != last_known_positions_.end())
105 last_known_positions_.erase(pos_id);
107 int i = 0;
108 bool inserted = false;
109 for (ExtensionList::iterator iter = toolbar_items_.begin();
110 iter != toolbar_items_.end();
111 ++iter, ++i) {
112 if (i == index) {
113 pos_id = std::find(last_known_positions_.begin(),
114 last_known_positions_.end(), (*iter)->id());
115 last_known_positions_.insert(pos_id, extension->id());
117 toolbar_items_.insert(iter, make_scoped_refptr(extension));
118 inserted = true;
119 break;
123 if (!inserted) {
124 DCHECK_EQ(index, static_cast<int>(toolbar_items_.size()));
125 index = toolbar_items_.size();
127 toolbar_items_.push_back(make_scoped_refptr(extension));
128 last_known_positions_.push_back(extension->id());
131 FOR_EACH_OBSERVER(Observer, observers_, BrowserActionMoved(extension, index));
133 UpdatePrefs();
136 ExtensionToolbarModel::Action ExtensionToolbarModel::ExecuteBrowserAction(
137 const Extension* extension,
138 Browser* browser,
139 GURL* popup_url_out,
140 bool should_grant) {
141 content::WebContents* web_contents = NULL;
142 int tab_id = 0;
143 if (!ExtensionTabUtil::GetDefaultTab(browser, &web_contents, &tab_id))
144 return ACTION_NONE;
146 ExtensionAction* browser_action =
147 extensions::ExtensionActionManager::Get(service_->profile())->
148 GetBrowserAction(*extension);
150 // For browser actions, visibility == enabledness.
151 if (!browser_action->GetIsVisible(tab_id))
152 return ACTION_NONE;
154 if (should_grant) {
155 extensions::TabHelper::FromWebContents(web_contents)->
156 active_tab_permission_granter()->GrantIfRequested(extension);
159 if (browser_action->HasPopup(tab_id)) {
160 if (popup_url_out)
161 *popup_url_out = browser_action->GetPopupUrl(tab_id);
162 return ACTION_SHOW_POPUP;
165 extensions::ExtensionActionAPI::BrowserActionExecuted(
166 service_->profile(), *browser_action, web_contents);
167 return ACTION_NONE;
170 void ExtensionToolbarModel::SetVisibleIconCount(int count) {
171 visible_icon_count_ =
172 count == static_cast<int>(toolbar_items_.size()) ? -1 : count;
173 prefs_->SetInteger(prefs::kExtensionToolbarSize, visible_icon_count_);
176 void ExtensionToolbarModel::Observe(
177 int type,
178 const content::NotificationSource& source,
179 const content::NotificationDetails& details) {
180 if (type == chrome::NOTIFICATION_EXTENSIONS_READY) {
181 InitializeExtensionList();
182 return;
185 if (!service_->is_ready())
186 return;
188 const Extension* extension = NULL;
189 if (type == chrome::NOTIFICATION_EXTENSION_UNLOADED) {
190 extension = content::Details<extensions::UnloadedExtensionInfo>(
191 details)->extension;
192 } else if (type ==
193 chrome::NOTIFICATION_EXTENSION_BROWSER_ACTION_VISIBILITY_CHANGED) {
194 extension = service_->GetExtensionById(
195 *content::Details<const std::string>(details).ptr(), true);
196 } else {
197 extension = content::Details<const Extension>(details).ptr();
199 if (type == chrome::NOTIFICATION_EXTENSION_LOADED) {
200 // We don't want to add the same extension twice. It may have already been
201 // added by EXTENSION_BROWSER_ACTION_VISIBILITY_CHANGED below, if the user
202 // hides the browser action and then disables and enables the extension.
203 for (size_t i = 0; i < toolbar_items_.size(); i++) {
204 if (toolbar_items_[i].get() == extension)
205 return; // Already exists.
207 if (extensions::ExtensionActionAPI::GetBrowserActionVisibility(
208 service_->extension_prefs(), extension->id())) {
209 AddExtension(extension);
211 } else if (type == chrome::NOTIFICATION_EXTENSION_UNLOADED) {
212 RemoveExtension(extension);
213 } else if (type == chrome::NOTIFICATION_EXTENSION_UNINSTALLED) {
214 UninstalledExtension(extension);
215 } else if (type ==
216 chrome::NOTIFICATION_EXTENSION_BROWSER_ACTION_VISIBILITY_CHANGED) {
217 if (extensions::ExtensionActionAPI::GetBrowserActionVisibility(
218 service_->extension_prefs(), extension->id())) {
219 AddExtension(extension);
220 } else {
221 RemoveExtension(extension);
223 } else {
224 NOTREACHED() << "Received unexpected notification";
228 size_t ExtensionToolbarModel::FindNewPositionFromLastKnownGood(
229 const Extension* extension) {
230 // See if we have last known good position for this extension.
231 size_t new_index = 0;
232 // Loop through the ID list of known positions, to count the number of visible
233 // browser action icons preceding |extension|.
234 for (ExtensionIdList::const_iterator iter_id = last_known_positions_.begin();
235 iter_id < last_known_positions_.end(); ++iter_id) {
236 if ((*iter_id) == extension->id())
237 return new_index; // We've found the right position.
238 // Found an id, need to see if it is visible.
239 for (ExtensionList::const_iterator iter_ext = toolbar_items_.begin();
240 iter_ext < toolbar_items_.end(); ++iter_ext) {
241 if ((*iter_ext)->id().compare(*iter_id) == 0) {
242 // This extension is visible, update the index value.
243 ++new_index;
244 break;
249 return -1;
252 void ExtensionToolbarModel::AddExtension(const Extension* extension) {
253 // We only care about extensions with browser actions.
254 if (!extensions::ExtensionActionManager::Get(service_->profile())->
255 GetBrowserAction(*extension)) {
256 return;
259 size_t new_index = -1;
261 // See if we have a last known good position for this extension.
262 ExtensionIdList::iterator last_pos = std::find(last_known_positions_.begin(),
263 last_known_positions_.end(),
264 extension->id());
265 if (last_pos != last_known_positions_.end()) {
266 new_index = FindNewPositionFromLastKnownGood(extension);
267 if (new_index != toolbar_items_.size()) {
268 toolbar_items_.insert(toolbar_items_.begin() + new_index,
269 make_scoped_refptr(extension));
270 } else {
271 toolbar_items_.push_back(make_scoped_refptr(extension));
273 } else {
274 // This is a never before seen extension, that was added to the end. Make
275 // sure to reflect that.
276 toolbar_items_.push_back(make_scoped_refptr(extension));
277 last_known_positions_.push_back(extension->id());
278 new_index = toolbar_items_.size() - 1;
279 UpdatePrefs();
282 FOR_EACH_OBSERVER(Observer, observers_,
283 BrowserActionAdded(extension, new_index));
286 void ExtensionToolbarModel::RemoveExtension(const Extension* extension) {
287 ExtensionList::iterator pos =
288 std::find(toolbar_items_.begin(), toolbar_items_.end(), extension);
289 if (pos == toolbar_items_.end())
290 return;
292 toolbar_items_.erase(pos);
293 FOR_EACH_OBSERVER(Observer, observers_,
294 BrowserActionRemoved(extension));
296 UpdatePrefs();
299 void ExtensionToolbarModel::UninstalledExtension(const Extension* extension) {
300 // Remove the extension id from the ordered list, if it exists (the extension
301 // might not be represented in the list because it might not have an icon).
302 ExtensionIdList::iterator pos =
303 std::find(last_known_positions_.begin(),
304 last_known_positions_.end(), extension->id());
306 if (pos != last_known_positions_.end()) {
307 last_known_positions_.erase(pos);
308 UpdatePrefs();
312 // Combine the currently enabled extensions that have browser actions (which
313 // we get from the ExtensionService) with the ordering we get from the
314 // pref service. For robustness we use a somewhat inefficient process:
315 // 1. Create a vector of extensions sorted by their pref values. This vector may
316 // have holes.
317 // 2. Create a vector of extensions that did not have a pref value.
318 // 3. Remove holes from the sorted vector and append the unsorted vector.
319 void ExtensionToolbarModel::InitializeExtensionList() {
320 DCHECK(service_->is_ready());
322 last_known_positions_ = service_->extension_prefs()->GetToolbarOrder();
323 Populate(last_known_positions_);
325 extensions_initialized_ = true;
326 FOR_EACH_OBSERVER(Observer, observers_, ModelLoaded());
329 void ExtensionToolbarModel::Populate(
330 const extensions::ExtensionIdList& positions) {
331 // Items that have explicit positions.
332 ExtensionList sorted;
333 sorted.resize(positions.size(), NULL);
334 // The items that don't have explicit positions.
335 ExtensionList unsorted;
337 extensions::ExtensionActionManager* extension_action_manager =
338 extensions::ExtensionActionManager::Get(service_->profile());
340 // Create the lists.
341 for (ExtensionSet::const_iterator it = service_->extensions()->begin();
342 it != service_->extensions()->end(); ++it) {
343 const Extension* extension = it->get();
344 if (!extension_action_manager->GetBrowserAction(*extension))
345 continue;
346 if (!extensions::ExtensionActionAPI::GetBrowserActionVisibility(
347 service_->extension_prefs(), extension->id())) {
348 continue;
351 extensions::ExtensionIdList::const_iterator pos =
352 std::find(positions.begin(), positions.end(), extension->id());
353 if (pos != positions.end())
354 sorted[pos - positions.begin()] = extension;
355 else
356 unsorted.push_back(make_scoped_refptr(extension));
359 // Erase current icons.
360 for (size_t i = 0; i < toolbar_items_.size(); i++) {
361 FOR_EACH_OBSERVER(
362 Observer, observers_, BrowserActionRemoved(toolbar_items_[i].get()));
364 toolbar_items_.clear();
366 // Merge the lists.
367 toolbar_items_.reserve(sorted.size() + unsorted.size());
368 for (ExtensionList::const_iterator iter = sorted.begin();
369 iter != sorted.end(); ++iter) {
370 // It's possible for the extension order to contain items that aren't
371 // actually loaded on this machine. For example, when extension sync is on,
372 // we sync the extension order as-is but double-check with the user before
373 // syncing NPAPI-containing extensions, so if one of those is not actually
374 // synced, we'll get a NULL in the list. This sort of case can also happen
375 // if some error prevents an extension from loading.
376 if (iter->get() != NULL)
377 toolbar_items_.push_back(*iter);
379 toolbar_items_.insert(toolbar_items_.end(), unsorted.begin(),
380 unsorted.end());
382 // Inform observers.
383 for (size_t i = 0; i < toolbar_items_.size(); i++) {
384 FOR_EACH_OBSERVER(
385 Observer, observers_, BrowserActionAdded(toolbar_items_[i].get(), i));
389 void ExtensionToolbarModel::FillExtensionList(
390 const extensions::ExtensionIdList& order) {
391 toolbar_items_.clear();
392 toolbar_items_.reserve(order.size());
393 for (size_t i = 0; i < order.size(); ++i) {
394 const extensions::Extension* extension =
395 service_->GetExtensionById(order[i], false);
396 if (extension)
397 AddExtension(extension);
401 void ExtensionToolbarModel::UpdatePrefs() {
402 if (!service_->extension_prefs())
403 return;
405 // Don't observe change caused by self.
406 pref_change_registrar_.Remove(prefs::kExtensionToolbar);
407 service_->extension_prefs()->SetToolbarOrder(last_known_positions_);
408 pref_change_registrar_.Add(prefs::kExtensionToolbar, pref_change_callback_);
411 int ExtensionToolbarModel::IncognitoIndexToOriginal(int incognito_index) {
412 int original_index = 0, i = 0;
413 for (ExtensionList::iterator iter = toolbar_items_.begin();
414 iter != toolbar_items_.end();
415 ++iter, ++original_index) {
416 if (extension_util::IsIncognitoEnabled((*iter)->id(), service_)) {
417 if (incognito_index == i)
418 break;
419 ++i;
422 return original_index;
425 int ExtensionToolbarModel::OriginalIndexToIncognito(int original_index) {
426 int incognito_index = 0, i = 0;
427 for (ExtensionList::iterator iter = toolbar_items_.begin();
428 iter != toolbar_items_.end();
429 ++iter, ++i) {
430 if (original_index == i)
431 break;
432 if (extension_util::IsIncognitoEnabled((*iter)->id(), service_))
433 ++incognito_index;
435 return incognito_index;
438 void ExtensionToolbarModel::OnExtensionToolbarPrefChange() {
439 // If extensions are not ready, defer to later Populate() call.
440 if (!extensions_initialized_)
441 return;
443 // Recalculate |last_known_positions_| to be |pref_positions| followed by
444 // ones that are only in |last_known_positions_|.
445 extensions::ExtensionIdList pref_positions =
446 service_->extension_prefs()->GetToolbarOrder();
447 size_t pref_position_size = pref_positions.size();
448 for (size_t i = 0; i < last_known_positions_.size(); ++i) {
449 if (std::find(pref_positions.begin(), pref_positions.end(),
450 last_known_positions_[i]) == pref_positions.end()) {
451 pref_positions.push_back(last_known_positions_[i]);
454 last_known_positions_.swap(pref_positions);
456 // Re-populate.
457 Populate(last_known_positions_);
459 if (last_known_positions_.size() > pref_position_size) {
460 // Need to update pref because we have extra icons. But can't call
461 // UpdatePrefs() directly within observation closure.
462 base::MessageLoop::current()->PostTask(
463 FROM_HERE,
464 base::Bind(&ExtensionToolbarModel::UpdatePrefs,
465 weak_ptr_factory_.GetWeakPtr()));
469 bool ExtensionToolbarModel::ShowBrowserActionPopup(
470 const extensions::Extension* extension) {
471 ObserverListBase<Observer>::Iterator it(observers_);
472 Observer* obs = NULL;
473 while ((obs = it.GetNext()) != NULL) {
474 // Stop after first popup since it should only show in the active window.
475 if (obs->BrowserActionShowPopup(extension))
476 return true;
478 return false;