blob: 10549c092a83bfda1f16634e2bfd0b55db87d35c [file] [log] [blame]
// Copyright (c) 2012 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 <algorithm>
#include <string>
#include "base/message_loop/message_loop.h"
#include "base/metrics/histogram.h"
#include "base/metrics/histogram_base.h"
#include "base/prefs/pref_service.h"
#include "chrome/browser/chrome_notification_types.h"
#include "chrome/browser/extensions/api/extension_action/extension_action_api.h"
#include "chrome/browser/extensions/extension_action_manager.h"
#include "chrome/browser/extensions/extension_tab_util.h"
#include "chrome/browser/extensions/extension_toolbar_model_factory.h"
#include "chrome/browser/extensions/extension_util.h"
#include "chrome/browser/extensions/tab_helper.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h"
#include "content/public/browser/notification_details.h"
#include "content/public/browser/notification_source.h"
#include "content/public/browser/web_contents.h"
#include "extensions/browser/extension_prefs.h"
#include "extensions/browser/extension_registry.h"
#include "extensions/browser/extension_system.h"
#include "extensions/browser/pref_names.h"
#include "extensions/common/extension.h"
#include "extensions/common/extension_set.h"
#include "extensions/common/feature_switch.h"
#include "extensions/common/one_shot_event.h"
namespace extensions {
ExtensionToolbarModel::ExtensionToolbarModel(Profile* profile,
ExtensionPrefs* extension_prefs)
: profile_(profile),
extension_prefs_(extension_prefs),
prefs_(profile_->GetPrefs()),
extensions_initialized_(false),
include_all_extensions_(
FeatureSwitch::extension_action_redesign()->IsEnabled()),
is_highlighting_(false),
extension_action_observer_(this),
extension_registry_observer_(this),
weak_ptr_factory_(this) {
ExtensionSystem::Get(profile_)->ready().Post(
FROM_HERE,
base::Bind(&ExtensionToolbarModel::OnReady,
weak_ptr_factory_.GetWeakPtr()));
visible_icon_count_ = prefs_->GetInteger(pref_names::kToolbarSize);
// We only care about watching the prefs if not in incognito mode.
if (!profile_->IsOffTheRecord()) {
pref_change_registrar_.Init(prefs_);
pref_change_callback_ =
base::Bind(&ExtensionToolbarModel::OnExtensionToolbarPrefChange,
base::Unretained(this));
pref_change_registrar_.Add(pref_names::kToolbar, pref_change_callback_);
}
}
ExtensionToolbarModel::~ExtensionToolbarModel() {
}
// static
ExtensionToolbarModel* ExtensionToolbarModel::Get(Profile* profile) {
return ExtensionToolbarModelFactory::GetForProfile(profile);
}
void ExtensionToolbarModel::AddObserver(Observer* observer) {
observers_.AddObserver(observer);
}
void ExtensionToolbarModel::RemoveObserver(Observer* observer) {
observers_.RemoveObserver(observer);
}
void ExtensionToolbarModel::MoveExtensionIcon(const std::string& id,
size_t index) {
ExtensionList::iterator pos = toolbar_items_.begin();
while (pos != toolbar_items_.end() && (*pos)->id() != id)
++pos;
if (pos == toolbar_items_.end()) {
NOTREACHED();
return;
}
scoped_refptr<const Extension> extension = *pos;
toolbar_items_.erase(pos);
ExtensionIdList::iterator pos_id = std::find(last_known_positions_.begin(),
last_known_positions_.end(),
id);
if (pos_id != last_known_positions_.end())
last_known_positions_.erase(pos_id);
if (index < toolbar_items_.size()) {
// If the index is not at the end, find the item currently at |index|, and
// insert |extension| before it in both |toolbar_items_| and
// |last_known_positions_|.
ExtensionList::iterator iter = toolbar_items_.begin() + index;
last_known_positions_.insert(std::find(last_known_positions_.begin(),
last_known_positions_.end(),
(*iter)->id()),
id);
toolbar_items_.insert(iter, extension);
} else {
// Otherwise, put |extension| at the end.
DCHECK_EQ(toolbar_items_.size(), index);
index = toolbar_items_.size();
toolbar_items_.push_back(extension);
last_known_positions_.push_back(id);
}
FOR_EACH_OBSERVER(
Observer, observers_, ToolbarExtensionMoved(extension.get(), index));
MaybeUpdateVisibilityPref(extension.get(), index);
UpdatePrefs();
}
void ExtensionToolbarModel::SetVisibleIconCount(size_t count) {
visible_icon_count_ = (count == toolbar_items_.size()) ? -1 : count;
// Only set the prefs if we're not in highlight mode and the profile is not
// incognito. Highlight mode is designed to be a transitory state, and should
// not persist across browser restarts (though it may be re-entered), and we
// don't store anything in incognito.
if (!is_highlighting_ && !profile_->IsOffTheRecord()) {
// Additionally, if we are using the new toolbar, any icons which are in the
// overflow menu are considered "hidden". But it so happens that the times
// we are likely to call SetVisibleIconCount() are also those when we are
// in flux. So wait for things to cool down before setting the prefs.
base::MessageLoop::current()->PostTask(
FROM_HERE,
base::Bind(&ExtensionToolbarModel::MaybeUpdateVisibilityPrefs,
weak_ptr_factory_.GetWeakPtr()));
prefs_->SetInteger(pref_names::kToolbarSize, visible_icon_count_);
}
FOR_EACH_OBSERVER(Observer, observers_, ToolbarVisibleCountChanged());
}
void ExtensionToolbarModel::OnExtensionActionUpdated(
ExtensionAction* extension_action,
content::WebContents* web_contents,
content::BrowserContext* browser_context) {
const Extension* extension =
ExtensionRegistry::Get(profile_)->enabled_extensions().GetByID(
extension_action->extension_id());
// Notify observers if the extension exists and is in the model.
ExtensionList::const_iterator iter =
std::find(toolbar_items_.begin(), toolbar_items_.end(), extension);
if (iter != toolbar_items_.end()) {
FOR_EACH_OBSERVER(
Observer, observers_, ToolbarExtensionUpdated(extension));
// If the action was in the overflow menu, we have to alert observers that
// the toolbar needs to be reordered (to show the action).
if (static_cast<size_t>(iter - toolbar_items_.begin()) >=
visible_icon_count()) {
FOR_EACH_OBSERVER(
Observer, observers_, OnToolbarReorderNecessary(web_contents));
}
}
}
void ExtensionToolbarModel::OnExtensionLoaded(
content::BrowserContext* browser_context,
const Extension* extension) {
// 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 < toolbar_items_.size(); i++) {
if (toolbar_items_[i].get() == extension)
return;
}
AddExtension(extension);
}
void ExtensionToolbarModel::OnExtensionUnloaded(
content::BrowserContext* browser_context,
const Extension* extension,
UnloadedExtensionInfo::Reason reason) {
RemoveExtension(extension);
}
void ExtensionToolbarModel::OnExtensionUninstalled(
content::BrowserContext* browser_context,
const Extension* extension,
extensions::UninstallReason reason) {
// Remove the extension id from the ordered list, if it exists (the extension
// might not be represented in the list because it might not have an icon).
ExtensionIdList::iterator pos =
std::find(last_known_positions_.begin(),
last_known_positions_.end(), extension->id());
if (pos != last_known_positions_.end()) {
last_known_positions_.erase(pos);
UpdatePrefs();
}
}
void ExtensionToolbarModel::Observe(
int type,
const content::NotificationSource& source,
const content::NotificationDetails& details) {
DCHECK_EQ(NOTIFICATION_EXTENSION_BROWSER_ACTION_VISIBILITY_CHANGED, type);
const Extension* extension =
ExtensionRegistry::Get(profile_)->GetExtensionById(
*content::Details<const std::string>(details).ptr(),
ExtensionRegistry::EVERYTHING);
bool visible = ExtensionActionAPI::GetBrowserActionVisibility(
extension_prefs_, extension->id());
// Hiding works differently with the new and old toolbars.
if (include_all_extensions_) {
// It's possible that we haven't added this extension yet, if its
// visibility was adjusted in the course of its initialization.
if (std::find(toolbar_items_.begin(), toolbar_items_.end(), extension) ==
toolbar_items_.end())
return;
int new_size = 0;
int new_index = 0;
if (visible) {
// If this action used to be hidden, we can't possibly be showing all.
DCHECK_NE(-1, visible_icon_count_);
// Grow the bar by one and move the extension to the end of the visibles.
new_size = visible_icon_count_ + 1;
new_index = new_size - 1;
} else {
// If we're hiding one, we must be showing at least one.
DCHECK_NE(visible_icon_count_, 0);
// Shrink the bar by one and move the extension to the beginning of the
// overflow menu.
new_size = visible_icon_count_ == -1 ?
toolbar_items_.size() - 1 : visible_icon_count_ - 1;
new_index = new_size;
}
SetVisibleIconCount(new_size);
MoveExtensionIcon(extension->id(), new_index);
} else { // Don't include all extensions.
if (visible)
AddExtension(extension);
else
RemoveExtension(extension);
}
}
void ExtensionToolbarModel::OnReady() {
ExtensionRegistry* registry = ExtensionRegistry::Get(profile_);
InitializeExtensionList();
// Wait until the extension system is ready before observing any further
// changes so that the toolbar buttons can be shown in their stable ordering
// taken from prefs.
extension_registry_observer_.Add(registry);
extension_action_observer_.Add(ExtensionActionAPI::Get(profile_));
registrar_.Add(
this,
extensions::NOTIFICATION_EXTENSION_BROWSER_ACTION_VISIBILITY_CHANGED,
content::Source<ExtensionPrefs>(extension_prefs_));
}
size_t ExtensionToolbarModel::FindNewPositionFromLastKnownGood(
const Extension* extension) {
// See if we have last known good position for this extension.
size_t new_index = 0;
// Loop through the ID list of known positions, to count the number of visible
// extension icons preceding |extension|.
for (ExtensionIdList::const_iterator iter_id = last_known_positions_.begin();
iter_id < last_known_positions_.end(); ++iter_id) {
if ((*iter_id) == extension->id())
return new_index; // We've found the right position.
// Found an id, need to see if it is visible.
for (ExtensionList::const_iterator iter_ext = toolbar_items_.begin();
iter_ext < toolbar_items_.end(); ++iter_ext) {
if ((*iter_ext)->id() == (*iter_id)) {
// This extension is visible, update the index value.
++new_index;
break;
}
}
}
// Position not found.
return toolbar_items_.size();
}
bool ExtensionToolbarModel::ShouldAddExtension(const Extension* extension) {
// In incognito mode, don't add any extensions that aren't incognito-enabled.
if (profile_->IsOffTheRecord() &&
!util::IsIncognitoEnabled(extension->id(), profile_))
return false;
ExtensionActionManager* action_manager =
ExtensionActionManager::Get(profile_);
if (include_all_extensions_) {
// In this case, we don't care about the browser action visibility, because
// we want to show each extension regardless.
// TODO(devlin): Extension actions which are not visible should be moved to
// the overflow menu by default.
return action_manager->GetExtensionAction(*extension) != NULL;
}
return action_manager->GetBrowserAction(*extension) &&
ExtensionActionAPI::GetBrowserActionVisibility(
extension_prefs_, extension->id());
}
void ExtensionToolbarModel::AddExtension(const Extension* extension) {
if (!ShouldAddExtension(extension))
return;
// See if we have a last known good position for this extension.
bool is_new_extension =
std::find(last_known_positions_.begin(),
last_known_positions_.end(),
extension->id()) == last_known_positions_.end();
size_t new_index = is_new_extension ? toolbar_items_.size() :
FindNewPositionFromLastKnownGood(extension);
toolbar_items_.insert(toolbar_items_.begin() + new_index,
make_scoped_refptr(extension));
if (is_new_extension) {
last_known_positions_.push_back(extension->id());
UpdatePrefs();
}
MaybeUpdateVisibilityPref(extension, new_index);
// If we're currently highlighting, then even though we add a browser action
// to the full list (|toolbar_items_|, there won't be another *visible*
// browser action, which was what the observers care about.
if (!is_highlighting_) {
FOR_EACH_OBSERVER(
Observer, observers_, ToolbarExtensionAdded(extension, new_index));
// If this is an incognito profile, we also have to check to make sure the
// overflow matches the main bar's status.
if (profile_->IsOffTheRecord()) {
ExtensionToolbarModel* main_model =
ExtensionToolbarModel::Get(profile_->GetOriginalProfile());
// Find what the index will be in the main bar. Because Observer calls are
// nondeterministic, we can't just assume the main bar will have the
// extension and look it up.
int main_index = is_new_extension ?
main_model->toolbar_items_.size() :
main_model->FindNewPositionFromLastKnownGood(extension);
bool visible = main_index < main_model->visible_icon_count_ ||
main_model->visible_icon_count_ == -1;
// We may need to adjust the visible count if the incognito bar isn't
// showing all icons and this one is visible, or if it is showing all
// icons and this is hidden.
if (visible && visible_icon_count_ != -1)
SetVisibleIconCount(visible_icon_count_ + 1);
else if (!visible && visible_icon_count_ == -1)
SetVisibleIconCount(toolbar_items_.size() - 1);
}
}
}
void ExtensionToolbarModel::RemoveExtension(const Extension* extension) {
ExtensionList::iterator pos =
std::find(toolbar_items_.begin(), toolbar_items_.end(), extension);
if (pos == toolbar_items_.end())
return;
// If our visible count is set to the current size, we need to decrement it.
if (visible_icon_count_ == static_cast<int>(toolbar_items_.size()))
SetVisibleIconCount(toolbar_items_.size() - 1);
toolbar_items_.erase(pos);
// If we're in highlight mode, we also have to remove the extension from
// the highlighted list.
if (is_highlighting_) {
pos = std::find(highlighted_items_.begin(),
highlighted_items_.end(),
extension);
if (pos != highlighted_items_.end()) {
highlighted_items_.erase(pos);
FOR_EACH_OBSERVER(
Observer, observers_, ToolbarExtensionRemoved(extension));
// If the highlighted list is now empty, we stop highlighting.
if (highlighted_items_.empty())
StopHighlighting();
}
} else {
FOR_EACH_OBSERVER(Observer, observers_, ToolbarExtensionRemoved(extension));
}
UpdatePrefs();
}
void ExtensionToolbarModel::ClearItems() {
size_t items_count = toolbar_items_.size();
for (size_t i = 0; i < items_count; ++i) {
const Extension* extension = toolbar_items_.back().get();
toolbar_items_.pop_back();
FOR_EACH_OBSERVER(Observer, observers_, ToolbarExtensionRemoved(extension));
}
DCHECK(toolbar_items_.empty());
}
// Combine the currently enabled extensions that have browser actions (which
// we get from the ExtensionRegistry) 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() {
last_known_positions_ = extension_prefs_->GetToolbarOrder();
if (profile_->IsOffTheRecord())
IncognitoPopulate();
else
Populate(last_known_positions_);
extensions_initialized_ = true;
MaybeUpdateVisibilityPrefs();
FOR_EACH_OBSERVER(Observer, observers_, ToolbarVisibleCountChanged());
}
void ExtensionToolbarModel::Populate(const ExtensionIdList& positions) {
DCHECK(!profile_->IsOffTheRecord());
const ExtensionSet& extensions =
ExtensionRegistry::Get(profile_)->enabled_extensions();
// Items that have explicit positions.
ExtensionList sorted(positions.size(), NULL);
// The items that don't have explicit positions.
ExtensionList unsorted;
// Create the lists.
int hidden = 0;
for (const scoped_refptr<const Extension>& extension : extensions) {
if (!ShouldAddExtension(extension.get())) {
if (!ExtensionActionAPI::GetBrowserActionVisibility(extension_prefs_,
extension->id()))
++hidden;
continue;
}
ExtensionIdList::const_iterator pos =
std::find(positions.begin(), positions.end(), extension->id());
if (pos != positions.end())
sorted[pos - positions.begin()] = extension;
else
unsorted.push_back(extension);
}
// Clear the current items, if any.
ClearItems();
// Merge the lists.
toolbar_items_.reserve(sorted.size() + unsorted.size());
for (const scoped_refptr<const Extension>& extension : sorted) {
// It's possible for the extension order to contain items that aren't
// actually loaded on this machine. For example, when extension sync is on,
// we sync the extension order as-is but double-check with the user before
// syncing NPAPI-containing extensions, so if one of those is not actually
// synced, we'll get a NULL in the list. This sort of case can also happen
// if some error prevents an extension from loading.
if (extension.get() != NULL) {
toolbar_items_.push_back(extension);
FOR_EACH_OBSERVER(
Observer,
observers_,
ToolbarExtensionAdded(extension.get(), toolbar_items_.size() - 1));
}
}
for (const scoped_refptr<const Extension>& extension : unsorted) {
if (extension.get() != NULL) {
toolbar_items_.push_back(extension);
FOR_EACH_OBSERVER(
Observer,
observers_,
ToolbarExtensionAdded(extension.get(), toolbar_items_.size() - 1));
}
}
UMA_HISTOGRAM_COUNTS_100(
"ExtensionToolbarModel.BrowserActionsPermanentlyHidden", hidden);
UMA_HISTOGRAM_COUNTS_100("ExtensionToolbarModel.BrowserActionsCount",
toolbar_items_.size());
if (!toolbar_items_.empty()) {
// Visible count can be -1, meaning: 'show all'. Since UMA converts negative
// values to 0, this would be counted as 'show none' unless we convert it to
// max.
UMA_HISTOGRAM_COUNTS_100("ExtensionToolbarModel.BrowserActionsVisible",
visible_icon_count_ == -1 ?
base::HistogramBase::kSampleType_MAX :
visible_icon_count_);
}
}
void ExtensionToolbarModel::IncognitoPopulate() {
DCHECK(profile_->IsOffTheRecord());
// Clear the current items, if any.
ClearItems();
const ExtensionToolbarModel* original_model =
ExtensionToolbarModel::Get(profile_->GetOriginalProfile());
// Find the absolute value of the original model's count.
int original_visible = original_model->visible_icon_count();
// In incognito mode, we show only those extensions that are
// incognito-enabled. Further, any actions that were overflowed in regular
// mode are still overflowed. Order is the same as in regular mode.
visible_icon_count_ = 0;
for (ExtensionList::const_iterator iter =
original_model->toolbar_items_.begin();
iter != original_model->toolbar_items_.end(); ++iter) {
if (ShouldAddExtension(iter->get())) {
toolbar_items_.push_back(*iter);
if (iter - original_model->toolbar_items_.begin() < original_visible)
++visible_icon_count_;
FOR_EACH_OBSERVER(
Observer,
observers_,
ToolbarExtensionAdded(iter->get(), toolbar_items_.size() - 1));
}
}
}
void ExtensionToolbarModel::UpdatePrefs() {
if (!extension_prefs_ || profile_->IsOffTheRecord())
return;
// Don't observe change caused by self.
pref_change_registrar_.Remove(pref_names::kToolbar);
extension_prefs_->SetToolbarOrder(last_known_positions_);
pref_change_registrar_.Add(pref_names::kToolbar, pref_change_callback_);
}
void ExtensionToolbarModel::MaybeUpdateVisibilityPref(
const Extension* extension, int index) {
// We only update the visibility pref for hidden/not hidden based on the
// overflow menu with the new toolbar design.
if (include_all_extensions_ && !profile_->IsOffTheRecord()) {
bool visible = index < visible_icon_count_ || visible_icon_count_ == -1;
if (visible != ExtensionActionAPI::GetBrowserActionVisibility(
extension_prefs_, extension->id())) {
// Don't observe changes caused by ourselves.
bool was_registered = false;
if (registrar_.IsRegistered(
this,
NOTIFICATION_EXTENSION_BROWSER_ACTION_VISIBILITY_CHANGED,
content::Source<ExtensionPrefs>(extension_prefs_))) {
was_registered = true;
registrar_.Remove(
this,
NOTIFICATION_EXTENSION_BROWSER_ACTION_VISIBILITY_CHANGED,
content::Source<ExtensionPrefs>(extension_prefs_));
}
ExtensionActionAPI::SetBrowserActionVisibility(
extension_prefs_, extension->id(), visible);
if (was_registered) {
registrar_.Add(this,
NOTIFICATION_EXTENSION_BROWSER_ACTION_VISIBILITY_CHANGED,
content::Source<ExtensionPrefs>(extension_prefs_));
}
}
}
}
void ExtensionToolbarModel::MaybeUpdateVisibilityPrefs() {
for (size_t i = 0u; i < toolbar_items_.size(); ++i)
MaybeUpdateVisibilityPref(toolbar_items_[i].get(), i);
}
void ExtensionToolbarModel::OnExtensionToolbarPrefChange() {
// If extensions are not ready, defer to later Populate() call.
if (!extensions_initialized_)
return;
// Recalculate |last_known_positions_| to be |pref_positions| followed by
// ones that are only in |last_known_positions_|.
ExtensionIdList pref_positions = extension_prefs_->GetToolbarOrder();
size_t pref_position_size = pref_positions.size();
for (size_t i = 0; i < last_known_positions_.size(); ++i) {
if (std::find(pref_positions.begin(), pref_positions.end(),
last_known_positions_[i]) == pref_positions.end()) {
pref_positions.push_back(last_known_positions_[i]);
}
}
last_known_positions_.swap(pref_positions);
// Re-populate.
Populate(last_known_positions_);
if (last_known_positions_.size() > pref_position_size) {
// Need to update pref because we have extra icons. But can't call
// UpdatePrefs() directly within observation closure.
base::MessageLoop::current()->PostTask(
FROM_HERE,
base::Bind(&ExtensionToolbarModel::UpdatePrefs,
weak_ptr_factory_.GetWeakPtr()));
}
}
size_t ExtensionToolbarModel::GetVisibleIconCountForTab(
content::WebContents* web_contents) const {
if (all_icons_visible())
return visible_icon_count(); // Already displaying all actions.
ExtensionActionAPI* extension_action_api = ExtensionActionAPI::Get(profile_);
size_t total_icons = visible_icon_count_;
for (size_t i = total_icons; i < toolbar_items_.size(); ++i) {
if (extension_action_api->ExtensionWantsToRun(toolbar_items_[i].get(),
web_contents))
++total_icons;
}
return total_icons;
}
ExtensionList ExtensionToolbarModel::GetItemOrderForTab(
content::WebContents* web_contents) const {
// If we're highlighting, the items are always the same.
if (is_highlighting_)
return highlighted_items_;
// Start by initializing the array to be the same as toolbar items (this isn't
// any more expensive than initializing it to be of the same size with all
// nulls, and saves us time at the end).
ExtensionList result = toolbar_items_;
if (toolbar_items_.empty())
return result;
ExtensionList overflowed_actions_wanting_to_run;
ExtensionActionAPI* extension_action_api = ExtensionActionAPI::Get(profile_);
size_t boundary = visible_icon_count();
// Rotate any actions that want to run to the boundary between visible and
// overflowed actions.
for (ExtensionList::iterator iter = result.begin() + boundary;
iter != result.end(); ++iter) {
if (extension_action_api->ExtensionWantsToRun(iter->get(), web_contents)) {
std::rotate(result.begin() + boundary, iter, iter + 1);
++boundary;
}
}
return result;
}
bool ExtensionToolbarModel::ShowExtensionActionPopup(
const Extension* extension,
Browser* browser,
bool grant_active_tab) {
ObserverListBase<Observer>::Iterator it(observers_);
Observer* obs = NULL;
// Look for the Observer associated with the browser.
// This would be cleaner if we had an abstract class for the Toolbar UI
// (like we do for LocationBar), but sadly, we don't.
while ((obs = it.GetNext()) != NULL) {
if (obs->GetBrowser() == browser)
return obs->ShowExtensionActionPopup(extension, grant_active_tab);
}
return false;
}
void ExtensionToolbarModel::EnsureVisibility(
const ExtensionIdList& extension_ids) {
if (visible_icon_count_ == -1)
return; // Already showing all.
// Otherwise, make sure we have enough room to show all the extensions
// requested.
if (visible_icon_count_ < static_cast<int>(extension_ids.size()))
SetVisibleIconCount(extension_ids.size());
if (visible_icon_count_ == -1)
return; // May have been set to max by SetVisibleIconCount.
// Guillotine's Delight: Move an orange noble to the front of the line.
for (ExtensionIdList::const_iterator it = extension_ids.begin();
it != extension_ids.end(); ++it) {
for (ExtensionList::const_iterator extension = toolbar_items_.begin();
extension != toolbar_items_.end(); ++extension) {
if ((*extension)->id() == (*it)) {
if (extension - toolbar_items_.begin() >= visible_icon_count_)
MoveExtensionIcon((*extension)->id(), 0);
break;
}
}
}
}
bool ExtensionToolbarModel::HighlightExtensions(
const ExtensionIdList& extension_ids) {
highlighted_items_.clear();
for (ExtensionIdList::const_iterator id = extension_ids.begin();
id != extension_ids.end();
++id) {
for (ExtensionList::const_iterator extension = toolbar_items_.begin();
extension != toolbar_items_.end();
++extension) {
if (*id == (*extension)->id())
highlighted_items_.push_back(*extension);
}
}
// If we have any items in |highlighted_items_|, then we entered highlighting
// mode.
if (highlighted_items_.size()) {
old_visible_icon_count_ = visible_icon_count_;
is_highlighting_ = true;
if (visible_icon_count_ != -1 &&
visible_icon_count_ < static_cast<int>(extension_ids.size())) {
SetVisibleIconCount(extension_ids.size());
}
FOR_EACH_OBSERVER(Observer, observers_, ToolbarHighlightModeChanged(true));
return true;
}
// Otherwise, we didn't enter highlighting mode (and, in fact, exited it if
// we were otherwise in it).
if (is_highlighting_)
StopHighlighting();
return false;
}
void ExtensionToolbarModel::StopHighlighting() {
if (is_highlighting_) {
highlighted_items_.clear();
is_highlighting_ = false;
if (old_visible_icon_count_ != visible_icon_count_)
SetVisibleIconCount(old_visible_icon_count_);
FOR_EACH_OBSERVER(Observer, observers_, ToolbarHighlightModeChanged(false));
}
}
} // namespace extensions