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 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 34 ExtensionToolbarModel::~ExtensionToolbarModel() { 35 } 36 37 void ExtensionToolbarModel::DestroyingProfile() { 38 registrar_.RemoveAll(); 39 } 40 41 void ExtensionToolbarModel::AddObserver(Observer* observer) { 42 observers_.AddObserver(observer); 43 } 44 45 void ExtensionToolbarModel::RemoveObserver(Observer* observer) { 46 observers_.RemoveObserver(observer); 47 } 48 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 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 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 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 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. 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 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 233 const Extension* ExtensionToolbarModel::GetExtensionByIndex(int index) const { 234 return toolitems_[index]; 235 } 236 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 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