Pin Chrome's shortcut to the Win10 Start menu on install and OS upgrade.
[chromium-blink-merge.git] / chrome / browser / extensions / extension_toolbar_model.cc
bloba50998db3f9c2af102082cddaf041375903a7f05
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.
5 #include "chrome/browser/extensions/extension_toolbar_model.h"
7 #include <algorithm>
8 #include <string>
10 #include "base/location.h"
11 #include "base/metrics/histogram.h"
12 #include "base/metrics/histogram_base.h"
13 #include "base/prefs/pref_service.h"
14 #include "base/single_thread_task_runner.h"
15 #include "base/thread_task_runner_handle.h"
16 #include "chrome/browser/chrome_notification_types.h"
17 #include "chrome/browser/extensions/api/extension_action/extension_action_api.h"
18 #include "chrome/browser/extensions/extension_action_manager.h"
19 #include "chrome/browser/extensions/extension_tab_util.h"
20 #include "chrome/browser/extensions/extension_toolbar_model_factory.h"
21 #include "chrome/browser/extensions/extension_util.h"
22 #include "chrome/browser/extensions/tab_helper.h"
23 #include "chrome/browser/profiles/profile.h"
24 #include "chrome/browser/ui/browser.h"
25 #include "chrome/browser/ui/extensions/extension_toolbar_icon_surfacing_bubble_delegate.h"
26 #include "chrome/browser/ui/tabs/tab_strip_model.h"
27 #include "content/public/browser/notification_details.h"
28 #include "content/public/browser/notification_source.h"
29 #include "content/public/browser/web_contents.h"
30 #include "extensions/browser/extension_prefs.h"
31 #include "extensions/browser/extension_registry.h"
32 #include "extensions/browser/extension_system.h"
33 #include "extensions/browser/pref_names.h"
34 #include "extensions/common/extension.h"
35 #include "extensions/common/extension_set.h"
36 #include "extensions/common/feature_switch.h"
37 #include "extensions/common/manifest_constants.h"
38 #include "extensions/common/one_shot_event.h"
40 namespace extensions {
42 ExtensionToolbarModel::ExtensionToolbarModel(Profile* profile,
43 ExtensionPrefs* extension_prefs)
44 : profile_(profile),
45 extension_prefs_(extension_prefs),
46 prefs_(profile_->GetPrefs()),
47 extension_action_api_(ExtensionActionAPI::Get(profile_)),
48 extensions_initialized_(false),
49 include_all_extensions_(FeatureSwitch::extension_action_redesign()
50 ->IsEnabled()),
51 highlight_type_(HIGHLIGHT_NONE),
52 extension_action_observer_(this),
53 extension_registry_observer_(this),
54 weak_ptr_factory_(this) {
55 ExtensionSystem::Get(profile_)->ready().Post(
56 FROM_HERE,
57 base::Bind(&ExtensionToolbarModel::OnReady,
58 weak_ptr_factory_.GetWeakPtr()));
59 visible_icon_count_ = prefs_->GetInteger(pref_names::kToolbarSize);
61 // We only care about watching the prefs if not in incognito mode.
62 if (!profile_->IsOffTheRecord()) {
63 pref_change_registrar_.Init(prefs_);
64 pref_change_callback_ =
65 base::Bind(&ExtensionToolbarModel::OnExtensionToolbarPrefChange,
66 base::Unretained(this));
67 pref_change_registrar_.Add(pref_names::kToolbar, pref_change_callback_);
71 ExtensionToolbarModel::~ExtensionToolbarModel() {
74 // static
75 ExtensionToolbarModel* ExtensionToolbarModel::Get(Profile* profile) {
76 return ExtensionToolbarModelFactory::GetForProfile(profile);
79 void ExtensionToolbarModel::AddObserver(Observer* observer) {
80 observers_.AddObserver(observer);
83 void ExtensionToolbarModel::RemoveObserver(Observer* observer) {
84 observers_.RemoveObserver(observer);
87 void ExtensionToolbarModel::MoveExtensionIcon(const std::string& id,
88 size_t index) {
89 ExtensionList::iterator pos = toolbar_items_.begin();
90 while (pos != toolbar_items_.end() && (*pos)->id() != id)
91 ++pos;
92 if (pos == toolbar_items_.end()) {
93 NOTREACHED();
94 return;
96 scoped_refptr<const Extension> extension = *pos;
97 toolbar_items_.erase(pos);
99 ExtensionIdList::iterator pos_id = std::find(last_known_positions_.begin(),
100 last_known_positions_.end(),
101 id);
102 if (pos_id != last_known_positions_.end())
103 last_known_positions_.erase(pos_id);
105 if (index < toolbar_items_.size()) {
106 // If the index is not at the end, find the item currently at |index|, and
107 // insert |extension| before it in both |toolbar_items_| and
108 // |last_known_positions_|.
109 ExtensionList::iterator iter = toolbar_items_.begin() + index;
110 last_known_positions_.insert(std::find(last_known_positions_.begin(),
111 last_known_positions_.end(),
112 (*iter)->id()),
113 id);
114 toolbar_items_.insert(iter, extension);
115 } else {
116 // Otherwise, put |extension| at the end.
117 DCHECK_EQ(toolbar_items_.size(), index);
118 index = toolbar_items_.size();
119 toolbar_items_.push_back(extension);
120 last_known_positions_.push_back(id);
123 FOR_EACH_OBSERVER(Observer, observers_,
124 OnToolbarExtensionMoved(extension.get(), index));
125 MaybeUpdateVisibilityPref(extension.get(), index);
126 UpdatePrefs();
129 void ExtensionToolbarModel::SetVisibleIconCount(size_t count) {
130 visible_icon_count_ = (count >= toolbar_items_.size()) ? -1 : count;
132 // Only set the prefs if we're not in highlight mode and the profile is not
133 // incognito. Highlight mode is designed to be a transitory state, and should
134 // not persist across browser restarts (though it may be re-entered), and we
135 // don't store anything in incognito.
136 if (!is_highlighting() && !profile_->IsOffTheRecord()) {
137 // Additionally, if we are using the new toolbar, any icons which are in the
138 // overflow menu are considered "hidden". But it so happens that the times
139 // we are likely to call SetVisibleIconCount() are also those when we are
140 // in flux. So wait for things to cool down before setting the prefs.
141 base::ThreadTaskRunnerHandle::Get()->PostTask(
142 FROM_HERE,
143 base::Bind(&ExtensionToolbarModel::MaybeUpdateVisibilityPrefs,
144 weak_ptr_factory_.GetWeakPtr()));
145 prefs_->SetInteger(pref_names::kToolbarSize, visible_icon_count_);
148 FOR_EACH_OBSERVER(Observer, observers_, OnToolbarVisibleCountChanged());
151 void ExtensionToolbarModel::OnExtensionActionUpdated(
152 ExtensionAction* extension_action,
153 content::WebContents* web_contents,
154 content::BrowserContext* browser_context) {
155 const Extension* extension =
156 ExtensionRegistry::Get(profile_)->enabled_extensions().GetByID(
157 extension_action->extension_id());
158 // Notify observers if the extension exists and is in the model.
159 if (std::find(toolbar_items_.begin(), toolbar_items_.end(), extension) !=
160 toolbar_items_.end()) {
161 FOR_EACH_OBSERVER(Observer, observers_,
162 OnToolbarExtensionUpdated(extension));
166 void ExtensionToolbarModel::OnExtensionActionVisibilityChanged(
167 const std::string& extension_id,
168 bool is_now_visible) {
169 const Extension* extension =
170 ExtensionRegistry::Get(profile_)->GetExtensionById(
171 extension_id, ExtensionRegistry::EVERYTHING);
173 // Hiding works differently with the new and old toolbars.
174 if (include_all_extensions_) {
175 // It's possible that we haven't added this extension yet, if its
176 // visibility was adjusted in the course of its initialization.
177 if (std::find(toolbar_items_.begin(), toolbar_items_.end(), extension) ==
178 toolbar_items_.end())
179 return;
181 int new_size = 0;
182 int new_index = 0;
183 if (is_now_visible) {
184 // If this action used to be hidden, we can't possibly be showing all.
185 DCHECK_LT(visible_icon_count(), toolbar_items_.size());
186 // Grow the bar by one and move the extension to the end of the visibles.
187 new_size = visible_icon_count() + 1;
188 new_index = new_size - 1;
189 } else {
190 // If we're hiding one, we must be showing at least one.
191 DCHECK_GE(visible_icon_count(), 0u);
192 // Shrink the bar by one and move the extension to the beginning of the
193 // overflow menu.
194 new_size = visible_icon_count() - 1;
195 new_index = new_size;
197 SetVisibleIconCount(new_size);
198 MoveExtensionIcon(extension->id(), new_index);
199 } else { // Don't include all extensions.
200 if (is_now_visible)
201 AddExtension(extension);
202 else
203 RemoveExtension(extension);
207 void ExtensionToolbarModel::OnExtensionLoaded(
208 content::BrowserContext* browser_context,
209 const Extension* extension) {
210 // We don't want to add the same extension twice. It may have already been
211 // added by EXTENSION_BROWSER_ACTION_VISIBILITY_CHANGED below, if the user
212 // hides the browser action and then disables and enables the extension.
213 for (size_t i = 0; i < toolbar_items_.size(); i++) {
214 if (toolbar_items_[i].get() == extension)
215 return;
218 AddExtension(extension);
221 void ExtensionToolbarModel::OnExtensionUnloaded(
222 content::BrowserContext* browser_context,
223 const Extension* extension,
224 UnloadedExtensionInfo::Reason reason) {
225 RemoveExtension(extension);
228 void ExtensionToolbarModel::OnExtensionUninstalled(
229 content::BrowserContext* browser_context,
230 const Extension* extension,
231 extensions::UninstallReason reason) {
232 // Remove the extension id from the ordered list, if it exists (the extension
233 // might not be represented in the list because it might not have an icon).
234 ExtensionIdList::iterator pos =
235 std::find(last_known_positions_.begin(),
236 last_known_positions_.end(), extension->id());
238 if (pos != last_known_positions_.end()) {
239 last_known_positions_.erase(pos);
240 UpdatePrefs();
244 void ExtensionToolbarModel::OnReady() {
245 ExtensionRegistry* registry = ExtensionRegistry::Get(profile_);
246 InitializeExtensionList();
247 // Wait until the extension system is ready before observing any further
248 // changes so that the toolbar buttons can be shown in their stable ordering
249 // taken from prefs.
250 extension_registry_observer_.Add(registry);
251 extension_action_observer_.Add(extension_action_api_);
253 if (ExtensionToolbarIconSurfacingBubbleDelegate::ShouldShowForProfile(
254 profile_)) {
255 ExtensionIdList ids;
256 for (const auto& extension : toolbar_items_)
257 ids.push_back(extension->id());
258 HighlightExtensions(ids, HIGHLIGHT_INFO);
261 extensions_initialized_ = true;
262 FOR_EACH_OBSERVER(Observer, observers_, OnToolbarModelInitialized());
265 size_t ExtensionToolbarModel::FindNewPositionFromLastKnownGood(
266 const Extension* extension) {
267 // See if we have last known good position for this extension.
268 size_t new_index = 0;
269 // Loop through the ID list of known positions, to count the number of visible
270 // extension icons preceding |extension|.
271 for (ExtensionIdList::const_iterator iter_id = last_known_positions_.begin();
272 iter_id < last_known_positions_.end(); ++iter_id) {
273 if ((*iter_id) == extension->id())
274 return new_index; // We've found the right position.
275 // Found an id, need to see if it is visible.
276 for (ExtensionList::const_iterator iter_ext = toolbar_items_.begin();
277 iter_ext < toolbar_items_.end(); ++iter_ext) {
278 if ((*iter_ext)->id() == (*iter_id)) {
279 // This extension is visible, update the index value.
280 ++new_index;
281 break;
286 // Position not found.
287 return toolbar_items_.size();
290 bool ExtensionToolbarModel::ShouldAddExtension(const Extension* extension) {
291 // In incognito mode, don't add any extensions that aren't incognito-enabled.
292 if (profile_->IsOffTheRecord() &&
293 !util::IsIncognitoEnabled(extension->id(), profile_))
294 return false;
296 ExtensionActionManager* action_manager =
297 ExtensionActionManager::Get(profile_);
298 if (include_all_extensions_) {
299 // In this case, we don't care about the browser action visibility, because
300 // we want to show each extension regardless.
301 // TODO(devlin): Extension actions which are not visible should be moved to
302 // the overflow menu by default.
303 return action_manager->GetExtensionAction(*extension) != NULL;
306 return action_manager->GetBrowserAction(*extension) &&
307 extension_action_api_->GetBrowserActionVisibility(extension->id());
310 void ExtensionToolbarModel::AddExtension(const Extension* extension) {
311 // We only use AddExtension() once the system is initialized.
312 DCHECK(extensions_initialized_);
313 if (!ShouldAddExtension(extension))
314 return;
316 // See if we have a last known good position for this extension.
317 bool is_new_extension =
318 std::find(last_known_positions_.begin(),
319 last_known_positions_.end(),
320 extension->id()) == last_known_positions_.end();
322 // New extensions go at the right (end) of the visible extensions. Other
323 // extensions go at their previous position.
324 size_t new_index = 0;
325 if (is_new_extension) {
326 new_index = Manifest::IsComponentLocation(extension->location()) ?
327 0 : visible_icon_count();
328 // For the last-known position, we use the index of the extension that is
329 // just before this extension, plus one. (Note that this isn't the same
330 // as new_index + 1, because last_known_positions_ can include disabled
331 // extensions.)
332 int new_last_known_index =
333 new_index == 0 ? 0 :
334 std::find(last_known_positions_.begin(),
335 last_known_positions_.end(),
336 toolbar_items_[new_index - 1]->id()) -
337 last_known_positions_.begin() + 1;
338 // In theory, the extension before this one should always
339 // be in last known positions, but if something funny happened with prefs,
340 // make sure we handle it.
341 // TODO(devlin): Track down these cases so we can CHECK this.
342 new_last_known_index =
343 std::min<int>(new_last_known_index, last_known_positions_.size());
344 last_known_positions_.insert(
345 last_known_positions_.begin() + new_last_known_index, extension->id());
346 UpdatePrefs();
347 } else {
348 new_index = FindNewPositionFromLastKnownGood(extension);
351 toolbar_items_.insert(toolbar_items_.begin() + new_index, extension);
353 // If we're currently highlighting, then even though we add a browser action
354 // to the full list (|toolbar_items_|, there won't be another *visible*
355 // browser action, which was what the observers care about.
356 if (!is_highlighting()) {
357 FOR_EACH_OBSERVER(Observer, observers_,
358 OnToolbarExtensionAdded(extension, new_index));
360 int visible_count_delta = 0;
361 if (is_new_extension && !all_icons_visible()) {
362 // If this is a new extension (and not all extensions are visible), we
363 // expand the toolbar out so that the new one can be seen.
364 visible_count_delta = 1;
365 } else if (profile_->IsOffTheRecord()) {
366 // If this is an incognito profile, we also have to check to make sure the
367 // overflow matches the main bar's status.
368 ExtensionToolbarModel* main_model =
369 ExtensionToolbarModel::Get(profile_->GetOriginalProfile());
370 // Find what the index will be in the main bar. Because Observer calls are
371 // nondeterministic, we can't just assume the main bar will have the
372 // extension and look it up.
373 size_t main_index =
374 main_model->FindNewPositionFromLastKnownGood(extension);
375 bool visible = main_index < main_model->visible_icon_count();
376 // We may need to adjust the visible count if the incognito bar isn't
377 // showing all icons and this one is visible, or if it is showing all
378 // icons and this is hidden.
379 if (visible && !all_icons_visible())
380 visible_count_delta = 1;
381 else if (!visible && all_icons_visible())
382 visible_count_delta = -1;
385 if (visible_count_delta)
386 SetVisibleIconCount(visible_icon_count() + visible_count_delta);
389 MaybeUpdateVisibilityPref(extension, new_index);
392 void ExtensionToolbarModel::RemoveExtension(const Extension* extension) {
393 ExtensionList::iterator pos =
394 std::find(toolbar_items_.begin(), toolbar_items_.end(), extension);
395 if (pos == toolbar_items_.end())
396 return;
398 // If our visible count is set to the current size, we need to decrement it.
399 if (visible_icon_count_ == static_cast<int>(toolbar_items_.size()))
400 SetVisibleIconCount(toolbar_items_.size() - 1);
402 toolbar_items_.erase(pos);
404 // If we're in highlight mode, we also have to remove the extension from
405 // the highlighted list.
406 if (is_highlighting()) {
407 pos = std::find(highlighted_items_.begin(),
408 highlighted_items_.end(),
409 extension);
410 if (pos != highlighted_items_.end()) {
411 highlighted_items_.erase(pos);
412 FOR_EACH_OBSERVER(Observer, observers_,
413 OnToolbarExtensionRemoved(extension));
414 // If the highlighted list is now empty, we stop highlighting.
415 if (highlighted_items_.empty())
416 StopHighlighting();
418 } else {
419 FOR_EACH_OBSERVER(Observer, observers_,
420 OnToolbarExtensionRemoved(extension));
423 UpdatePrefs();
426 // Combine the currently enabled extensions that have browser actions (which
427 // we get from the ExtensionRegistry) with the ordering we get from the
428 // pref service. For robustness we use a somewhat inefficient process:
429 // 1. Create a vector of extensions sorted by their pref values. This vector may
430 // have holes.
431 // 2. Create a vector of extensions that did not have a pref value.
432 // 3. Remove holes from the sorted vector and append the unsorted vector.
433 void ExtensionToolbarModel::InitializeExtensionList() {
434 DCHECK(toolbar_items_.empty()); // We shouldn't have any items yet.
436 last_known_positions_ = extension_prefs_->GetToolbarOrder();
437 if (profile_->IsOffTheRecord())
438 IncognitoPopulate();
439 else
440 Populate(&last_known_positions_);
442 MaybeUpdateVisibilityPrefs();
445 void ExtensionToolbarModel::Populate(ExtensionIdList* positions) {
446 DCHECK(!profile_->IsOffTheRecord());
447 const ExtensionSet& extensions =
448 ExtensionRegistry::Get(profile_)->enabled_extensions();
449 // Items that have explicit positions.
450 ExtensionList sorted(positions->size(), NULL);
451 // The items that don't have explicit positions.
452 ExtensionList unsorted;
454 // Create the lists.
455 int hidden = 0;
456 for (const scoped_refptr<const Extension>& extension : extensions) {
457 if (!ShouldAddExtension(extension.get())) {
458 if (!extension_action_api_->GetBrowserActionVisibility(extension->id()))
459 ++hidden;
460 continue;
463 ExtensionIdList::const_iterator pos =
464 std::find(positions->begin(), positions->end(), extension->id());
465 if (pos != positions->end()) {
466 sorted[pos - positions->begin()] = extension;
467 } else {
468 // Unknown extension - push it to the back of unsorted, and add it to the
469 // list of ids at the end.
470 unsorted.push_back(extension);
471 positions->push_back(extension->id());
475 // Merge the lists.
476 sorted.insert(sorted.end(), unsorted.begin(), unsorted.end());
477 toolbar_items_.reserve(sorted.size());
479 for (const scoped_refptr<const Extension>& extension : sorted) {
480 // It's possible for the extension order to contain items that aren't
481 // actually loaded on this machine. For example, when extension sync is on,
482 // we sync the extension order as-is but double-check with the user before
483 // syncing NPAPI-containing extensions, so if one of those is not actually
484 // synced, we'll get a NULL in the list. This sort of case can also happen
485 // if some error prevents an extension from loading.
486 if (extension.get()) {
487 // We don't notify observers of the added extension yet. Rather, observers
488 // should wait for the "OnToolbarModelInitialized" notification, and then
489 // bulk-update. (This saves a lot of bouncing-back-and-forth here, and
490 // allows observers to ensure that the extension system is always
491 // initialized before using the extensions).
492 toolbar_items_.push_back(extension);
496 UMA_HISTOGRAM_COUNTS_100(
497 "ExtensionToolbarModel.BrowserActionsPermanentlyHidden", hidden);
498 UMA_HISTOGRAM_COUNTS_100("ExtensionToolbarModel.BrowserActionsCount",
499 toolbar_items_.size());
501 if (!toolbar_items_.empty()) {
502 // Visible count can be -1, meaning: 'show all'. Since UMA converts negative
503 // values to 0, this would be counted as 'show none' unless we convert it to
504 // max.
505 UMA_HISTOGRAM_COUNTS_100("ExtensionToolbarModel.BrowserActionsVisible",
506 visible_icon_count_ == -1 ?
507 base::HistogramBase::kSampleType_MAX :
508 visible_icon_count_);
512 void ExtensionToolbarModel::IncognitoPopulate() {
513 DCHECK(profile_->IsOffTheRecord());
514 const ExtensionToolbarModel* original_model =
515 ExtensionToolbarModel::Get(profile_->GetOriginalProfile());
517 // Find the absolute value of the original model's count.
518 int original_visible = original_model->visible_icon_count();
520 // In incognito mode, we show only those extensions that are
521 // incognito-enabled. Further, any actions that were overflowed in regular
522 // mode are still overflowed. Order is the same as in regular mode.
523 visible_icon_count_ = 0;
524 for (ExtensionList::const_iterator iter =
525 original_model->toolbar_items_.begin();
526 iter != original_model->toolbar_items_.end(); ++iter) {
527 if (ShouldAddExtension(iter->get())) {
528 toolbar_items_.push_back(*iter);
529 if (iter - original_model->toolbar_items_.begin() < original_visible)
530 ++visible_icon_count_;
535 void ExtensionToolbarModel::UpdatePrefs() {
536 if (!extension_prefs_ || profile_->IsOffTheRecord())
537 return;
539 // Don't observe change caused by self.
540 pref_change_registrar_.Remove(pref_names::kToolbar);
541 extension_prefs_->SetToolbarOrder(last_known_positions_);
542 pref_change_registrar_.Add(pref_names::kToolbar, pref_change_callback_);
545 void ExtensionToolbarModel::MaybeUpdateVisibilityPref(
546 const Extension* extension, size_t index) {
547 // We only update the visibility pref for hidden/not hidden based on the
548 // overflow menu with the new toolbar design.
549 if (include_all_extensions_ && !profile_->IsOffTheRecord()) {
550 bool visible = index < visible_icon_count();
551 if (visible != extension_action_api_->GetBrowserActionVisibility(
552 extension->id())) {
553 // Don't observe changes caused by ourselves.
554 bool was_registered = false;
555 if (extension_action_observer_.IsObserving(extension_action_api_)) {
556 was_registered = true;
557 extension_action_observer_.RemoveAll();
559 extension_action_api_->SetBrowserActionVisibility(extension->id(),
560 visible);
561 if (was_registered)
562 extension_action_observer_.Add(extension_action_api_);
567 void ExtensionToolbarModel::MaybeUpdateVisibilityPrefs() {
568 for (size_t i = 0u; i < toolbar_items_.size(); ++i)
569 MaybeUpdateVisibilityPref(toolbar_items_[i].get(), i);
572 void ExtensionToolbarModel::OnExtensionToolbarPrefChange() {
573 // If extensions are not ready, defer to later Populate() call.
574 if (!extensions_initialized_)
575 return;
577 // Recalculate |last_known_positions_| to be |pref_positions| followed by
578 // ones that are only in |last_known_positions_|.
579 ExtensionIdList pref_positions = extension_prefs_->GetToolbarOrder();
580 size_t pref_position_size = pref_positions.size();
581 for (size_t i = 0; i < last_known_positions_.size(); ++i) {
582 if (std::find(pref_positions.begin(), pref_positions.end(),
583 last_known_positions_[i]) == pref_positions.end()) {
584 pref_positions.push_back(last_known_positions_[i]);
587 last_known_positions_.swap(pref_positions);
589 int desired_index = 0;
590 // Loop over the updated list of last known positions, moving any extensions
591 // that are in the wrong place.
592 for (const std::string& id : last_known_positions_) {
593 int current_index = GetIndexForId(id);
594 if (current_index == -1)
595 continue;
596 if (current_index != desired_index) {
597 scoped_refptr<const Extension> extension = toolbar_items_[current_index];
598 toolbar_items_.erase(toolbar_items_.begin() + current_index);
599 toolbar_items_.insert(toolbar_items_.begin() + desired_index, extension);
600 // Notify the observers to keep them up-to-date.
601 FOR_EACH_OBSERVER(
602 Observer, observers_,
603 OnToolbarExtensionMoved(extension.get(), desired_index));
605 ++desired_index;
608 if (last_known_positions_.size() > pref_position_size) {
609 // Need to update pref because we have extra icons. But can't call
610 // UpdatePrefs() directly within observation closure.
611 base::ThreadTaskRunnerHandle::Get()->PostTask(
612 FROM_HERE, base::Bind(&ExtensionToolbarModel::UpdatePrefs,
613 weak_ptr_factory_.GetWeakPtr()));
617 int ExtensionToolbarModel::GetIndexForId(const std::string& id) const {
618 for (size_t i = 0; i < toolbar_items().size(); ++i) {
619 if (toolbar_items()[i]->id() == id)
620 return i;
622 return -1;
625 bool ExtensionToolbarModel::ShowExtensionActionPopup(
626 const Extension* extension,
627 Browser* browser,
628 bool grant_active_tab) {
629 base::ObserverListBase<Observer>::Iterator it(&observers_);
630 Observer* obs = NULL;
631 // Look for the Observer associated with the browser.
632 // This would be cleaner if we had an abstract class for the Toolbar UI
633 // (like we do for LocationBar), but sadly, we don't.
634 while ((obs = it.GetNext()) != NULL) {
635 if (obs->GetBrowser() == browser)
636 return obs->ShowExtensionActionPopup(extension, grant_active_tab);
638 return false;
641 void ExtensionToolbarModel::EnsureVisibility(
642 const ExtensionIdList& extension_ids) {
643 if (all_icons_visible())
644 return; // Already showing all.
646 // Otherwise, make sure we have enough room to show all the extensions
647 // requested.
648 if (visible_icon_count() < extension_ids.size())
649 SetVisibleIconCount(extension_ids.size());
651 if (all_icons_visible())
652 return; // May have been set to max by SetVisibleIconCount.
654 // Guillotine's Delight: Move an orange noble to the front of the line.
655 for (ExtensionIdList::const_iterator it = extension_ids.begin();
656 it != extension_ids.end(); ++it) {
657 for (ExtensionList::const_iterator extension = toolbar_items_.begin();
658 extension != toolbar_items_.end(); ++extension) {
659 if ((*extension)->id() == (*it)) {
660 if (extension - toolbar_items_.begin() >=
661 static_cast<int>(visible_icon_count()))
662 MoveExtensionIcon((*extension)->id(), 0);
663 break;
669 bool ExtensionToolbarModel::HighlightExtensions(
670 const ExtensionIdList& extension_ids,
671 HighlightType highlight_type) {
672 highlighted_items_.clear();
674 for (ExtensionIdList::const_iterator id = extension_ids.begin();
675 id != extension_ids.end();
676 ++id) {
677 for (ExtensionList::const_iterator extension = toolbar_items_.begin();
678 extension != toolbar_items_.end();
679 ++extension) {
680 if (*id == (*extension)->id())
681 highlighted_items_.push_back(*extension);
685 // If we have any items in |highlighted_items_|, then we entered highlighting
686 // mode.
687 if (highlighted_items_.size()) {
688 // It's important that is_highlighting_ is changed immediately before the
689 // observers are notified since it changes the result of toolbar_items().
690 highlight_type_ = highlight_type;
691 FOR_EACH_OBSERVER(Observer, observers_,
692 OnToolbarHighlightModeChanged(true));
694 // We set the visible icon count after the highlight mode change because
695 // the UI actions are created/destroyed during highlight, and doing that
696 // prior to changing the size allows us to still have smooth animations.
697 if (visible_icon_count() < extension_ids.size())
698 SetVisibleIconCount(extension_ids.size());
700 return true;
703 // Otherwise, we didn't enter highlighting mode (and, in fact, exited it if
704 // we were otherwise in it).
705 if (is_highlighting())
706 StopHighlighting();
707 return false;
710 void ExtensionToolbarModel::StopHighlighting() {
711 if (is_highlighting()) {
712 // It's important that is_highlighting_ is changed immediately before the
713 // observers are notified since it changes the result of toolbar_items().
714 highlight_type_ = HIGHLIGHT_NONE;
715 FOR_EACH_OBSERVER(Observer, observers_,
716 OnToolbarHighlightModeChanged(false));
718 // For the same reason, we don't clear highlighted_items_ until after the
719 // mode changed.
720 highlighted_items_.clear();
722 // We set the visible icon count after the highlight mode change because
723 // the UI actions are created/destroyed during highlight, and doing that
724 // prior to changing the size allows us to still have smooth animations.
725 int saved_icon_count = prefs_->GetInteger(pref_names::kToolbarSize);
726 if (saved_icon_count != visible_icon_count_)
727 SetVisibleIconCount(saved_icon_count);
731 bool ExtensionToolbarModel::RedesignIsShowingNewIcons() const {
732 for (const scoped_refptr<const Extension>& extension : toolbar_items_) {
733 // Without the redesign, we only show extensions with browser actions.
734 // Any extension without a browser action is an indication that we're
735 // showing something new.
736 if (!extension->manifest()->HasKey(manifest_keys::kBrowserAction))
737 return true;
739 return false;
742 } // namespace extensions