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"
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
)
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()
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(
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() {
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
,
89 ExtensionList::iterator pos
= toolbar_items_
.begin();
90 while (pos
!= toolbar_items_
.end() && (*pos
)->id() != id
)
92 if (pos
== toolbar_items_
.end()) {
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(),
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(),
114 toolbar_items_
.insert(iter
, extension
);
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
);
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(
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())
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;
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
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.
201 AddExtension(extension
);
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
)
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
);
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
250 extension_registry_observer_
.Add(registry
);
251 extension_action_observer_
.Add(extension_action_api_
);
253 if (ExtensionToolbarIconSurfacingBubbleDelegate::ShouldShowForProfile(
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.
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_
))
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
))
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
332 int new_last_known_index
=
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());
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.
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())
398 size_t index
= pos
- toolbar_items_
.begin();
399 // If the removed extension was on the toolbar, a new one will take its place
400 // if there are any in overflow.
401 bool new_extension_shown
=
402 !all_icons_visible() && index
< visible_icon_count();
404 // If our visible count is set to the current size, we need to decrement it.
405 if (visible_icon_count_
== static_cast<int>(toolbar_items_
.size()))
406 SetVisibleIconCount(toolbar_items_
.size() - 1);
408 toolbar_items_
.erase(pos
);
410 // If we're in highlight mode, we also have to remove the extension from
411 // the highlighted list.
412 if (is_highlighting()) {
413 pos
= std::find(highlighted_items_
.begin(),
414 highlighted_items_
.end(),
416 if (pos
!= highlighted_items_
.end()) {
417 highlighted_items_
.erase(pos
);
418 FOR_EACH_OBSERVER(Observer
, observers_
,
419 OnToolbarExtensionRemoved(extension
));
420 // If the highlighted list is now empty, we stop highlighting.
421 if (highlighted_items_
.empty())
425 FOR_EACH_OBSERVER(Observer
, observers_
,
426 OnToolbarExtensionRemoved(extension
));
430 if (new_extension_shown
) {
431 size_t newly_visible_index
= visible_icon_count() - 1;
432 MaybeUpdateVisibilityPref(toolbar_items_
[newly_visible_index
].get(),
433 newly_visible_index
);
437 // Combine the currently enabled extensions that have browser actions (which
438 // we get from the ExtensionRegistry) with the ordering we get from the
439 // pref service. For robustness we use a somewhat inefficient process:
440 // 1. Create a vector of extensions sorted by their pref values. This vector may
442 // 2. Create a vector of extensions that did not have a pref value.
443 // 3. Remove holes from the sorted vector and append the unsorted vector.
444 void ExtensionToolbarModel::InitializeExtensionList() {
445 DCHECK(toolbar_items_
.empty()); // We shouldn't have any items yet.
447 last_known_positions_
= extension_prefs_
->GetToolbarOrder();
448 if (profile_
->IsOffTheRecord())
451 Populate(&last_known_positions_
);
453 MaybeUpdateVisibilityPrefs();
456 void ExtensionToolbarModel::Populate(ExtensionIdList
* positions
) {
457 DCHECK(!profile_
->IsOffTheRecord());
458 const ExtensionSet
& extensions
=
459 ExtensionRegistry::Get(profile_
)->enabled_extensions();
460 // Items that have explicit positions.
461 ExtensionList
sorted(positions
->size(), NULL
);
462 // The items that don't have explicit positions.
463 ExtensionList unsorted
;
467 for (const scoped_refptr
<const Extension
>& extension
: extensions
) {
468 if (!ShouldAddExtension(extension
.get())) {
469 if (!extension_action_api_
->GetBrowserActionVisibility(extension
->id()))
474 ExtensionIdList::const_iterator pos
=
475 std::find(positions
->begin(), positions
->end(), extension
->id());
476 if (pos
!= positions
->end()) {
477 sorted
[pos
- positions
->begin()] = extension
;
479 // Unknown extension - push it to the back of unsorted, and add it to the
480 // list of ids at the end.
481 unsorted
.push_back(extension
);
482 positions
->push_back(extension
->id());
487 sorted
.insert(sorted
.end(), unsorted
.begin(), unsorted
.end());
488 toolbar_items_
.reserve(sorted
.size());
490 for (const scoped_refptr
<const Extension
>& extension
: sorted
) {
491 // It's possible for the extension order to contain items that aren't
492 // actually loaded on this machine. For example, when extension sync is on,
493 // we sync the extension order as-is but double-check with the user before
494 // syncing NPAPI-containing extensions, so if one of those is not actually
495 // synced, we'll get a NULL in the list. This sort of case can also happen
496 // if some error prevents an extension from loading.
497 if (extension
.get()) {
498 // We don't notify observers of the added extension yet. Rather, observers
499 // should wait for the "OnToolbarModelInitialized" notification, and then
500 // bulk-update. (This saves a lot of bouncing-back-and-forth here, and
501 // allows observers to ensure that the extension system is always
502 // initialized before using the extensions).
503 toolbar_items_
.push_back(extension
);
507 UMA_HISTOGRAM_COUNTS_100(
508 "ExtensionToolbarModel.BrowserActionsPermanentlyHidden", hidden
);
509 UMA_HISTOGRAM_COUNTS_100("ExtensionToolbarModel.BrowserActionsCount",
510 toolbar_items_
.size());
512 if (!toolbar_items_
.empty()) {
513 // Visible count can be -1, meaning: 'show all'. Since UMA converts negative
514 // values to 0, this would be counted as 'show none' unless we convert it to
516 UMA_HISTOGRAM_COUNTS_100("ExtensionToolbarModel.BrowserActionsVisible",
517 visible_icon_count_
== -1 ?
518 base::HistogramBase::kSampleType_MAX
:
519 visible_icon_count_
);
523 void ExtensionToolbarModel::IncognitoPopulate() {
524 DCHECK(profile_
->IsOffTheRecord());
525 const ExtensionToolbarModel
* original_model
=
526 ExtensionToolbarModel::Get(profile_
->GetOriginalProfile());
528 // Find the absolute value of the original model's count.
529 int original_visible
= original_model
->visible_icon_count();
531 // In incognito mode, we show only those extensions that are
532 // incognito-enabled. Further, any actions that were overflowed in regular
533 // mode are still overflowed. Order is the same as in regular mode.
534 visible_icon_count_
= 0;
535 for (ExtensionList::const_iterator iter
=
536 original_model
->toolbar_items_
.begin();
537 iter
!= original_model
->toolbar_items_
.end(); ++iter
) {
538 if (ShouldAddExtension(iter
->get())) {
539 toolbar_items_
.push_back(*iter
);
540 if (iter
- original_model
->toolbar_items_
.begin() < original_visible
)
541 ++visible_icon_count_
;
546 void ExtensionToolbarModel::UpdatePrefs() {
547 if (!extension_prefs_
|| profile_
->IsOffTheRecord())
550 // Don't observe change caused by self.
551 pref_change_registrar_
.Remove(pref_names::kToolbar
);
552 extension_prefs_
->SetToolbarOrder(last_known_positions_
);
553 pref_change_registrar_
.Add(pref_names::kToolbar
, pref_change_callback_
);
556 void ExtensionToolbarModel::MaybeUpdateVisibilityPref(
557 const Extension
* extension
, size_t index
) {
558 // We only update the visibility pref for hidden/not hidden based on the
559 // overflow menu with the new toolbar design.
560 if (include_all_extensions_
&& !profile_
->IsOffTheRecord()) {
561 bool visible
= index
< visible_icon_count();
562 if (visible
!= extension_action_api_
->GetBrowserActionVisibility(
564 // Don't observe changes caused by ourselves.
565 bool was_registered
= false;
566 if (extension_action_observer_
.IsObserving(extension_action_api_
)) {
567 was_registered
= true;
568 extension_action_observer_
.RemoveAll();
570 extension_action_api_
->SetBrowserActionVisibility(extension
->id(),
573 extension_action_observer_
.Add(extension_action_api_
);
578 void ExtensionToolbarModel::MaybeUpdateVisibilityPrefs() {
579 for (size_t i
= 0u; i
< toolbar_items_
.size(); ++i
)
580 MaybeUpdateVisibilityPref(toolbar_items_
[i
].get(), i
);
583 void ExtensionToolbarModel::OnExtensionToolbarPrefChange() {
584 // If extensions are not ready, defer to later Populate() call.
585 if (!extensions_initialized_
)
588 // Recalculate |last_known_positions_| to be |pref_positions| followed by
589 // ones that are only in |last_known_positions_|.
590 ExtensionIdList pref_positions
= extension_prefs_
->GetToolbarOrder();
591 size_t pref_position_size
= pref_positions
.size();
592 for (size_t i
= 0; i
< last_known_positions_
.size(); ++i
) {
593 if (std::find(pref_positions
.begin(), pref_positions
.end(),
594 last_known_positions_
[i
]) == pref_positions
.end()) {
595 pref_positions
.push_back(last_known_positions_
[i
]);
598 last_known_positions_
.swap(pref_positions
);
600 int desired_index
= 0;
601 // Loop over the updated list of last known positions, moving any extensions
602 // that are in the wrong place.
603 for (const std::string
& id
: last_known_positions_
) {
604 int current_index
= GetIndexForId(id
);
605 if (current_index
== -1)
607 if (current_index
!= desired_index
) {
608 scoped_refptr
<const Extension
> extension
= toolbar_items_
[current_index
];
609 toolbar_items_
.erase(toolbar_items_
.begin() + current_index
);
610 toolbar_items_
.insert(toolbar_items_
.begin() + desired_index
, extension
);
611 // Notify the observers to keep them up-to-date.
613 Observer
, observers_
,
614 OnToolbarExtensionMoved(extension
.get(), desired_index
));
619 if (last_known_positions_
.size() > pref_position_size
) {
620 // Need to update pref because we have extra icons. But can't call
621 // UpdatePrefs() directly within observation closure.
622 base::ThreadTaskRunnerHandle::Get()->PostTask(
623 FROM_HERE
, base::Bind(&ExtensionToolbarModel::UpdatePrefs
,
624 weak_ptr_factory_
.GetWeakPtr()));
628 int ExtensionToolbarModel::GetIndexForId(const std::string
& id
) const {
629 for (size_t i
= 0; i
< toolbar_items().size(); ++i
) {
630 if (toolbar_items()[i
]->id() == id
)
636 bool ExtensionToolbarModel::ShowExtensionActionPopup(
637 const Extension
* extension
,
639 bool grant_active_tab
) {
640 base::ObserverListBase
<Observer
>::Iterator
it(&observers_
);
641 Observer
* obs
= NULL
;
642 // Look for the Observer associated with the browser.
643 // This would be cleaner if we had an abstract class for the Toolbar UI
644 // (like we do for LocationBar), but sadly, we don't.
645 while ((obs
= it
.GetNext()) != NULL
) {
646 if (obs
->GetBrowser() == browser
)
647 return obs
->ShowExtensionActionPopup(extension
, grant_active_tab
);
652 void ExtensionToolbarModel::EnsureVisibility(
653 const ExtensionIdList
& extension_ids
) {
654 if (all_icons_visible())
655 return; // Already showing all.
657 // Otherwise, make sure we have enough room to show all the extensions
659 if (visible_icon_count() < extension_ids
.size())
660 SetVisibleIconCount(extension_ids
.size());
662 if (all_icons_visible())
663 return; // May have been set to max by SetVisibleIconCount.
665 // Guillotine's Delight: Move an orange noble to the front of the line.
666 for (ExtensionIdList::const_iterator it
= extension_ids
.begin();
667 it
!= extension_ids
.end(); ++it
) {
668 for (ExtensionList::const_iterator extension
= toolbar_items_
.begin();
669 extension
!= toolbar_items_
.end(); ++extension
) {
670 if ((*extension
)->id() == (*it
)) {
671 if (extension
- toolbar_items_
.begin() >=
672 static_cast<int>(visible_icon_count()))
673 MoveExtensionIcon((*extension
)->id(), 0);
680 bool ExtensionToolbarModel::HighlightExtensions(
681 const ExtensionIdList
& extension_ids
,
682 HighlightType highlight_type
) {
683 highlighted_items_
.clear();
685 for (ExtensionIdList::const_iterator id
= extension_ids
.begin();
686 id
!= extension_ids
.end();
688 for (ExtensionList::const_iterator extension
= toolbar_items_
.begin();
689 extension
!= toolbar_items_
.end();
691 if (*id
== (*extension
)->id())
692 highlighted_items_
.push_back(*extension
);
696 // If we have any items in |highlighted_items_|, then we entered highlighting
698 if (highlighted_items_
.size()) {
699 // It's important that is_highlighting_ is changed immediately before the
700 // observers are notified since it changes the result of toolbar_items().
701 highlight_type_
= highlight_type
;
702 FOR_EACH_OBSERVER(Observer
, observers_
,
703 OnToolbarHighlightModeChanged(true));
705 // We set the visible icon count after the highlight mode change because
706 // the UI actions are created/destroyed during highlight, and doing that
707 // prior to changing the size allows us to still have smooth animations.
708 if (visible_icon_count() < extension_ids
.size())
709 SetVisibleIconCount(extension_ids
.size());
714 // Otherwise, we didn't enter highlighting mode (and, in fact, exited it if
715 // we were otherwise in it).
716 if (is_highlighting())
721 void ExtensionToolbarModel::StopHighlighting() {
722 if (is_highlighting()) {
723 // It's important that is_highlighting_ is changed immediately before the
724 // observers are notified since it changes the result of toolbar_items().
725 highlight_type_
= HIGHLIGHT_NONE
;
726 FOR_EACH_OBSERVER(Observer
, observers_
,
727 OnToolbarHighlightModeChanged(false));
729 // For the same reason, we don't clear highlighted_items_ until after the
731 highlighted_items_
.clear();
733 // We set the visible icon count after the highlight mode change because
734 // the UI actions are created/destroyed during highlight, and doing that
735 // prior to changing the size allows us to still have smooth animations.
736 int saved_icon_count
= prefs_
->GetInteger(pref_names::kToolbarSize
);
737 if (saved_icon_count
!= visible_icon_count_
)
738 SetVisibleIconCount(saved_icon_count
);
742 bool ExtensionToolbarModel::RedesignIsShowingNewIcons() const {
743 for (const scoped_refptr
<const Extension
>& extension
: toolbar_items_
) {
744 // Without the redesign, we only show extensions with browser actions.
745 // Any extension without a browser action is an indication that we're
746 // showing something new.
747 if (!extension
->manifest()->HasKey(manifest_keys::kBrowserAction
))
753 } // namespace extensions