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