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/message_loop/message_loop.h"
11 #include "base/metrics/histogram.h"
12 #include "base/metrics/histogram_base.h"
13 #include "base/prefs/pref_service.h"
14 #include "chrome/browser/chrome_notification_types.h"
15 #include "chrome/browser/extensions/api/extension_action/extension_action_api.h"
16 #include "chrome/browser/extensions/extension_action_manager.h"
17 #include "chrome/browser/extensions/extension_tab_util.h"
18 #include "chrome/browser/extensions/extension_toolbar_model_factory.h"
19 #include "chrome/browser/extensions/extension_util.h"
20 #include "chrome/browser/extensions/tab_helper.h"
21 #include "chrome/browser/profiles/profile.h"
22 #include "chrome/browser/ui/browser.h"
23 #include "chrome/browser/ui/tabs/tab_strip_model.h"
24 #include "content/public/browser/notification_details.h"
25 #include "content/public/browser/notification_source.h"
26 #include "content/public/browser/web_contents.h"
27 #include "extensions/browser/extension_prefs.h"
28 #include "extensions/browser/extension_registry.h"
29 #include "extensions/browser/extension_system.h"
30 #include "extensions/browser/pref_names.h"
31 #include "extensions/common/extension.h"
32 #include "extensions/common/extension_set.h"
33 #include "extensions/common/feature_switch.h"
34 #include "extensions/common/one_shot_event.h"
36 namespace extensions
{
38 ExtensionToolbarModel::ExtensionToolbarModel(Profile
* profile
,
39 ExtensionPrefs
* extension_prefs
)
41 extension_prefs_(extension_prefs
),
42 prefs_(profile_
->GetPrefs()),
43 extensions_initialized_(false),
44 include_all_extensions_(
45 FeatureSwitch::extension_action_redesign()->IsEnabled()),
46 is_highlighting_(false),
47 extension_action_observer_(this),
48 extension_registry_observer_(this),
49 weak_ptr_factory_(this) {
50 ExtensionSystem::Get(profile_
)->ready().Post(
52 base::Bind(&ExtensionToolbarModel::OnReady
,
53 weak_ptr_factory_
.GetWeakPtr()));
54 visible_icon_count_
= prefs_
->GetInteger(pref_names::kToolbarSize
);
56 // We only care about watching the prefs if not in incognito mode.
57 if (!profile_
->IsOffTheRecord()) {
58 pref_change_registrar_
.Init(prefs_
);
59 pref_change_callback_
=
60 base::Bind(&ExtensionToolbarModel::OnExtensionToolbarPrefChange
,
61 base::Unretained(this));
62 pref_change_registrar_
.Add(pref_names::kToolbar
, pref_change_callback_
);
66 ExtensionToolbarModel::~ExtensionToolbarModel() {
70 ExtensionToolbarModel
* ExtensionToolbarModel::Get(Profile
* profile
) {
71 return ExtensionToolbarModelFactory::GetForProfile(profile
);
74 void ExtensionToolbarModel::AddObserver(Observer
* observer
) {
75 observers_
.AddObserver(observer
);
78 void ExtensionToolbarModel::RemoveObserver(Observer
* observer
) {
79 observers_
.RemoveObserver(observer
);
82 void ExtensionToolbarModel::MoveExtensionIcon(const std::string
& id
,
84 ExtensionList::iterator pos
= toolbar_items_
.begin();
85 while (pos
!= toolbar_items_
.end() && (*pos
)->id() != id
)
87 if (pos
== toolbar_items_
.end()) {
91 scoped_refptr
<const Extension
> extension
= *pos
;
92 toolbar_items_
.erase(pos
);
94 ExtensionIdList::iterator pos_id
= std::find(last_known_positions_
.begin(),
95 last_known_positions_
.end(),
97 if (pos_id
!= last_known_positions_
.end())
98 last_known_positions_
.erase(pos_id
);
100 if (index
< toolbar_items_
.size()) {
101 // If the index is not at the end, find the item currently at |index|, and
102 // insert |extension| before it in both |toolbar_items_| and
103 // |last_known_positions_|.
104 ExtensionList::iterator iter
= toolbar_items_
.begin() + index
;
105 last_known_positions_
.insert(std::find(last_known_positions_
.begin(),
106 last_known_positions_
.end(),
109 toolbar_items_
.insert(iter
, extension
);
111 // Otherwise, put |extension| at the end.
112 DCHECK_EQ(toolbar_items_
.size(), index
);
113 index
= toolbar_items_
.size();
114 toolbar_items_
.push_back(extension
);
115 last_known_positions_
.push_back(id
);
119 Observer
, observers_
, ToolbarExtensionMoved(extension
.get(), index
));
120 MaybeUpdateVisibilityPref(extension
.get(), index
);
124 void ExtensionToolbarModel::SetVisibleIconCount(size_t count
) {
125 visible_icon_count_
= (count
== toolbar_items_
.size()) ? -1 : count
;
127 // Only set the prefs if we're not in highlight mode and the profile is not
128 // incognito. Highlight mode is designed to be a transitory state, and should
129 // not persist across browser restarts (though it may be re-entered), and we
130 // don't store anything in incognito.
131 if (!is_highlighting_
&& !profile_
->IsOffTheRecord()) {
132 // Additionally, if we are using the new toolbar, any icons which are in the
133 // overflow menu are considered "hidden". But it so happens that the times
134 // we are likely to call SetVisibleIconCount() are also those when we are
135 // in flux. So wait for things to cool down before setting the prefs.
136 base::MessageLoop::current()->PostTask(
138 base::Bind(&ExtensionToolbarModel::MaybeUpdateVisibilityPrefs
,
139 weak_ptr_factory_
.GetWeakPtr()));
140 prefs_
->SetInteger(pref_names::kToolbarSize
, visible_icon_count_
);
143 FOR_EACH_OBSERVER(Observer
, observers_
, ToolbarVisibleCountChanged());
146 void ExtensionToolbarModel::OnExtensionActionUpdated(
147 ExtensionAction
* extension_action
,
148 content::WebContents
* web_contents
,
149 content::BrowserContext
* browser_context
) {
150 const Extension
* extension
=
151 ExtensionRegistry::Get(profile_
)->enabled_extensions().GetByID(
152 extension_action
->extension_id());
153 // Notify observers if the extension exists and is in the model.
154 ExtensionList::const_iterator iter
=
155 std::find(toolbar_items_
.begin(), toolbar_items_
.end(), extension
);
156 if (iter
!= toolbar_items_
.end()) {
158 Observer
, observers_
, ToolbarExtensionUpdated(extension
));
159 // If the action was in the overflow menu, we have to alert observers that
160 // the toolbar needs to be reordered (to show the action).
161 if (static_cast<size_t>(iter
- toolbar_items_
.begin()) >=
162 visible_icon_count()) {
164 Observer
, observers_
, OnToolbarReorderNecessary(web_contents
));
169 void ExtensionToolbarModel::OnExtensionLoaded(
170 content::BrowserContext
* browser_context
,
171 const Extension
* extension
) {
172 // We don't want to add the same extension twice. It may have already been
173 // added by EXTENSION_BROWSER_ACTION_VISIBILITY_CHANGED below, if the user
174 // hides the browser action and then disables and enables the extension.
175 for (size_t i
= 0; i
< toolbar_items_
.size(); i
++) {
176 if (toolbar_items_
[i
].get() == extension
)
180 AddExtension(extension
);
183 void ExtensionToolbarModel::OnExtensionUnloaded(
184 content::BrowserContext
* browser_context
,
185 const Extension
* extension
,
186 UnloadedExtensionInfo::Reason reason
) {
187 RemoveExtension(extension
);
190 void ExtensionToolbarModel::OnExtensionUninstalled(
191 content::BrowserContext
* browser_context
,
192 const Extension
* extension
,
193 extensions::UninstallReason reason
) {
194 // Remove the extension id from the ordered list, if it exists (the extension
195 // might not be represented in the list because it might not have an icon).
196 ExtensionIdList::iterator pos
=
197 std::find(last_known_positions_
.begin(),
198 last_known_positions_
.end(), extension
->id());
200 if (pos
!= last_known_positions_
.end()) {
201 last_known_positions_
.erase(pos
);
206 void ExtensionToolbarModel::Observe(
208 const content::NotificationSource
& source
,
209 const content::NotificationDetails
& details
) {
210 DCHECK_EQ(NOTIFICATION_EXTENSION_BROWSER_ACTION_VISIBILITY_CHANGED
, type
);
211 const Extension
* extension
=
212 ExtensionRegistry::Get(profile_
)->GetExtensionById(
213 *content::Details
<const std::string
>(details
).ptr(),
214 ExtensionRegistry::EVERYTHING
);
216 bool visible
= ExtensionActionAPI::GetBrowserActionVisibility(
217 extension_prefs_
, extension
->id());
218 // Hiding works differently with the new and old toolbars.
219 if (include_all_extensions_
) {
220 // It's possible that we haven't added this extension yet, if its
221 // visibility was adjusted in the course of its initialization.
222 if (std::find(toolbar_items_
.begin(), toolbar_items_
.end(), extension
) ==
223 toolbar_items_
.end())
229 // If this action used to be hidden, we can't possibly be showing all.
230 DCHECK_NE(-1, visible_icon_count_
);
231 // Grow the bar by one and move the extension to the end of the visibles.
232 new_size
= visible_icon_count_
+ 1;
233 new_index
= new_size
- 1;
235 // If we're hiding one, we must be showing at least one.
236 DCHECK_NE(visible_icon_count_
, 0);
237 // Shrink the bar by one and move the extension to the beginning of the
239 new_size
= visible_icon_count_
== -1 ?
240 toolbar_items_
.size() - 1 : visible_icon_count_
- 1;
241 new_index
= new_size
;
243 SetVisibleIconCount(new_size
);
244 MoveExtensionIcon(extension
->id(), new_index
);
245 } else { // Don't include all extensions.
247 AddExtension(extension
);
249 RemoveExtension(extension
);
253 void ExtensionToolbarModel::OnReady() {
254 ExtensionRegistry
* registry
= ExtensionRegistry::Get(profile_
);
255 InitializeExtensionList();
256 // Wait until the extension system is ready before observing any further
257 // changes so that the toolbar buttons can be shown in their stable ordering
259 extension_registry_observer_
.Add(registry
);
260 extension_action_observer_
.Add(ExtensionActionAPI::Get(profile_
));
263 extensions::NOTIFICATION_EXTENSION_BROWSER_ACTION_VISIBILITY_CHANGED
,
264 content::Source
<ExtensionPrefs
>(extension_prefs_
));
267 size_t ExtensionToolbarModel::FindNewPositionFromLastKnownGood(
268 const Extension
* extension
) {
269 // See if we have last known good position for this extension.
270 size_t new_index
= 0;
271 // Loop through the ID list of known positions, to count the number of visible
272 // extension icons preceding |extension|.
273 for (ExtensionIdList::const_iterator iter_id
= last_known_positions_
.begin();
274 iter_id
< last_known_positions_
.end(); ++iter_id
) {
275 if ((*iter_id
) == extension
->id())
276 return new_index
; // We've found the right position.
277 // Found an id, need to see if it is visible.
278 for (ExtensionList::const_iterator iter_ext
= toolbar_items_
.begin();
279 iter_ext
< toolbar_items_
.end(); ++iter_ext
) {
280 if ((*iter_ext
)->id() == (*iter_id
)) {
281 // This extension is visible, update the index value.
288 // Position not found.
289 return toolbar_items_
.size();
292 bool ExtensionToolbarModel::ShouldAddExtension(const Extension
* extension
) {
293 // In incognito mode, don't add any extensions that aren't incognito-enabled.
294 if (profile_
->IsOffTheRecord() &&
295 !util::IsIncognitoEnabled(extension
->id(), profile_
))
298 ExtensionActionManager
* action_manager
=
299 ExtensionActionManager::Get(profile_
);
300 if (include_all_extensions_
) {
301 // In this case, we don't care about the browser action visibility, because
302 // we want to show each extension regardless.
303 // TODO(devlin): Extension actions which are not visible should be moved to
304 // the overflow menu by default.
305 return action_manager
->GetExtensionAction(*extension
) != NULL
;
308 return action_manager
->GetBrowserAction(*extension
) &&
309 ExtensionActionAPI::GetBrowserActionVisibility(
310 extension_prefs_
, extension
->id());
313 void ExtensionToolbarModel::AddExtension(const Extension
* extension
) {
314 if (!ShouldAddExtension(extension
))
317 // See if we have a last known good position for this extension.
318 bool is_new_extension
=
319 std::find(last_known_positions_
.begin(),
320 last_known_positions_
.end(),
321 extension
->id()) == last_known_positions_
.end();
322 size_t new_index
= is_new_extension
? toolbar_items_
.size() :
323 FindNewPositionFromLastKnownGood(extension
);
324 toolbar_items_
.insert(toolbar_items_
.begin() + new_index
,
325 make_scoped_refptr(extension
));
326 if (is_new_extension
) {
327 last_known_positions_
.push_back(extension
->id());
331 MaybeUpdateVisibilityPref(extension
, new_index
);
333 // If we're currently highlighting, then even though we add a browser action
334 // to the full list (|toolbar_items_|, there won't be another *visible*
335 // browser action, which was what the observers care about.
336 if (!is_highlighting_
) {
338 Observer
, observers_
, ToolbarExtensionAdded(extension
, new_index
));
340 // If this is an incognito profile, we also have to check to make sure the
341 // overflow matches the main bar's status.
342 if (profile_
->IsOffTheRecord()) {
343 ExtensionToolbarModel
* main_model
=
344 ExtensionToolbarModel::Get(profile_
->GetOriginalProfile());
345 // Find what the index will be in the main bar. Because Observer calls are
346 // nondeterministic, we can't just assume the main bar will have the
347 // extension and look it up.
348 int main_index
= is_new_extension
?
349 main_model
->toolbar_items_
.size() :
350 main_model
->FindNewPositionFromLastKnownGood(extension
);
351 bool visible
= main_index
< main_model
->visible_icon_count_
||
352 main_model
->visible_icon_count_
== -1;
353 // We may need to adjust the visible count if the incognito bar isn't
354 // showing all icons and this one is visible, or if it is showing all
355 // icons and this is hidden.
356 if (visible
&& visible_icon_count_
!= -1)
357 SetVisibleIconCount(visible_icon_count_
+ 1);
358 else if (!visible
&& visible_icon_count_
== -1)
359 SetVisibleIconCount(toolbar_items_
.size() - 1);
364 void ExtensionToolbarModel::RemoveExtension(const Extension
* extension
) {
365 ExtensionList::iterator pos
=
366 std::find(toolbar_items_
.begin(), toolbar_items_
.end(), extension
);
367 if (pos
== toolbar_items_
.end())
370 // If our visible count is set to the current size, we need to decrement it.
371 if (visible_icon_count_
== static_cast<int>(toolbar_items_
.size()))
372 SetVisibleIconCount(toolbar_items_
.size() - 1);
374 toolbar_items_
.erase(pos
);
376 // If we're in highlight mode, we also have to remove the extension from
377 // the highlighted list.
378 if (is_highlighting_
) {
379 pos
= std::find(highlighted_items_
.begin(),
380 highlighted_items_
.end(),
382 if (pos
!= highlighted_items_
.end()) {
383 highlighted_items_
.erase(pos
);
385 Observer
, observers_
, ToolbarExtensionRemoved(extension
));
386 // If the highlighted list is now empty, we stop highlighting.
387 if (highlighted_items_
.empty())
391 FOR_EACH_OBSERVER(Observer
, observers_
, ToolbarExtensionRemoved(extension
));
397 void ExtensionToolbarModel::ClearItems() {
398 size_t items_count
= toolbar_items_
.size();
399 for (size_t i
= 0; i
< items_count
; ++i
) {
400 const Extension
* extension
= toolbar_items_
.back().get();
401 toolbar_items_
.pop_back();
402 FOR_EACH_OBSERVER(Observer
, observers_
, ToolbarExtensionRemoved(extension
));
404 DCHECK(toolbar_items_
.empty());
407 // Combine the currently enabled extensions that have browser actions (which
408 // we get from the ExtensionRegistry) with the ordering we get from the
409 // pref service. For robustness we use a somewhat inefficient process:
410 // 1. Create a vector of extensions sorted by their pref values. This vector may
412 // 2. Create a vector of extensions that did not have a pref value.
413 // 3. Remove holes from the sorted vector and append the unsorted vector.
414 void ExtensionToolbarModel::InitializeExtensionList() {
415 last_known_positions_
= extension_prefs_
->GetToolbarOrder();
416 if (profile_
->IsOffTheRecord())
419 Populate(last_known_positions_
);
421 extensions_initialized_
= true;
422 MaybeUpdateVisibilityPrefs();
423 FOR_EACH_OBSERVER(Observer
, observers_
, OnToolbarModelInitialized());
426 void ExtensionToolbarModel::Populate(const ExtensionIdList
& positions
) {
427 DCHECK(!profile_
->IsOffTheRecord());
428 const ExtensionSet
& extensions
=
429 ExtensionRegistry::Get(profile_
)->enabled_extensions();
430 // Items that have explicit positions.
431 ExtensionList
sorted(positions
.size(), NULL
);
432 // The items that don't have explicit positions.
433 ExtensionList unsorted
;
437 for (const scoped_refptr
<const Extension
>& extension
: extensions
) {
438 if (!ShouldAddExtension(extension
.get())) {
439 if (!ExtensionActionAPI::GetBrowserActionVisibility(extension_prefs_
,
445 ExtensionIdList::const_iterator pos
=
446 std::find(positions
.begin(), positions
.end(), extension
->id());
447 if (pos
!= positions
.end())
448 sorted
[pos
- positions
.begin()] = extension
;
450 unsorted
.push_back(extension
);
453 // Clear the current items, if any.
457 toolbar_items_
.reserve(sorted
.size() + unsorted
.size());
459 for (const scoped_refptr
<const Extension
>& extension
: sorted
) {
460 // It's possible for the extension order to contain items that aren't
461 // actually loaded on this machine. For example, when extension sync is on,
462 // we sync the extension order as-is but double-check with the user before
463 // syncing NPAPI-containing extensions, so if one of those is not actually
464 // synced, we'll get a NULL in the list. This sort of case can also happen
465 // if some error prevents an extension from loading.
466 if (extension
.get() != NULL
) {
467 toolbar_items_
.push_back(extension
);
471 ToolbarExtensionAdded(extension
.get(), toolbar_items_
.size() - 1));
474 for (const scoped_refptr
<const Extension
>& extension
: unsorted
) {
475 if (extension
.get() != NULL
) {
476 toolbar_items_
.push_back(extension
);
480 ToolbarExtensionAdded(extension
.get(), toolbar_items_
.size() - 1));
484 UMA_HISTOGRAM_COUNTS_100(
485 "ExtensionToolbarModel.BrowserActionsPermanentlyHidden", hidden
);
486 UMA_HISTOGRAM_COUNTS_100("ExtensionToolbarModel.BrowserActionsCount",
487 toolbar_items_
.size());
489 if (!toolbar_items_
.empty()) {
490 // Visible count can be -1, meaning: 'show all'. Since UMA converts negative
491 // values to 0, this would be counted as 'show none' unless we convert it to
493 UMA_HISTOGRAM_COUNTS_100("ExtensionToolbarModel.BrowserActionsVisible",
494 visible_icon_count_
== -1 ?
495 base::HistogramBase::kSampleType_MAX
:
496 visible_icon_count_
);
500 void ExtensionToolbarModel::IncognitoPopulate() {
501 DCHECK(profile_
->IsOffTheRecord());
502 // Clear the current items, if any.
505 const ExtensionToolbarModel
* original_model
=
506 ExtensionToolbarModel::Get(profile_
->GetOriginalProfile());
508 // Find the absolute value of the original model's count.
509 int original_visible
= original_model
->visible_icon_count();
511 // In incognito mode, we show only those extensions that are
512 // incognito-enabled. Further, any actions that were overflowed in regular
513 // mode are still overflowed. Order is the same as in regular mode.
514 visible_icon_count_
= 0;
515 for (ExtensionList::const_iterator iter
=
516 original_model
->toolbar_items_
.begin();
517 iter
!= original_model
->toolbar_items_
.end(); ++iter
) {
518 if (ShouldAddExtension(iter
->get())) {
519 toolbar_items_
.push_back(*iter
);
520 if (iter
- original_model
->toolbar_items_
.begin() < original_visible
)
521 ++visible_icon_count_
;
525 ToolbarExtensionAdded(iter
->get(), toolbar_items_
.size() - 1));
530 void ExtensionToolbarModel::UpdatePrefs() {
531 if (!extension_prefs_
|| profile_
->IsOffTheRecord())
534 // Don't observe change caused by self.
535 pref_change_registrar_
.Remove(pref_names::kToolbar
);
536 extension_prefs_
->SetToolbarOrder(last_known_positions_
);
537 pref_change_registrar_
.Add(pref_names::kToolbar
, pref_change_callback_
);
540 void ExtensionToolbarModel::MaybeUpdateVisibilityPref(
541 const Extension
* extension
, int index
) {
542 // We only update the visibility pref for hidden/not hidden based on the
543 // overflow menu with the new toolbar design.
544 if (include_all_extensions_
&& !profile_
->IsOffTheRecord()) {
545 bool visible
= index
< visible_icon_count_
|| visible_icon_count_
== -1;
546 if (visible
!= ExtensionActionAPI::GetBrowserActionVisibility(
547 extension_prefs_
, extension
->id())) {
548 // Don't observe changes caused by ourselves.
549 bool was_registered
= false;
550 if (registrar_
.IsRegistered(
552 NOTIFICATION_EXTENSION_BROWSER_ACTION_VISIBILITY_CHANGED
,
553 content::Source
<ExtensionPrefs
>(extension_prefs_
))) {
554 was_registered
= true;
557 NOTIFICATION_EXTENSION_BROWSER_ACTION_VISIBILITY_CHANGED
,
558 content::Source
<ExtensionPrefs
>(extension_prefs_
));
560 ExtensionActionAPI::SetBrowserActionVisibility(
561 extension_prefs_
, extension
->id(), visible
);
562 if (was_registered
) {
564 NOTIFICATION_EXTENSION_BROWSER_ACTION_VISIBILITY_CHANGED
,
565 content::Source
<ExtensionPrefs
>(extension_prefs_
));
571 void ExtensionToolbarModel::MaybeUpdateVisibilityPrefs() {
572 for (size_t i
= 0u; i
< toolbar_items_
.size(); ++i
)
573 MaybeUpdateVisibilityPref(toolbar_items_
[i
].get(), i
);
576 void ExtensionToolbarModel::OnExtensionToolbarPrefChange() {
577 // If extensions are not ready, defer to later Populate() call.
578 if (!extensions_initialized_
)
581 // Recalculate |last_known_positions_| to be |pref_positions| followed by
582 // ones that are only in |last_known_positions_|.
583 ExtensionIdList pref_positions
= extension_prefs_
->GetToolbarOrder();
584 size_t pref_position_size
= pref_positions
.size();
585 for (size_t i
= 0; i
< last_known_positions_
.size(); ++i
) {
586 if (std::find(pref_positions
.begin(), pref_positions
.end(),
587 last_known_positions_
[i
]) == pref_positions
.end()) {
588 pref_positions
.push_back(last_known_positions_
[i
]);
591 last_known_positions_
.swap(pref_positions
);
594 Populate(last_known_positions_
);
596 if (last_known_positions_
.size() > pref_position_size
) {
597 // Need to update pref because we have extra icons. But can't call
598 // UpdatePrefs() directly within observation closure.
599 base::MessageLoop::current()->PostTask(
601 base::Bind(&ExtensionToolbarModel::UpdatePrefs
,
602 weak_ptr_factory_
.GetWeakPtr()));
606 size_t ExtensionToolbarModel::GetVisibleIconCountForTab(
607 content::WebContents
* web_contents
) const {
608 if (all_icons_visible())
609 return visible_icon_count(); // Already displaying all actions.
611 ExtensionActionAPI
* extension_action_api
= ExtensionActionAPI::Get(profile_
);
612 size_t total_icons
= visible_icon_count_
;
613 for (size_t i
= total_icons
; i
< toolbar_items_
.size(); ++i
) {
614 if (extension_action_api
->ExtensionWantsToRun(toolbar_items_
[i
].get(),
621 ExtensionList
ExtensionToolbarModel::GetItemOrderForTab(
622 content::WebContents
* web_contents
) const {
623 // If we're highlighting, the items are always the same.
624 if (is_highlighting_
)
625 return highlighted_items_
;
627 // Start by initializing the array to be the same as toolbar items (this isn't
628 // any more expensive than initializing it to be of the same size with all
629 // nulls, and saves us time at the end).
630 ExtensionList result
= toolbar_items_
;
631 if (toolbar_items_
.empty())
634 ExtensionList overflowed_actions_wanting_to_run
;
635 ExtensionActionAPI
* extension_action_api
= ExtensionActionAPI::Get(profile_
);
636 size_t boundary
= visible_icon_count();
637 // Rotate any actions that want to run to the boundary between visible and
638 // overflowed actions.
639 for (ExtensionList::iterator iter
= result
.begin() + boundary
;
640 iter
!= result
.end(); ++iter
) {
641 if (extension_action_api
->ExtensionWantsToRun(iter
->get(), web_contents
)) {
642 std::rotate(result
.begin() + boundary
, iter
, iter
+ 1);
649 bool ExtensionToolbarModel::ShowExtensionActionPopup(
650 const Extension
* extension
,
652 bool grant_active_tab
) {
653 ObserverListBase
<Observer
>::Iterator
it(observers_
);
654 Observer
* obs
= NULL
;
655 // Look for the Observer associated with the browser.
656 // This would be cleaner if we had an abstract class for the Toolbar UI
657 // (like we do for LocationBar), but sadly, we don't.
658 while ((obs
= it
.GetNext()) != NULL
) {
659 if (obs
->GetBrowser() == browser
)
660 return obs
->ShowExtensionActionPopup(extension
, grant_active_tab
);
665 void ExtensionToolbarModel::EnsureVisibility(
666 const ExtensionIdList
& extension_ids
) {
667 if (visible_icon_count_
== -1)
668 return; // Already showing all.
670 // Otherwise, make sure we have enough room to show all the extensions
672 if (visible_icon_count_
< static_cast<int>(extension_ids
.size()))
673 SetVisibleIconCount(extension_ids
.size());
675 if (visible_icon_count_
== -1)
676 return; // May have been set to max by SetVisibleIconCount.
678 // Guillotine's Delight: Move an orange noble to the front of the line.
679 for (ExtensionIdList::const_iterator it
= extension_ids
.begin();
680 it
!= extension_ids
.end(); ++it
) {
681 for (ExtensionList::const_iterator extension
= toolbar_items_
.begin();
682 extension
!= toolbar_items_
.end(); ++extension
) {
683 if ((*extension
)->id() == (*it
)) {
684 if (extension
- toolbar_items_
.begin() >= visible_icon_count_
)
685 MoveExtensionIcon((*extension
)->id(), 0);
692 bool ExtensionToolbarModel::HighlightExtensions(
693 const ExtensionIdList
& extension_ids
) {
694 highlighted_items_
.clear();
696 for (ExtensionIdList::const_iterator id
= extension_ids
.begin();
697 id
!= extension_ids
.end();
699 for (ExtensionList::const_iterator extension
= toolbar_items_
.begin();
700 extension
!= toolbar_items_
.end();
702 if (*id
== (*extension
)->id())
703 highlighted_items_
.push_back(*extension
);
707 // If we have any items in |highlighted_items_|, then we entered highlighting
709 if (highlighted_items_
.size()) {
710 old_visible_icon_count_
= visible_icon_count_
;
711 is_highlighting_
= true;
712 if (visible_icon_count_
!= -1 &&
713 visible_icon_count_
< static_cast<int>(extension_ids
.size())) {
714 SetVisibleIconCount(extension_ids
.size());
717 FOR_EACH_OBSERVER(Observer
, observers_
, ToolbarHighlightModeChanged(true));
721 // Otherwise, we didn't enter highlighting mode (and, in fact, exited it if
722 // we were otherwise in it).
723 if (is_highlighting_
)
728 void ExtensionToolbarModel::StopHighlighting() {
729 if (is_highlighting_
) {
730 highlighted_items_
.clear();
731 is_highlighting_
= false;
732 if (old_visible_icon_count_
!= visible_icon_count_
)
733 SetVisibleIconCount(old_visible_icon_count_
);
734 FOR_EACH_OBSERVER(Observer
, observers_
, ToolbarHighlightModeChanged(false));
738 } // namespace extensions