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 "base/profiler/scoped_tracker.h"
15 #include "chrome/browser/chrome_notification_types.h"
16 #include "chrome/browser/extensions/api/extension_action/extension_action_api.h"
17 #include "chrome/browser/extensions/extension_action_manager.h"
18 #include "chrome/browser/extensions/extension_tab_util.h"
19 #include "chrome/browser/extensions/extension_toolbar_model_factory.h"
20 #include "chrome/browser/extensions/extension_util.h"
21 #include "chrome/browser/extensions/tab_helper.h"
22 #include "chrome/browser/profiles/profile.h"
23 #include "chrome/browser/ui/browser.h"
24 #include "chrome/browser/ui/tabs/tab_strip_model.h"
25 #include "content/public/browser/notification_details.h"
26 #include "content/public/browser/notification_source.h"
27 #include "content/public/browser/web_contents.h"
28 #include "extensions/browser/extension_prefs.h"
29 #include "extensions/browser/extension_registry.h"
30 #include "extensions/browser/extension_system.h"
31 #include "extensions/browser/pref_names.h"
32 #include "extensions/common/extension.h"
33 #include "extensions/common/extension_set.h"
34 #include "extensions/common/feature_switch.h"
35 #include "extensions/common/one_shot_event.h"
37 namespace extensions
{
39 ExtensionToolbarModel::ExtensionToolbarModel(Profile
* profile
,
40 ExtensionPrefs
* extension_prefs
)
42 extension_prefs_(extension_prefs
),
43 prefs_(profile_
->GetPrefs()),
44 extensions_initialized_(false),
45 include_all_extensions_(
46 FeatureSwitch::extension_action_redesign()->IsEnabled()),
47 is_highlighting_(false),
48 extension_action_observer_(this),
49 extension_registry_observer_(this),
50 weak_ptr_factory_(this) {
51 ExtensionSystem::Get(profile_
)->ready().Post(
53 base::Bind(&ExtensionToolbarModel::OnReady
,
54 weak_ptr_factory_
.GetWeakPtr()));
55 visible_icon_count_
= prefs_
->GetInteger(pref_names::kToolbarSize
);
57 // We only care about watching the prefs if not in incognito mode.
58 if (!profile_
->IsOffTheRecord()) {
59 pref_change_registrar_
.Init(prefs_
);
60 pref_change_callback_
=
61 base::Bind(&ExtensionToolbarModel::OnExtensionToolbarPrefChange
,
62 base::Unretained(this));
63 pref_change_registrar_
.Add(pref_names::kToolbar
, pref_change_callback_
);
67 ExtensionToolbarModel::~ExtensionToolbarModel() {
71 ExtensionToolbarModel
* ExtensionToolbarModel::Get(Profile
* profile
) {
72 return ExtensionToolbarModelFactory::GetForProfile(profile
);
75 void ExtensionToolbarModel::AddObserver(Observer
* observer
) {
76 observers_
.AddObserver(observer
);
79 void ExtensionToolbarModel::RemoveObserver(Observer
* observer
) {
80 observers_
.RemoveObserver(observer
);
83 void ExtensionToolbarModel::MoveExtensionIcon(const std::string
& id
,
85 ExtensionList::iterator pos
= toolbar_items_
.begin();
86 while (pos
!= toolbar_items_
.end() && (*pos
)->id() != id
)
88 if (pos
== toolbar_items_
.end()) {
92 scoped_refptr
<const Extension
> extension
= *pos
;
93 toolbar_items_
.erase(pos
);
95 ExtensionIdList::iterator pos_id
= std::find(last_known_positions_
.begin(),
96 last_known_positions_
.end(),
98 if (pos_id
!= last_known_positions_
.end())
99 last_known_positions_
.erase(pos_id
);
101 if (index
< toolbar_items_
.size()) {
102 // If the index is not at the end, find the item currently at |index|, and
103 // insert |extension| before it in both |toolbar_items_| and
104 // |last_known_positions_|.
105 ExtensionList::iterator iter
= toolbar_items_
.begin() + index
;
106 last_known_positions_
.insert(std::find(last_known_positions_
.begin(),
107 last_known_positions_
.end(),
110 toolbar_items_
.insert(iter
, extension
);
112 // Otherwise, put |extension| at the end.
113 DCHECK_EQ(toolbar_items_
.size(), index
);
114 index
= toolbar_items_
.size();
115 toolbar_items_
.push_back(extension
);
116 last_known_positions_
.push_back(id
);
119 FOR_EACH_OBSERVER(Observer
, observers_
,
120 OnToolbarExtensionMoved(extension
.get(), index
));
121 MaybeUpdateVisibilityPref(extension
.get(), index
);
125 void ExtensionToolbarModel::SetVisibleIconCount(size_t count
) {
126 visible_icon_count_
= (count
>= toolbar_items_
.size()) ? -1 : count
;
128 // Only set the prefs if we're not in highlight mode and the profile is not
129 // incognito. Highlight mode is designed to be a transitory state, and should
130 // not persist across browser restarts (though it may be re-entered), and we
131 // don't store anything in incognito.
132 if (!is_highlighting_
&& !profile_
->IsOffTheRecord()) {
133 // Additionally, if we are using the new toolbar, any icons which are in the
134 // overflow menu are considered "hidden". But it so happens that the times
135 // we are likely to call SetVisibleIconCount() are also those when we are
136 // in flux. So wait for things to cool down before setting the prefs.
137 base::MessageLoop::current()->PostTask(
139 base::Bind(&ExtensionToolbarModel::MaybeUpdateVisibilityPrefs
,
140 weak_ptr_factory_
.GetWeakPtr()));
141 prefs_
->SetInteger(pref_names::kToolbarSize
, visible_icon_count_
);
144 FOR_EACH_OBSERVER(Observer
, observers_
, OnToolbarVisibleCountChanged());
147 void ExtensionToolbarModel::OnExtensionActionUpdated(
148 ExtensionAction
* extension_action
,
149 content::WebContents
* web_contents
,
150 content::BrowserContext
* browser_context
) {
151 const Extension
* extension
=
152 ExtensionRegistry::Get(profile_
)->enabled_extensions().GetByID(
153 extension_action
->extension_id());
154 // Notify observers if the extension exists and is in the model.
155 if (std::find(toolbar_items_
.begin(), toolbar_items_
.end(), extension
) !=
156 toolbar_items_
.end()) {
157 FOR_EACH_OBSERVER(Observer
, observers_
,
158 OnToolbarExtensionUpdated(extension
));
162 void ExtensionToolbarModel::OnExtensionLoaded(
163 content::BrowserContext
* browser_context
,
164 const Extension
* extension
) {
165 // We don't want to add the same extension twice. It may have already been
166 // added by EXTENSION_BROWSER_ACTION_VISIBILITY_CHANGED below, if the user
167 // hides the browser action and then disables and enables the extension.
168 for (size_t i
= 0; i
< toolbar_items_
.size(); i
++) {
169 if (toolbar_items_
[i
].get() == extension
)
173 AddExtension(extension
);
176 void ExtensionToolbarModel::OnExtensionUnloaded(
177 content::BrowserContext
* browser_context
,
178 const Extension
* extension
,
179 UnloadedExtensionInfo::Reason reason
) {
180 RemoveExtension(extension
);
183 void ExtensionToolbarModel::OnExtensionUninstalled(
184 content::BrowserContext
* browser_context
,
185 const Extension
* extension
,
186 extensions::UninstallReason reason
) {
187 // Remove the extension id from the ordered list, if it exists (the extension
188 // might not be represented in the list because it might not have an icon).
189 ExtensionIdList::iterator pos
=
190 std::find(last_known_positions_
.begin(),
191 last_known_positions_
.end(), extension
->id());
193 if (pos
!= last_known_positions_
.end()) {
194 last_known_positions_
.erase(pos
);
199 void ExtensionToolbarModel::Observe(
201 const content::NotificationSource
& source
,
202 const content::NotificationDetails
& details
) {
203 DCHECK_EQ(NOTIFICATION_EXTENSION_BROWSER_ACTION_VISIBILITY_CHANGED
, type
);
204 const Extension
* extension
=
205 ExtensionRegistry::Get(profile_
)->GetExtensionById(
206 *content::Details
<const std::string
>(details
).ptr(),
207 ExtensionRegistry::EVERYTHING
);
209 bool visible
= ExtensionActionAPI::GetBrowserActionVisibility(
210 extension_prefs_
, extension
->id());
211 // Hiding works differently with the new and old toolbars.
212 if (include_all_extensions_
) {
213 // It's possible that we haven't added this extension yet, if its
214 // visibility was adjusted in the course of its initialization.
215 if (std::find(toolbar_items_
.begin(), toolbar_items_
.end(), extension
) ==
216 toolbar_items_
.end())
222 // If this action used to be hidden, we can't possibly be showing all.
223 DCHECK_LT(visible_icon_count(), toolbar_items_
.size());
224 // Grow the bar by one and move the extension to the end of the visibles.
225 new_size
= visible_icon_count() + 1;
226 new_index
= new_size
- 1;
228 // If we're hiding one, we must be showing at least one.
229 DCHECK_GE(visible_icon_count(), 0u);
230 // Shrink the bar by one and move the extension to the beginning of the
232 new_size
= visible_icon_count() - 1;
233 new_index
= new_size
;
235 SetVisibleIconCount(new_size
);
236 MoveExtensionIcon(extension
->id(), new_index
);
237 } else { // Don't include all extensions.
239 AddExtension(extension
);
241 RemoveExtension(extension
);
245 void ExtensionToolbarModel::OnReady() {
246 ExtensionRegistry
* registry
= ExtensionRegistry::Get(profile_
);
247 InitializeExtensionList();
248 // Wait until the extension system is ready before observing any further
249 // changes so that the toolbar buttons can be shown in their stable ordering
251 extension_registry_observer_
.Add(registry
);
252 extension_action_observer_
.Add(ExtensionActionAPI::Get(profile_
));
255 extensions::NOTIFICATION_EXTENSION_BROWSER_ACTION_VISIBILITY_CHANGED
,
256 content::Source
<ExtensionPrefs
>(extension_prefs_
));
259 size_t ExtensionToolbarModel::FindNewPositionFromLastKnownGood(
260 const Extension
* extension
) {
261 // See if we have last known good position for this extension.
262 size_t new_index
= 0;
263 // Loop through the ID list of known positions, to count the number of visible
264 // extension icons preceding |extension|.
265 for (ExtensionIdList::const_iterator iter_id
= last_known_positions_
.begin();
266 iter_id
< last_known_positions_
.end(); ++iter_id
) {
267 if ((*iter_id
) == extension
->id())
268 return new_index
; // We've found the right position.
269 // Found an id, need to see if it is visible.
270 for (ExtensionList::const_iterator iter_ext
= toolbar_items_
.begin();
271 iter_ext
< toolbar_items_
.end(); ++iter_ext
) {
272 if ((*iter_ext
)->id() == (*iter_id
)) {
273 // This extension is visible, update the index value.
280 // Position not found.
281 return toolbar_items_
.size();
284 bool ExtensionToolbarModel::ShouldAddExtension(const Extension
* extension
) {
285 // In incognito mode, don't add any extensions that aren't incognito-enabled.
286 if (profile_
->IsOffTheRecord() &&
287 !util::IsIncognitoEnabled(extension
->id(), profile_
))
290 ExtensionActionManager
* action_manager
=
291 ExtensionActionManager::Get(profile_
);
292 if (include_all_extensions_
) {
293 // In this case, we don't care about the browser action visibility, because
294 // we want to show each extension regardless.
295 // TODO(devlin): Extension actions which are not visible should be moved to
296 // the overflow menu by default.
297 return action_manager
->GetExtensionAction(*extension
) != NULL
;
300 return action_manager
->GetBrowserAction(*extension
) &&
301 ExtensionActionAPI::GetBrowserActionVisibility(
302 extension_prefs_
, extension
->id());
305 void ExtensionToolbarModel::AddExtension(const Extension
* extension
) {
306 // We only use AddExtension() once the system is initialized.
307 DCHECK(extensions_initialized_
);
308 if (!ShouldAddExtension(extension
))
311 // See if we have a last known good position for this extension.
312 bool is_new_extension
=
313 std::find(last_known_positions_
.begin(),
314 last_known_positions_
.end(),
315 extension
->id()) == last_known_positions_
.end();
317 // New extensions go at the right (end) of the visible extensions. Other
318 // extensions go at their previous position.
319 size_t new_index
= 0;
320 if (is_new_extension
) {
321 new_index
= Manifest::IsComponentLocation(extension
->location()) ?
322 0 : visible_icon_count();
323 // For the last-known position, we use the index of the extension that is
324 // just before this extension, plus one. (Note that this isn't the same
325 // as new_index + 1, because last_known_positions_ can include disabled
327 int new_last_known_index
=
329 std::find(last_known_positions_
.begin(),
330 last_known_positions_
.end(),
331 toolbar_items_
[new_index
- 1]->id()) -
332 last_known_positions_
.begin() + 1;
333 // In theory, the extension before this one should always
334 // be in last known positions, but if something funny happened with prefs,
335 // make sure we handle it.
336 // TODO(devlin): Track down these cases so we can CHECK this.
337 new_last_known_index
=
338 std::min
<int>(new_last_known_index
, last_known_positions_
.size());
339 last_known_positions_
.insert(
340 last_known_positions_
.begin() + new_last_known_index
, extension
->id());
343 new_index
= FindNewPositionFromLastKnownGood(extension
);
346 toolbar_items_
.insert(toolbar_items_
.begin() + new_index
, extension
);
348 // If we're currently highlighting, then even though we add a browser action
349 // to the full list (|toolbar_items_|, there won't be another *visible*
350 // browser action, which was what the observers care about.
351 if (!is_highlighting_
) {
352 FOR_EACH_OBSERVER(Observer
, observers_
,
353 OnToolbarExtensionAdded(extension
, new_index
));
355 int visible_count_delta
= 0;
356 if (is_new_extension
&& !all_icons_visible()) {
357 // If this is a new extension (and not all extensions are visible), we
358 // expand the toolbar out so that the new one can be seen.
359 visible_count_delta
= 1;
360 } else if (profile_
->IsOffTheRecord()) {
361 // If this is an incognito profile, we also have to check to make sure the
362 // overflow matches the main bar's status.
363 ExtensionToolbarModel
* main_model
=
364 ExtensionToolbarModel::Get(profile_
->GetOriginalProfile());
365 // Find what the index will be in the main bar. Because Observer calls are
366 // nondeterministic, we can't just assume the main bar will have the
367 // extension and look it up.
369 main_model
->FindNewPositionFromLastKnownGood(extension
);
370 bool visible
= main_index
< main_model
->visible_icon_count();
371 // We may need to adjust the visible count if the incognito bar isn't
372 // showing all icons and this one is visible, or if it is showing all
373 // icons and this is hidden.
374 if (visible
&& !all_icons_visible())
375 visible_count_delta
= 1;
376 else if (!visible
&& all_icons_visible())
377 visible_count_delta
= -1;
380 if (visible_count_delta
)
381 SetVisibleIconCount(visible_icon_count() + visible_count_delta
);
384 MaybeUpdateVisibilityPref(extension
, new_index
);
387 void ExtensionToolbarModel::RemoveExtension(const Extension
* extension
) {
388 ExtensionList::iterator pos
=
389 std::find(toolbar_items_
.begin(), toolbar_items_
.end(), extension
);
390 if (pos
== toolbar_items_
.end())
393 // If our visible count is set to the current size, we need to decrement it.
394 if (visible_icon_count_
== static_cast<int>(toolbar_items_
.size()))
395 SetVisibleIconCount(toolbar_items_
.size() - 1);
397 toolbar_items_
.erase(pos
);
399 // If we're in highlight mode, we also have to remove the extension from
400 // the highlighted list.
401 if (is_highlighting_
) {
402 pos
= std::find(highlighted_items_
.begin(),
403 highlighted_items_
.end(),
405 if (pos
!= highlighted_items_
.end()) {
406 highlighted_items_
.erase(pos
);
407 FOR_EACH_OBSERVER(Observer
, observers_
,
408 OnToolbarExtensionRemoved(extension
));
409 // If the highlighted list is now empty, we stop highlighting.
410 if (highlighted_items_
.empty())
414 FOR_EACH_OBSERVER(Observer
, observers_
,
415 OnToolbarExtensionRemoved(extension
));
421 // Combine the currently enabled extensions that have browser actions (which
422 // we get from the ExtensionRegistry) with the ordering we get from the
423 // pref service. For robustness we use a somewhat inefficient process:
424 // 1. Create a vector of extensions sorted by their pref values. This vector may
426 // 2. Create a vector of extensions that did not have a pref value.
427 // 3. Remove holes from the sorted vector and append the unsorted vector.
428 void ExtensionToolbarModel::InitializeExtensionList() {
429 DCHECK(toolbar_items_
.empty()); // We shouldn't have any items yet.
431 // TODO(robliao): Remove ScopedTracker below once crbug.com/463337 is fixed.
432 tracked_objects::ScopedTracker
tracking_profile1(
433 FROM_HERE_WITH_EXPLICIT_FUNCTION(
434 "463337 ExtensionToolbarModel::InitializeExtensionList1"));
435 last_known_positions_
= extension_prefs_
->GetToolbarOrder();
436 if (profile_
->IsOffTheRecord()) {
437 // TODO(robliao): Remove ScopedTracker below once crbug.com/463337 is fixed.
438 tracked_objects::ScopedTracker
tracking_profile2(
439 FROM_HERE_WITH_EXPLICIT_FUNCTION(
440 "463337 ExtensionToolbarModel::InitializeExtensionList2"));
443 // TODO(robliao): Remove ScopedTracker below once crbug.com/463337 is fixed.
444 tracked_objects::ScopedTracker
tracking_profile3(
445 FROM_HERE_WITH_EXPLICIT_FUNCTION(
446 "463337 ExtensionToolbarModel::InitializeExtensionList3"));
447 Populate(&last_known_positions_
);
450 extensions_initialized_
= true;
452 // TODO(robliao): Remove ScopedTracker below once crbug.com/463337 is fixed.
453 tracked_objects::ScopedTracker
tracking_profile4(
454 FROM_HERE_WITH_EXPLICIT_FUNCTION(
455 "463337 ExtensionToolbarModel::InitializeExtensionList4"));
456 MaybeUpdateVisibilityPrefs();
458 // TODO(robliao): Remove ScopedTracker below once crbug.com/463337 is fixed.
459 tracked_objects::ScopedTracker
tracking_profile5(
460 FROM_HERE_WITH_EXPLICIT_FUNCTION(
461 "463337 ExtensionToolbarModel::InitializeExtensionList5"));
462 FOR_EACH_OBSERVER(Observer
, observers_
, OnToolbarModelInitialized());
465 void ExtensionToolbarModel::Populate(ExtensionIdList
* positions
) {
466 DCHECK(!profile_
->IsOffTheRecord());
467 const ExtensionSet
& extensions
=
468 ExtensionRegistry::Get(profile_
)->enabled_extensions();
469 // Items that have explicit positions.
470 ExtensionList
sorted(positions
->size(), NULL
);
471 // The items that don't have explicit positions.
472 ExtensionList unsorted
;
476 for (const scoped_refptr
<const Extension
>& extension
: extensions
) {
477 if (!ShouldAddExtension(extension
.get())) {
478 if (!ExtensionActionAPI::GetBrowserActionVisibility(extension_prefs_
,
484 ExtensionIdList::const_iterator pos
=
485 std::find(positions
->begin(), positions
->end(), extension
->id());
486 if (pos
!= positions
->end()) {
487 sorted
[pos
- positions
->begin()] = extension
;
489 // Unknown extension - push it to the back of unsorted, and add it to the
490 // list of ids at the end.
491 unsorted
.push_back(extension
);
492 positions
->push_back(extension
->id());
497 sorted
.insert(sorted
.end(), unsorted
.begin(), unsorted
.end());
498 toolbar_items_
.reserve(sorted
.size());
500 for (const scoped_refptr
<const Extension
>& extension
: sorted
) {
501 // It's possible for the extension order to contain items that aren't
502 // actually loaded on this machine. For example, when extension sync is on,
503 // we sync the extension order as-is but double-check with the user before
504 // syncing NPAPI-containing extensions, so if one of those is not actually
505 // synced, we'll get a NULL in the list. This sort of case can also happen
506 // if some error prevents an extension from loading.
507 if (extension
.get()) {
508 // We don't notify observers of the added extension yet. Rather, observers
509 // should wait for the "OnToolbarModelInitialized" notification, and then
510 // bulk-update. (This saves a lot of bouncing-back-and-forth here, and
511 // allows observers to ensure that the extension system is always
512 // initialized before using the extensions).
513 toolbar_items_
.push_back(extension
);
517 UMA_HISTOGRAM_COUNTS_100(
518 "ExtensionToolbarModel.BrowserActionsPermanentlyHidden", hidden
);
519 UMA_HISTOGRAM_COUNTS_100("ExtensionToolbarModel.BrowserActionsCount",
520 toolbar_items_
.size());
522 if (!toolbar_items_
.empty()) {
523 // Visible count can be -1, meaning: 'show all'. Since UMA converts negative
524 // values to 0, this would be counted as 'show none' unless we convert it to
526 UMA_HISTOGRAM_COUNTS_100("ExtensionToolbarModel.BrowserActionsVisible",
527 visible_icon_count_
== -1 ?
528 base::HistogramBase::kSampleType_MAX
:
529 visible_icon_count_
);
533 void ExtensionToolbarModel::IncognitoPopulate() {
534 DCHECK(profile_
->IsOffTheRecord());
535 const ExtensionToolbarModel
* original_model
=
536 ExtensionToolbarModel::Get(profile_
->GetOriginalProfile());
538 // Find the absolute value of the original model's count.
539 int original_visible
= original_model
->visible_icon_count();
541 // In incognito mode, we show only those extensions that are
542 // incognito-enabled. Further, any actions that were overflowed in regular
543 // mode are still overflowed. Order is the same as in regular mode.
544 visible_icon_count_
= 0;
545 for (ExtensionList::const_iterator iter
=
546 original_model
->toolbar_items_
.begin();
547 iter
!= original_model
->toolbar_items_
.end(); ++iter
) {
548 if (ShouldAddExtension(iter
->get())) {
549 toolbar_items_
.push_back(*iter
);
550 if (iter
- original_model
->toolbar_items_
.begin() < original_visible
)
551 ++visible_icon_count_
;
556 void ExtensionToolbarModel::UpdatePrefs() {
557 if (!extension_prefs_
|| profile_
->IsOffTheRecord())
560 // Don't observe change caused by self.
561 pref_change_registrar_
.Remove(pref_names::kToolbar
);
562 extension_prefs_
->SetToolbarOrder(last_known_positions_
);
563 pref_change_registrar_
.Add(pref_names::kToolbar
, pref_change_callback_
);
566 void ExtensionToolbarModel::MaybeUpdateVisibilityPref(
567 const Extension
* extension
, size_t index
) {
568 // We only update the visibility pref for hidden/not hidden based on the
569 // overflow menu with the new toolbar design.
570 if (include_all_extensions_
&& !profile_
->IsOffTheRecord()) {
571 bool visible
= index
< visible_icon_count();
572 if (visible
!= ExtensionActionAPI::GetBrowserActionVisibility(
573 extension_prefs_
, extension
->id())) {
574 // Don't observe changes caused by ourselves.
575 bool was_registered
= false;
576 if (registrar_
.IsRegistered(
578 NOTIFICATION_EXTENSION_BROWSER_ACTION_VISIBILITY_CHANGED
,
579 content::Source
<ExtensionPrefs
>(extension_prefs_
))) {
580 was_registered
= true;
583 NOTIFICATION_EXTENSION_BROWSER_ACTION_VISIBILITY_CHANGED
,
584 content::Source
<ExtensionPrefs
>(extension_prefs_
));
586 ExtensionActionAPI::SetBrowserActionVisibility(
587 extension_prefs_
, extension
->id(), visible
);
588 if (was_registered
) {
590 NOTIFICATION_EXTENSION_BROWSER_ACTION_VISIBILITY_CHANGED
,
591 content::Source
<ExtensionPrefs
>(extension_prefs_
));
597 void ExtensionToolbarModel::MaybeUpdateVisibilityPrefs() {
598 for (size_t i
= 0u; i
< toolbar_items_
.size(); ++i
)
599 MaybeUpdateVisibilityPref(toolbar_items_
[i
].get(), i
);
602 void ExtensionToolbarModel::OnExtensionToolbarPrefChange() {
603 // If extensions are not ready, defer to later Populate() call.
604 if (!extensions_initialized_
)
607 // Recalculate |last_known_positions_| to be |pref_positions| followed by
608 // ones that are only in |last_known_positions_|.
609 ExtensionIdList pref_positions
= extension_prefs_
->GetToolbarOrder();
610 size_t pref_position_size
= pref_positions
.size();
611 for (size_t i
= 0; i
< last_known_positions_
.size(); ++i
) {
612 if (std::find(pref_positions
.begin(), pref_positions
.end(),
613 last_known_positions_
[i
]) == pref_positions
.end()) {
614 pref_positions
.push_back(last_known_positions_
[i
]);
617 last_known_positions_
.swap(pref_positions
);
619 // Clear out the old...
620 while (!toolbar_items_
.empty()) {
621 scoped_refptr
<const Extension
> extension
= toolbar_items_
.back();
622 toolbar_items_
.pop_back();
623 FOR_EACH_OBSERVER(Observer
, observers_
,
624 OnToolbarExtensionRemoved(extension
.get()));
626 DCHECK(toolbar_items_
.empty());
629 Populate(&last_known_positions_
);
632 for (size_t i
= 0; i
< toolbar_items().size(); ++i
) {
633 FOR_EACH_OBSERVER(Observer
, observers_
,
634 OnToolbarExtensionAdded(toolbar_items()[i
].get(), i
));
637 if (last_known_positions_
.size() > pref_position_size
) {
638 // Need to update pref because we have extra icons. But can't call
639 // UpdatePrefs() directly within observation closure.
640 base::MessageLoop::current()->PostTask(
642 base::Bind(&ExtensionToolbarModel::UpdatePrefs
,
643 weak_ptr_factory_
.GetWeakPtr()));
647 int ExtensionToolbarModel::GetIndexForId(const std::string
& id
) const {
648 for (size_t i
= 0; i
< toolbar_items().size(); ++i
) {
649 if (toolbar_items()[i
]->id() == id
)
655 bool ExtensionToolbarModel::ShowExtensionActionPopup(
656 const Extension
* extension
,
658 bool grant_active_tab
) {
659 ObserverListBase
<Observer
>::Iterator
it(&observers_
);
660 Observer
* obs
= NULL
;
661 // Look for the Observer associated with the browser.
662 // This would be cleaner if we had an abstract class for the Toolbar UI
663 // (like we do for LocationBar), but sadly, we don't.
664 while ((obs
= it
.GetNext()) != NULL
) {
665 if (obs
->GetBrowser() == browser
)
666 return obs
->ShowExtensionActionPopup(extension
, grant_active_tab
);
671 void ExtensionToolbarModel::EnsureVisibility(
672 const ExtensionIdList
& extension_ids
) {
673 if (all_icons_visible())
674 return; // Already showing all.
676 // Otherwise, make sure we have enough room to show all the extensions
678 if (visible_icon_count() < extension_ids
.size())
679 SetVisibleIconCount(extension_ids
.size());
681 if (all_icons_visible())
682 return; // May have been set to max by SetVisibleIconCount.
684 // Guillotine's Delight: Move an orange noble to the front of the line.
685 for (ExtensionIdList::const_iterator it
= extension_ids
.begin();
686 it
!= extension_ids
.end(); ++it
) {
687 for (ExtensionList::const_iterator extension
= toolbar_items_
.begin();
688 extension
!= toolbar_items_
.end(); ++extension
) {
689 if ((*extension
)->id() == (*it
)) {
690 if (extension
- toolbar_items_
.begin() >=
691 static_cast<int>(visible_icon_count()))
692 MoveExtensionIcon((*extension
)->id(), 0);
699 bool ExtensionToolbarModel::HighlightExtensions(
700 const ExtensionIdList
& extension_ids
) {
701 highlighted_items_
.clear();
703 for (ExtensionIdList::const_iterator id
= extension_ids
.begin();
704 id
!= extension_ids
.end();
706 for (ExtensionList::const_iterator extension
= toolbar_items_
.begin();
707 extension
!= toolbar_items_
.end();
709 if (*id
== (*extension
)->id())
710 highlighted_items_
.push_back(*extension
);
714 // If we have any items in |highlighted_items_|, then we entered highlighting
716 if (highlighted_items_
.size()) {
717 old_visible_icon_count_
= visible_icon_count_
;
718 is_highlighting_
= true;
719 if (visible_icon_count() < extension_ids
.size())
720 SetVisibleIconCount(extension_ids
.size());
722 FOR_EACH_OBSERVER(Observer
, observers_
,
723 OnToolbarHighlightModeChanged(true));
727 // Otherwise, we didn't enter highlighting mode (and, in fact, exited it if
728 // we were otherwise in it).
729 if (is_highlighting_
)
734 void ExtensionToolbarModel::StopHighlighting() {
735 if (is_highlighting_
) {
736 highlighted_items_
.clear();
737 is_highlighting_
= false;
738 if (old_visible_icon_count_
!= visible_icon_count_
)
739 SetVisibleIconCount(old_visible_icon_count_
);
740 FOR_EACH_OBSERVER(Observer
, observers_
,
741 OnToolbarHighlightModeChanged(false));
745 } // namespace extensions