extension_toolbar_model.cc revision 46d4c2bc3267f3f028f39e7e311b0f89aba2e4fd
1d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen// Copyright (c) 2012 The Chromium Authors. All rights reserved. 2d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen// Use of this source code is governed by a BSD-style license that can be 3d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen// found in the LICENSE file. 4d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 5d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen#include "chrome/browser/extensions/extension_toolbar_model.h" 6d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 7d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen#include <string> 8d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 9d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen#include "base/metrics/histogram.h" 10d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen#include "base/metrics/histogram_base.h" 11d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen#include "base/prefs/pref_service.h" 12d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen#include "chrome/browser/chrome_notification_types.h" 13d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen#include "chrome/browser/extensions/api/extension_action/extension_action_api.h" 14d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen#include "chrome/browser/extensions/extension_action.h" 15d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen#include "chrome/browser/extensions/extension_action_manager.h" 16d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen#include "chrome/browser/extensions/extension_tab_util.h" 17d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen#include "chrome/browser/extensions/extension_toolbar_model_factory.h" 18d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen#include "chrome/browser/extensions/extension_util.h" 19d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen#include "chrome/browser/extensions/tab_helper.h" 20d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen#include "chrome/browser/profiles/profile.h" 21d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen#include "chrome/browser/ui/browser.h" 22d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen#include "chrome/browser/ui/tabs/tab_strip_model.h" 23d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen#include "chrome/common/pref_names.h" 24d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen#include "content/public/browser/notification_details.h" 25d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen#include "content/public/browser/notification_source.h" 26d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen#include "content/public/browser/web_contents.h" 27d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen#include "extensions/browser/extension_prefs.h" 28d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen#include "extensions/browser/extension_registry.h" 29d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen#include "extensions/browser/extension_system.h" 30d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen#include "extensions/browser/pref_names.h" 31d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen#include "extensions/common/extension.h" 32d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen#include "extensions/common/extension_set.h" 33d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen#include "extensions/common/feature_switch.h" 34d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen#include "extensions/common/one_shot_event.h" 35d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 36d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chennamespace extensions { 37d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 38d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenbool ExtensionToolbarModel::Observer::BrowserActionShowPopup( 39d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen const Extension* extension) { 40d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen return false; 41d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen} 42d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 43d7955ce24d294fb2014c59d11fca184471056f44Shuyi ChenExtensionToolbarModel::ExtensionToolbarModel(Profile* profile, 44d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen ExtensionPrefs* extension_prefs) 45d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen : profile_(profile), 46d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen extension_prefs_(extension_prefs), 47d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen prefs_(profile_->GetPrefs()), 48d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen extensions_initialized_(false), 49d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen is_highlighting_(false), 50d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen extension_registry_observer_(this), 51d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen weak_ptr_factory_(this) { 52d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen ExtensionSystem::Get(profile_)->ready().Post( 53d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen FROM_HERE, 54d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen base::Bind(&ExtensionToolbarModel::OnReady, 55d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen weak_ptr_factory_.GetWeakPtr())); 56d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen visible_icon_count_ = prefs_->GetInteger(pref_names::kToolbarSize); 57d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen pref_change_registrar_.Init(prefs_); 58d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen pref_change_callback_ = 59d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen base::Bind(&ExtensionToolbarModel::OnExtensionToolbarPrefChange, 60d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen base::Unretained(this)); 61d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen pref_change_registrar_.Add(pref_names::kToolbar, pref_change_callback_); 62d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen} 63d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 64d7955ce24d294fb2014c59d11fca184471056f44Shuyi ChenExtensionToolbarModel::~ExtensionToolbarModel() { 65d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen} 66d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 67d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen// static 68d7955ce24d294fb2014c59d11fca184471056f44Shuyi ChenExtensionToolbarModel* ExtensionToolbarModel::Get(Profile* profile) { 69d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen return ExtensionToolbarModelFactory::GetForProfile(profile); 70d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen} 71d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 72d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenvoid ExtensionToolbarModel::AddObserver(Observer* observer) { 73d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen observers_.AddObserver(observer); 74d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen} 75d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 76d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenvoid ExtensionToolbarModel::RemoveObserver(Observer* observer) { 77d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen observers_.RemoveObserver(observer); 78d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen} 79d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 80d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenvoid ExtensionToolbarModel::MoveBrowserAction(const Extension* extension, 81d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen int index) { 82d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen ExtensionList::iterator pos = std::find(toolbar_items_.begin(), 83d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen toolbar_items_.end(), extension); 84d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen if (pos == toolbar_items_.end()) { 85d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen NOTREACHED(); 86d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen return; 87d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 88d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen toolbar_items_.erase(pos); 89d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 90d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen ExtensionIdList::iterator pos_id; 91d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen pos_id = std::find(last_known_positions_.begin(), 92d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen last_known_positions_.end(), extension->id()); 93d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen if (pos_id != last_known_positions_.end()) 94d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen last_known_positions_.erase(pos_id); 95d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 96d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen int i = 0; 97 bool inserted = false; 98 for (ExtensionList::iterator iter = toolbar_items_.begin(); 99 iter != toolbar_items_.end(); 100 ++iter, ++i) { 101 if (i == index) { 102 pos_id = std::find(last_known_positions_.begin(), 103 last_known_positions_.end(), (*iter)->id()); 104 last_known_positions_.insert(pos_id, extension->id()); 105 106 toolbar_items_.insert(iter, make_scoped_refptr(extension)); 107 inserted = true; 108 break; 109 } 110 } 111 112 if (!inserted) { 113 DCHECK_EQ(index, static_cast<int>(toolbar_items_.size())); 114 index = toolbar_items_.size(); 115 116 toolbar_items_.push_back(make_scoped_refptr(extension)); 117 last_known_positions_.push_back(extension->id()); 118 } 119 120 FOR_EACH_OBSERVER(Observer, observers_, BrowserActionMoved(extension, index)); 121 122 UpdatePrefs(); 123} 124 125ExtensionToolbarModel::Action ExtensionToolbarModel::ExecuteBrowserAction( 126 const Extension* extension, 127 Browser* browser, 128 GURL* popup_url_out, 129 bool should_grant) { 130 content::WebContents* web_contents = NULL; 131 int tab_id = 0; 132 if (!ExtensionTabUtil::GetDefaultTab(browser, &web_contents, &tab_id)) { 133 return ACTION_NONE; 134 } 135 136 ExtensionAction* browser_action = 137 ExtensionActionManager::Get(profile_)->GetBrowserAction(*extension); 138 139 // For browser actions, visibility == enabledness. 140 if (!browser_action->GetIsVisible(tab_id)) 141 return ACTION_NONE; 142 143 if (should_grant) { 144 TabHelper::FromWebContents(web_contents) 145 ->active_tab_permission_granter() 146 ->GrantIfRequested(extension); 147 } 148 149 if (browser_action->HasPopup(tab_id)) { 150 if (popup_url_out) 151 *popup_url_out = browser_action->GetPopupUrl(tab_id); 152 return ACTION_SHOW_POPUP; 153 } 154 155 ExtensionActionAPI::BrowserActionExecuted( 156 browser->profile(), *browser_action, web_contents); 157 return ACTION_NONE; 158} 159 160void ExtensionToolbarModel::SetVisibleIconCount(int count) { 161 VLOG(4) << "visible_icon_count_ before: " << visible_icon_count_; 162 visible_icon_count_ = 163 count == static_cast<int>(toolbar_items_.size()) ? -1 : count; 164 VLOG(4) << "SetVisibleIconCount " 165 << count << " == " << toolbar_items_.size() << " -> " 166 << visible_icon_count_ << " " 167 << "is_highlighting: " << is_highlighting_; 168 // Only set the prefs if we're not in highlight mode. Highlight mode is 169 // designed to be a transitory state, and should not persist across browser 170 // restarts (though it may be re-entered). 171 if (!is_highlighting_) { 172 prefs_->SetInteger(pref_names::kToolbarSize, visible_icon_count_); 173 } 174} 175 176void ExtensionToolbarModel::OnExtensionLoaded( 177 content::BrowserContext* browser_context, 178 const Extension* extension) { 179 VLOG(4) << "Loading extension"; 180 181 // We don't want to add the same extension twice. It may have already been 182 // added by EXTENSION_BROWSER_ACTION_VISIBILITY_CHANGED below, if the user 183 // hides the browser action and then disables and enables the extension. 184 for (size_t i = 0; i < toolbar_items_.size(); i++) { 185 if (toolbar_items_[i].get() == extension) { 186 VLOG(4) << "... but returning early"; 187 return; 188 } 189 } 190 if (ExtensionActionAPI::GetBrowserActionVisibility(extension_prefs_, 191 extension->id())) { 192 VLOG(4) << "Adding extensions"; 193 AddExtension(extension); 194 } else { 195 VLOG(4) << "NOT visible"; 196 } 197} 198 199void ExtensionToolbarModel::OnExtensionUnloaded( 200 content::BrowserContext* browser_context, 201 const Extension* extension, 202 UnloadedExtensionInfo::Reason reason) { 203 RemoveExtension(extension); 204} 205 206void ExtensionToolbarModel::OnExtensionUninstalled( 207 content::BrowserContext* browser_context, 208 const Extension* extension) { 209 // Remove the extension id from the ordered list, if it exists (the extension 210 // might not be represented in the list because it might not have an icon). 211 ExtensionIdList::iterator pos = 212 std::find(last_known_positions_.begin(), 213 last_known_positions_.end(), extension->id()); 214 215 if (pos != last_known_positions_.end()) { 216 last_known_positions_.erase(pos); 217 UpdatePrefs(); 218 } 219} 220 221void ExtensionToolbarModel::Observe( 222 int type, 223 const content::NotificationSource& source, 224 const content::NotificationDetails& details) { 225 DCHECK_EQ(chrome::NOTIFICATION_EXTENSION_BROWSER_ACTION_VISIBILITY_CHANGED, 226 type); 227 const Extension* extension = 228 ExtensionRegistry::Get(profile_)->GetExtensionById( 229 *content::Details<const std::string>(details).ptr(), 230 ExtensionRegistry::EVERYTHING); 231 if (ExtensionActionAPI::GetBrowserActionVisibility(extension_prefs_, 232 extension->id())) { 233 VLOG(4) << "Adding extension"; 234 AddExtension(extension); 235 } else { 236 VLOG(4) << "Removing extension"; 237 RemoveExtension(extension); 238 } 239} 240 241void ExtensionToolbarModel::OnReady() { 242 VLOG(4) << "OnReady called"; 243 ExtensionRegistry* registry = ExtensionRegistry::Get(profile_); 244 InitializeExtensionList(registry->enabled_extensions()); 245 // Wait until the extension system is ready before observing any further 246 // changes so that the toolbar buttons can be shown in their stable ordering 247 // taken from prefs. 248 extension_registry_observer_.Add(registry); 249 registrar_.Add( 250 this, 251 chrome::NOTIFICATION_EXTENSION_BROWSER_ACTION_VISIBILITY_CHANGED, 252 content::Source<ExtensionPrefs>(extension_prefs_)); 253} 254 255size_t ExtensionToolbarModel::FindNewPositionFromLastKnownGood( 256 const Extension* extension) { 257 // See if we have last known good position for this extension. 258 size_t new_index = 0; 259 // Loop through the ID list of known positions, to count the number of visible 260 // browser action icons preceding |extension|. 261 for (ExtensionIdList::const_iterator iter_id = last_known_positions_.begin(); 262 iter_id < last_known_positions_.end(); ++iter_id) { 263 if ((*iter_id) == extension->id()) 264 return new_index; // We've found the right position. 265 // Found an id, need to see if it is visible. 266 for (ExtensionList::const_iterator iter_ext = toolbar_items_.begin(); 267 iter_ext < toolbar_items_.end(); ++iter_ext) { 268 if ((*iter_ext)->id().compare(*iter_id) == 0) { 269 // This extension is visible, update the index value. 270 ++new_index; 271 break; 272 } 273 } 274 } 275 276 return -1; 277} 278 279void ExtensionToolbarModel::AddExtension(const Extension* extension) { 280 // We only care about extensions with browser actions. 281 if (!ExtensionActionManager::Get(profile_)->GetBrowserAction(*extension)) 282 return; 283 284 size_t new_index = -1; 285 286 // See if we have a last known good position for this extension. 287 ExtensionIdList::iterator last_pos = std::find(last_known_positions_.begin(), 288 last_known_positions_.end(), 289 extension->id()); 290 if (last_pos != last_known_positions_.end()) { 291 new_index = FindNewPositionFromLastKnownGood(extension); 292 if (new_index != toolbar_items_.size()) { 293 toolbar_items_.insert(toolbar_items_.begin() + new_index, 294 make_scoped_refptr(extension)); 295 } else { 296 toolbar_items_.push_back(make_scoped_refptr(extension)); 297 } 298 } else { 299 // This is a never before seen extension, that was added to the end. Make 300 // sure to reflect that. 301 toolbar_items_.push_back(make_scoped_refptr(extension)); 302 last_known_positions_.push_back(extension->id()); 303 new_index = toolbar_items_.size() - 1; 304 UpdatePrefs(); 305 } 306 307 // If we're currently highlighting, then even though we add a browser action 308 // to the full list (|toolbar_items_|, there won't be another *visible* 309 // browser action, which was what the observers care about. 310 if (!is_highlighting_) { 311 FOR_EACH_OBSERVER(Observer, observers_, 312 BrowserActionAdded(extension, new_index)); 313 } 314} 315 316void ExtensionToolbarModel::RemoveExtension(const Extension* extension) { 317 ExtensionList::iterator pos = 318 std::find(toolbar_items_.begin(), toolbar_items_.end(), extension); 319 if (pos == toolbar_items_.end()) 320 return; 321 322 toolbar_items_.erase(pos); 323 324 // If we're in highlight mode, we also have to remove the extension from 325 // the highlighted list. 326 if (is_highlighting_) { 327 pos = std::find(highlighted_items_.begin(), 328 highlighted_items_.end(), 329 extension); 330 if (pos != highlighted_items_.end()) { 331 highlighted_items_.erase(pos); 332 FOR_EACH_OBSERVER(Observer, observers_, BrowserActionRemoved(extension)); 333 // If the highlighted list is now empty, we stop highlighting. 334 if (highlighted_items_.empty()) 335 StopHighlighting(); 336 } 337 } else { 338 FOR_EACH_OBSERVER(Observer, observers_, BrowserActionRemoved(extension)); 339 } 340 341 UpdatePrefs(); 342} 343 344// Combine the currently enabled extensions that have browser actions (which 345// we get from the ExtensionRegistry) with the ordering we get from the 346// pref service. For robustness we use a somewhat inefficient process: 347// 1. Create a vector of extensions sorted by their pref values. This vector may 348// have holes. 349// 2. Create a vector of extensions that did not have a pref value. 350// 3. Remove holes from the sorted vector and append the unsorted vector. 351void ExtensionToolbarModel::InitializeExtensionList( 352 const ExtensionSet& extensions) { 353 last_known_positions_ = extension_prefs_->GetToolbarOrder(); 354 Populate(last_known_positions_, extensions); 355 356 VLOG(4) << "extensions_initialized!"; 357 extensions_initialized_ = true; 358 FOR_EACH_OBSERVER(Observer, observers_, VisibleCountChanged()); 359} 360 361void ExtensionToolbarModel::Populate(const ExtensionIdList& positions, 362 const ExtensionSet& extensions) { 363 // Items that have explicit positions. 364 ExtensionList sorted; 365 sorted.resize(positions.size(), NULL); 366 // The items that don't have explicit positions. 367 ExtensionList unsorted; 368 369 ExtensionActionManager* extension_action_manager = 370 ExtensionActionManager::Get(profile_); 371 372 // Create the lists. 373 int hidden = 0; 374 for (ExtensionSet::const_iterator it = extensions.begin(); 375 it != extensions.end(); 376 ++it) { 377 const Extension* extension = it->get(); 378 if (!extension_action_manager->GetBrowserAction(*extension)) 379 continue; 380 if (!ExtensionActionAPI::GetBrowserActionVisibility( 381 extension_prefs_, extension->id())) { 382 ++hidden; 383 continue; 384 } 385 386 ExtensionIdList::const_iterator pos = 387 std::find(positions.begin(), positions.end(), extension->id()); 388 if (pos != positions.end()) 389 sorted[pos - positions.begin()] = extension; 390 else 391 unsorted.push_back(make_scoped_refptr(extension)); 392 } 393 394 size_t items_count = toolbar_items_.size(); 395 for (size_t i = 0; i < items_count; i++) { 396 const Extension* extension = toolbar_items_.back(); 397 // By popping the extension here (before calling BrowserActionRemoved), 398 // we will not shrink visible count by one after BrowserActionRemoved 399 // calls SetVisibleCount. 400 toolbar_items_.pop_back(); 401 FOR_EACH_OBSERVER( 402 Observer, observers_, BrowserActionRemoved(extension)); 403 } 404 DCHECK(toolbar_items_.empty()); 405 406 // Merge the lists. 407 toolbar_items_.reserve(sorted.size() + unsorted.size()); 408 409 for (ExtensionList::const_iterator iter = sorted.begin(); 410 iter != sorted.end(); ++iter) { 411 // It's possible for the extension order to contain items that aren't 412 // actually loaded on this machine. For example, when extension sync is on, 413 // we sync the extension order as-is but double-check with the user before 414 // syncing NPAPI-containing extensions, so if one of those is not actually 415 // synced, we'll get a NULL in the list. This sort of case can also happen 416 // if some error prevents an extension from loading. 417 if (iter->get() != NULL) { 418 toolbar_items_.push_back(*iter); 419 FOR_EACH_OBSERVER( 420 Observer, observers_, BrowserActionAdded( 421 *iter, toolbar_items_.size() - 1)); 422 } 423 } 424 for (ExtensionList::const_iterator iter = unsorted.begin(); 425 iter != unsorted.end(); ++iter) { 426 if (iter->get() != NULL) { 427 toolbar_items_.push_back(*iter); 428 FOR_EACH_OBSERVER( 429 Observer, observers_, BrowserActionAdded( 430 *iter, toolbar_items_.size() - 1)); 431 } 432 } 433 434 UMA_HISTOGRAM_COUNTS_100( 435 "ExtensionToolbarModel.BrowserActionsPermanentlyHidden", hidden); 436 UMA_HISTOGRAM_COUNTS_100("ExtensionToolbarModel.BrowserActionsCount", 437 toolbar_items_.size()); 438 439 if (!toolbar_items_.empty()) { 440 // Visible count can be -1, meaning: 'show all'. Since UMA converts negative 441 // values to 0, this would be counted as 'show none' unless we convert it to 442 // max. 443 UMA_HISTOGRAM_COUNTS_100("ExtensionToolbarModel.BrowserActionsVisible", 444 visible_icon_count_ == -1 ? 445 base::HistogramBase::kSampleType_MAX : 446 visible_icon_count_); 447 } 448} 449 450void ExtensionToolbarModel::UpdatePrefs() { 451 if (!extension_prefs_) 452 return; 453 454 // Don't observe change caused by self. 455 pref_change_registrar_.Remove(pref_names::kToolbar); 456 extension_prefs_->SetToolbarOrder(last_known_positions_); 457 pref_change_registrar_.Add(pref_names::kToolbar, pref_change_callback_); 458} 459 460int ExtensionToolbarModel::IncognitoIndexToOriginal(int incognito_index) { 461 int original_index = 0, i = 0; 462 for (ExtensionList::iterator iter = toolbar_items_.begin(); 463 iter != toolbar_items_.end(); 464 ++iter, ++original_index) { 465 if (util::IsIncognitoEnabled((*iter)->id(), profile_)) { 466 if (incognito_index == i) 467 break; 468 ++i; 469 } 470 } 471 return original_index; 472} 473 474int ExtensionToolbarModel::OriginalIndexToIncognito(int original_index) { 475 int incognito_index = 0, i = 0; 476 for (ExtensionList::iterator iter = toolbar_items_.begin(); 477 iter != toolbar_items_.end(); 478 ++iter, ++i) { 479 if (original_index == i) 480 break; 481 if (util::IsIncognitoEnabled((*iter)->id(), profile_)) 482 ++incognito_index; 483 } 484 return incognito_index; 485} 486 487void ExtensionToolbarModel::OnExtensionToolbarPrefChange() { 488 // If extensions are not ready, defer to later Populate() call. 489 if (!extensions_initialized_) 490 return; 491 492 // Recalculate |last_known_positions_| to be |pref_positions| followed by 493 // ones that are only in |last_known_positions_|. 494 ExtensionIdList pref_positions = extension_prefs_->GetToolbarOrder(); 495 size_t pref_position_size = pref_positions.size(); 496 for (size_t i = 0; i < last_known_positions_.size(); ++i) { 497 if (std::find(pref_positions.begin(), pref_positions.end(), 498 last_known_positions_[i]) == pref_positions.end()) { 499 pref_positions.push_back(last_known_positions_[i]); 500 } 501 } 502 last_known_positions_.swap(pref_positions); 503 504 // Re-populate. 505 Populate(last_known_positions_, 506 ExtensionRegistry::Get(profile_)->enabled_extensions()); 507 508 if (last_known_positions_.size() > pref_position_size) { 509 // Need to update pref because we have extra icons. But can't call 510 // UpdatePrefs() directly within observation closure. 511 base::MessageLoop::current()->PostTask( 512 FROM_HERE, 513 base::Bind(&ExtensionToolbarModel::UpdatePrefs, 514 weak_ptr_factory_.GetWeakPtr())); 515 } 516} 517 518bool ExtensionToolbarModel::ShowBrowserActionPopup(const Extension* extension) { 519 ObserverListBase<Observer>::Iterator it(observers_); 520 Observer* obs = NULL; 521 while ((obs = it.GetNext()) != NULL) { 522 // Stop after first popup since it should only show in the active window. 523 if (obs->BrowserActionShowPopup(extension)) 524 return true; 525 } 526 return false; 527} 528 529void ExtensionToolbarModel::EnsureVisibility( 530 const ExtensionIdList& extension_ids) { 531 if (visible_icon_count_ == -1) 532 return; // Already showing all. 533 534 // Otherwise, make sure we have enough room to show all the extensions 535 // requested. 536 if (visible_icon_count_ < static_cast<int>(extension_ids.size())) { 537 SetVisibleIconCount(extension_ids.size()); 538 539 // Inform observers. 540 FOR_EACH_OBSERVER(Observer, observers_, VisibleCountChanged()); 541 } 542 543 if (visible_icon_count_ == -1) 544 return; // May have been set to max by SetVisibleIconCount. 545 546 // Guillotine's Delight: Move an orange noble to the front of the line. 547 for (ExtensionIdList::const_iterator it = extension_ids.begin(); 548 it != extension_ids.end(); ++it) { 549 for (ExtensionList::const_iterator extension = toolbar_items_.begin(); 550 extension != toolbar_items_.end(); ++extension) { 551 if ((*extension)->id() == (*it)) { 552 if (extension - toolbar_items_.begin() >= visible_icon_count_) 553 MoveBrowserAction(*extension, 0); 554 break; 555 } 556 } 557 } 558} 559 560bool ExtensionToolbarModel::HighlightExtensions( 561 const ExtensionIdList& extension_ids) { 562 highlighted_items_.clear(); 563 564 for (ExtensionIdList::const_iterator id = extension_ids.begin(); 565 id != extension_ids.end(); 566 ++id) { 567 for (ExtensionList::const_iterator extension = toolbar_items_.begin(); 568 extension != toolbar_items_.end(); 569 ++extension) { 570 if (*id == (*extension)->id()) 571 highlighted_items_.push_back(*extension); 572 } 573 } 574 575 // If we have any items in |highlighted_items_|, then we entered highlighting 576 // mode. 577 if (highlighted_items_.size()) { 578 old_visible_icon_count_ = visible_icon_count_; 579 is_highlighting_ = true; 580 if (visible_icon_count_ != -1 && 581 visible_icon_count_ < static_cast<int>(extension_ids.size())) { 582 SetVisibleIconCount(extension_ids.size()); 583 FOR_EACH_OBSERVER(Observer, observers_, VisibleCountChanged()); 584 } 585 586 FOR_EACH_OBSERVER(Observer, observers_, HighlightModeChanged(true)); 587 return true; 588 } 589 590 // Otherwise, we didn't enter highlighting mode (and, in fact, exited it if 591 // we were otherwise in it). 592 if (is_highlighting_) 593 StopHighlighting(); 594 return false; 595} 596 597void ExtensionToolbarModel::StopHighlighting() { 598 if (is_highlighting_) { 599 highlighted_items_.clear(); 600 is_highlighting_ = false; 601 if (old_visible_icon_count_ != visible_icon_count_) { 602 SetVisibleIconCount(old_visible_icon_count_); 603 FOR_EACH_OBSERVER(Observer, observers_, VisibleCountChanged()); 604 } 605 FOR_EACH_OBSERVER(Observer, observers_, HighlightModeChanged(false)); 606 } 607} 608 609} // namespace extensions 610