blob: bad90272f3d9505d74700d856ef57f1ffe28ae3a [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/location.h"
#include "base/metrics/histogram.h"
#include "base/metrics/histogram_base.h"
#include "base/prefs/pref_service.h"
#include "base/single_thread_task_runner.h"
#include "base/thread_task_runner_handle.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/extensions/extension_toolbar_icon_surfacing_bubble_delegate.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/manifest_constants.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()),
extension_action_api_(ExtensionActionAPI::Get(profile_)),
extensions_initialized_(false),
include_all_extensions_(FeatureSwitch::extension_action_redesign()
->IsEnabled()),
highlight_type_(HIGHLIGHT_NONE),
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_,
OnToolbarExtensionMoved(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::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE,
base::Bind(&ExtensionToolbarModel::MaybeUpdateVisibilityPrefs,
weak_ptr_factory_.GetWeakPtr()));
prefs_->SetInteger(pref_names::kToolbarSize, visible_icon_count_);
}
FOR_EACH_OBSERVER(Observer, observers_, OnToolbarVisibleCountChanged());
}
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.
if (std::find(toolbar_items_.begin(), toolbar_items_.end(), extension) !=
toolbar_items_.end()) {
FOR_EACH_OBSERVER(Observer, observers_,
OnToolbarExtensionUpdated(extension));
}
}
void ExtensionToolbarModel::OnExtensionActionVisibilityChanged(
const std::string& extension_id,
bool is_now_visible) {
const Extension* extension =
ExtensionRegistry::Get(profile_)->GetExtensionById(
extension_id, ExtensionRegistry::EVERYTHING);
// 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 (is_now_visible) {
// If this action used to be hidden, we can't possibly be showing all.
DCHECK_LT(visible_icon_count(), toolbar_items_.size());
// 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_GE(visible_icon_count(), 0u);
// Shrink the bar by one and move the extension to the beginning of the
// overflow menu.
new_size = visible_icon_count() - 1;
new_index = new_size;
}
SetVisibleIconCount(new_size);
MoveExtensionIcon(extension->id(), new_index);
} else { // Don't include all extensions.
if (is_now_visible)
AddExtension(extension);
else
RemoveExtension(extension);
}
}
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::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(extension_action_api_);
if (ExtensionToolbarIconSurfacingBubbleDelegate::ShouldShowForProfile(
profile_)) {
ExtensionIdList ids;
for (const auto& extension : toolbar_items_)
ids.push_back(extension->id());
HighlightExtensions(ids, HIGHLIGHT_INFO);
}
extensions_initialized_ = true;
FOR_EACH_OBSERVER(Observer, observers_, OnToolbarModelInitialized());
}
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) &&
extension_action_api_->GetBrowserActionVisibility(extension->id());
}
void ExtensionToolbarModel::AddExtension(const Extension* extension) {
// We only use AddExtension() once the system is initialized.
DCHECK(extensions_initialized_);
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();
// New extensions go at the right (end) of the visible extensions. Other
// extensions go at their previous position.
size_t new_index = 0;
if (is_new_extension) {
new_index = Manifest::IsComponentLocation(extension->location()) ?
0 : visible_icon_count();
// For the last-known position, we use the index of the extension that is
// just before this extension, plus one. (Note that this isn't the same
// as new_index + 1, because last_known_positions_ can include disabled
// extensions.)
int new_last_known_index =
new_index == 0 ? 0 :
std::find(last_known_positions_.begin(),
last_known_positions_.end(),
toolbar_items_[new_index - 1]->id()) -
last_known_positions_.begin() + 1;
// In theory, the extension before this one should always
// be in last known positions, but if something funny happened with prefs,
// make sure we handle it.
// TODO(devlin): Track down these cases so we can CHECK this.
new_last_known_index =
std::min<int>(new_last_known_index, last_known_positions_.size());
last_known_positions_.insert(
last_known_positions_.begin() + new_last_known_index, extension->id());
UpdatePrefs();
} else {
new_index = FindNewPositionFromLastKnownGood(extension);
}
toolbar_items_.insert(toolbar_items_.begin() + new_index, extension);
// 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_,
OnToolbarExtensionAdded(extension, new_index));
int visible_count_delta = 0;
if (is_new_extension && !all_icons_visible()) {
// If this is a new extension (and not all extensions are visible), we
// expand the toolbar out so that the new one can be seen.
visible_count_delta = 1;
} else if (profile_->IsOffTheRecord()) {
// If this is an incognito profile, we also have to check to make sure the
// overflow matches the main bar's status.
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.
size_t main_index =
main_model->FindNewPositionFromLastKnownGood(extension);
bool visible = main_index < main_model->visible_icon_count();
// 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 && !all_icons_visible())
visible_count_delta = 1;
else if (!visible && all_icons_visible())
visible_count_delta = -1;
}
if (visible_count_delta)
SetVisibleIconCount(visible_icon_count() + visible_count_delta);
}
MaybeUpdateVisibilityPref(extension, new_index);
}
void ExtensionToolbarModel::RemoveExtension(const Extension* extension) {
ExtensionList::iterator pos =
std::find(toolbar_items_.begin(), toolbar_items_.end(), extension);
if (pos == toolbar_items_.end())
return;
size_t index = pos - toolbar_items_.begin();
// If the removed extension was on the toolbar, a new one will take its place
// if there are any in overflow.
bool new_extension_shown =
!all_icons_visible() && index < visible_icon_count();
// 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_,
OnToolbarExtensionRemoved(extension));
// If the highlighted list is now empty, we stop highlighting.
if (highlighted_items_.empty())
StopHighlighting();
}
} else {
FOR_EACH_OBSERVER(Observer, observers_,
OnToolbarExtensionRemoved(extension));
}
UpdatePrefs();
if (new_extension_shown) {
size_t newly_visible_index = visible_icon_count() - 1;
MaybeUpdateVisibilityPref(toolbar_items_[newly_visible_index].get(),
newly_visible_index);
}
}
// 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() {
DCHECK(toolbar_items_.empty()); // We shouldn't have any items yet.
last_known_positions_ = extension_prefs_->GetToolbarOrder();
if (profile_->IsOffTheRecord())
IncognitoPopulate();
else
Populate(&last_known_positions_);
MaybeUpdateVisibilityPrefs();
}
void ExtensionToolbarModel::Populate(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 (!extension_action_api_->GetBrowserActionVisibility(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 {
// Unknown extension - push it to the back of unsorted, and add it to the
// list of ids at the end.
unsorted.push_back(extension);
positions->push_back(extension->id());
}
}
// Merge the lists.
sorted.insert(sorted.end(), unsorted.begin(), unsorted.end());
toolbar_items_.reserve(sorted.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()) {
// We don't notify observers of the added extension yet. Rather, observers
// should wait for the "OnToolbarModelInitialized" notification, and then
// bulk-update. (This saves a lot of bouncing-back-and-forth here, and
// allows observers to ensure that the extension system is always
// initialized before using the extensions).
toolbar_items_.push_back(extension);
}
}
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());
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_;
}
}
}
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, size_t 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();
if (visible != extension_action_api_->GetBrowserActionVisibility(
extension->id())) {
// Don't observe changes caused by ourselves.
bool was_registered = false;
if (extension_action_observer_.IsObserving(extension_action_api_)) {
was_registered = true;
extension_action_observer_.RemoveAll();
}
extension_action_api_->SetBrowserActionVisibility(extension->id(),
visible);
if (was_registered)
extension_action_observer_.Add(extension_action_api_);
}
}
}
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);
int desired_index = 0;
// Loop over the updated list of last known positions, moving any extensions
// that are in the wrong place.
for (const std::string& id : last_known_positions_) {
int current_index = GetIndexForId(id);
if (current_index == -1)
continue;
if (current_index != desired_index) {
scoped_refptr<const Extension> extension = toolbar_items_[current_index];
toolbar_items_.erase(toolbar_items_.begin() + current_index);
toolbar_items_.insert(toolbar_items_.begin() + desired_index, extension);
// Notify the observers to keep them up-to-date.
FOR_EACH_OBSERVER(
Observer, observers_,
OnToolbarExtensionMoved(extension.get(), desired_index));
}
++desired_index;
}
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::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::Bind(&ExtensionToolbarModel::UpdatePrefs,
weak_ptr_factory_.GetWeakPtr()));
}
}
int ExtensionToolbarModel::GetIndexForId(const std::string& id) const {
for (size_t i = 0; i < toolbar_items().size(); ++i) {
if (toolbar_items()[i]->id() == id)
return i;
}
return -1;
}
bool ExtensionToolbarModel::ShowExtensionActionPopup(
const Extension* extension,
Browser* browser,
bool grant_active_tab) {
base::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 (all_icons_visible())
return; // Already showing all.
// Otherwise, make sure we have enough room to show all the extensions
// requested.
if (visible_icon_count() < extension_ids.size())
SetVisibleIconCount(extension_ids.size());
if (all_icons_visible())
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() >=
static_cast<int>(visible_icon_count()))
MoveExtensionIcon((*extension)->id(), 0);
break;
}
}
}
}
bool ExtensionToolbarModel::HighlightExtensions(
const ExtensionIdList& extension_ids,
HighlightType highlight_type) {
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()) {
// It's important that is_highlighting_ is changed immediately before the
// observers are notified since it changes the result of toolbar_items().
highlight_type_ = highlight_type;
FOR_EACH_OBSERVER(Observer, observers_,
OnToolbarHighlightModeChanged(true));
// We set the visible icon count after the highlight mode change because
// the UI actions are created/destroyed during highlight, and doing that
// prior to changing the size allows us to still have smooth animations.
if (visible_icon_count() < extension_ids.size())
SetVisibleIconCount(extension_ids.size());
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()) {
// It's important that is_highlighting_ is changed immediately before the
// observers are notified since it changes the result of toolbar_items().
highlight_type_ = HIGHLIGHT_NONE;
FOR_EACH_OBSERVER(Observer, observers_,
OnToolbarHighlightModeChanged(false));
// For the same reason, we don't clear highlighted_items_ until after the
// mode changed.
highlighted_items_.clear();
// We set the visible icon count after the highlight mode change because
// the UI actions are created/destroyed during highlight, and doing that
// prior to changing the size allows us to still have smooth animations.
int saved_icon_count = prefs_->GetInteger(pref_names::kToolbarSize);
if (saved_icon_count != visible_icon_count_)
SetVisibleIconCount(saved_icon_count);
}
}
bool ExtensionToolbarModel::RedesignIsShowingNewIcons() const {
for (const scoped_refptr<const Extension>& extension : toolbar_items_) {
// Without the redesign, we only show extensions with browser actions.
// Any extension without a browser action is an indication that we're
// showing something new.
if (!extension->manifest()->HasKey(manifest_keys::kBrowserAction))
return true;
}
return false;
}
} // namespace extensions