// Copyright (c) 2011 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "chrome/browser/extensions/extension_toolbar_model.h" #include "chrome/browser/extensions/extension_prefs.h" #include "chrome/browser/extensions/extension_service.h" #include "chrome/browser/prefs/pref_service.h" #include "chrome/browser/profiles/profile.h" #include "chrome/common/extensions/extension.h" #include "chrome/common/pref_names.h" #include "content/common/notification_service.h" ExtensionToolbarModel::ExtensionToolbarModel(ExtensionService* service) : service_(service), prefs_(service->profile()->GetPrefs()), extensions_initialized_(false) { DCHECK(service_); registrar_.Add(this, NotificationType::EXTENSION_LOADED, Source<Profile>(service_->profile())); registrar_.Add(this, NotificationType::EXTENSION_UNLOADED, Source<Profile>(service_->profile())); registrar_.Add(this, NotificationType::EXTENSIONS_READY, Source<Profile>(service_->profile())); registrar_.Add(this, NotificationType::EXTENSION_BROWSER_ACTION_VISIBILITY_CHANGED, NotificationService::AllSources()); visible_icon_count_ = prefs_->GetInteger(prefs::kExtensionToolbarSize); } ExtensionToolbarModel::~ExtensionToolbarModel() { } void ExtensionToolbarModel::DestroyingProfile() { registrar_.RemoveAll(); } void ExtensionToolbarModel::AddObserver(Observer* observer) { observers_.AddObserver(observer); } void ExtensionToolbarModel::RemoveObserver(Observer* observer) { observers_.RemoveObserver(observer); } void ExtensionToolbarModel::MoveBrowserAction(const Extension* extension, int index) { ExtensionList::iterator pos = std::find(begin(), end(), extension); if (pos == end()) { NOTREACHED(); return; } toolitems_.erase(pos); int i = 0; bool inserted = false; for (ExtensionList::iterator iter = begin(); iter != end(); ++iter, ++i) { if (i == index) { toolitems_.insert(iter, make_scoped_refptr(extension)); inserted = true; break; } } if (!inserted) { DCHECK_EQ(index, static_cast<int>(toolitems_.size())); index = toolitems_.size(); toolitems_.push_back(make_scoped_refptr(extension)); } FOR_EACH_OBSERVER(Observer, observers_, BrowserActionMoved(extension, index)); UpdatePrefs(); } void ExtensionToolbarModel::SetVisibleIconCount(int count) { visible_icon_count_ = count == static_cast<int>(size()) ? -1 : count; prefs_->SetInteger(prefs::kExtensionToolbarSize, visible_icon_count_); prefs_->ScheduleSavePersistentPrefs(); } void ExtensionToolbarModel::Observe(NotificationType type, const NotificationSource& source, const NotificationDetails& details) { if (type == NotificationType::EXTENSIONS_READY) { InitializeExtensionList(); return; } if (!service_->is_ready()) return; const Extension* extension = NULL; if (type == NotificationType::EXTENSION_UNLOADED) { extension = Details<UnloadedExtensionInfo>(details)->extension; } else { extension = Details<const Extension>(details).ptr(); } if (type == NotificationType::EXTENSION_LOADED) { // We don't want to add the same extension twice. It may have already been // added by EXTENSION_BROWSER_ACTION_VISIBILITY_CHANGED below, if the user // hides the browser action and then disables and enables the extension. for (size_t i = 0; i < toolitems_.size(); i++) { if (toolitems_[i].get() == extension) return; // Already exists. } if (service_->GetBrowserActionVisibility(extension)) AddExtension(extension); } else if (type == NotificationType::EXTENSION_UNLOADED) { RemoveExtension(extension); } else if (type == NotificationType::EXTENSION_BROWSER_ACTION_VISIBILITY_CHANGED) { if (service_->GetBrowserActionVisibility(extension)) AddExtension(extension); else RemoveExtension(extension); } else { NOTREACHED() << "Received unexpected notification"; } } void ExtensionToolbarModel::AddExtension(const Extension* extension) { // We only care about extensions with browser actions. if (!extension->browser_action()) return; if (extension->id() == last_extension_removed_ && last_extension_removed_index_ < toolitems_.size()) { toolitems_.insert(begin() + last_extension_removed_index_, make_scoped_refptr(extension)); FOR_EACH_OBSERVER(Observer, observers_, BrowserActionAdded(extension, last_extension_removed_index_)); } else { toolitems_.push_back(make_scoped_refptr(extension)); FOR_EACH_OBSERVER(Observer, observers_, BrowserActionAdded(extension, toolitems_.size() - 1)); } last_extension_removed_ = ""; last_extension_removed_index_ = -1; UpdatePrefs(); } void ExtensionToolbarModel::RemoveExtension(const Extension* extension) { ExtensionList::iterator pos = std::find(begin(), end(), extension); if (pos == end()) { return; } last_extension_removed_ = extension->id(); last_extension_removed_index_ = pos - begin(); toolitems_.erase(pos); FOR_EACH_OBSERVER(Observer, observers_, BrowserActionRemoved(extension)); UpdatePrefs(); } // Combine the currently enabled extensions that have browser actions (which // we get from the ExtensionService) with the ordering we get from the // pref service. For robustness we use a somewhat inefficient process: // 1. Create a vector of extensions sorted by their pref values. This vector may // have holes. // 2. Create a vector of extensions that did not have a pref value. // 3. Remove holes from the sorted vector and append the unsorted vector. void ExtensionToolbarModel::InitializeExtensionList() { DCHECK(service_->is_ready()); std::vector<std::string> pref_order = service_->extension_prefs()-> GetToolbarOrder(); // Items that have a pref for their position. ExtensionList sorted; sorted.resize(pref_order.size(), NULL); // The items that don't have a pref for their position. ExtensionList unsorted; // Create the lists. for (size_t i = 0; i < service_->extensions()->size(); ++i) { const Extension* extension = service_->extensions()->at(i); if (!extension->browser_action()) continue; if (!service_->GetBrowserActionVisibility(extension)) continue; std::vector<std::string>::iterator pos = std::find(pref_order.begin(), pref_order.end(), extension->id()); if (pos != pref_order.end()) { int index = std::distance(pref_order.begin(), pos); sorted[index] = extension; } else { unsorted.push_back(make_scoped_refptr(extension)); } } // Merge the lists. toolitems_.reserve(sorted.size() + unsorted.size()); for (ExtensionList::iterator iter = sorted.begin(); iter != sorted.end(); ++iter) { if (*iter != NULL) toolitems_.push_back(*iter); } toolitems_.insert(toolitems_.end(), unsorted.begin(), unsorted.end()); // Inform observers. for (size_t i = 0; i < toolitems_.size(); i++) { FOR_EACH_OBSERVER(Observer, observers_, BrowserActionAdded(toolitems_[i], i)); } UpdatePrefs(); extensions_initialized_ = true; FOR_EACH_OBSERVER(Observer, observers_, ModelLoaded()); } void ExtensionToolbarModel::UpdatePrefs() { if (!service_->extension_prefs()) return; std::vector<std::string> ids; ids.reserve(toolitems_.size()); for (ExtensionList::iterator iter = begin(); iter != end(); ++iter) ids.push_back((*iter)->id()); service_->extension_prefs()->SetToolbarOrder(ids); } const Extension* ExtensionToolbarModel::GetExtensionByIndex(int index) const { return toolitems_[index]; } int ExtensionToolbarModel::IncognitoIndexToOriginal(int incognito_index) { int original_index = 0, i = 0; for (ExtensionList::iterator iter = begin(); iter != end(); ++iter, ++original_index) { if (service_->IsIncognitoEnabled((*iter)->id())) { if (incognito_index == i) break; ++i; } } return original_index; } int ExtensionToolbarModel::OriginalIndexToIncognito(int original_index) { int incognito_index = 0, i = 0; for (ExtensionList::iterator iter = begin(); iter != end(); ++iter, ++i) { if (original_index == i) break; if (service_->IsIncognitoEnabled((*iter)->id())) ++incognito_index; } return incognito_index; }