Prevent chrome://net-internals/#export from flickering
[chromium-blink-merge.git] / chrome / browser / extensions / extension_toolbar_model.cc
blob20191775e0f685b8b428e6080b36d929920b3074
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/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)
41 : profile_(profile),
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(
52 FROM_HERE,
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() {
70 // static
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,
84 size_t index) {
85 ExtensionList::iterator pos = toolbar_items_.begin();
86 while (pos != toolbar_items_.end() && (*pos)->id() != id)
87 ++pos;
88 if (pos == toolbar_items_.end()) {
89 NOTREACHED();
90 return;
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(),
97 id);
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(),
108 (*iter)->id()),
109 id);
110 toolbar_items_.insert(iter, extension);
111 } else {
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);
122 UpdatePrefs();
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(
138 FROM_HERE,
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)
170 return;
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);
195 UpdatePrefs();
199 void ExtensionToolbarModel::Observe(
200 int type,
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())
217 return;
219 int new_size = 0;
220 int new_index = 0;
221 if (visible) {
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;
227 } else {
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
231 // overflow menu.
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.
238 if (visible)
239 AddExtension(extension);
240 else
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
250 // taken from prefs.
251 extension_registry_observer_.Add(registry);
252 extension_action_observer_.Add(ExtensionActionAPI::Get(profile_));
253 registrar_.Add(
254 this,
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.
274 ++new_index;
275 break;
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_))
288 return false;
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))
309 return;
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
326 // extensions.)
327 int new_last_known_index =
328 new_index == 0 ? 0 :
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());
341 UpdatePrefs();
342 } else {
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.
368 size_t main_index =
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())
391 return;
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(),
404 extension);
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())
411 StopHighlighting();
413 } else {
414 FOR_EACH_OBSERVER(Observer, observers_,
415 OnToolbarExtensionRemoved(extension));
418 UpdatePrefs();
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
425 // have holes.
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"));
441 IncognitoPopulate();
442 } else {
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;
474 // Create the lists.
475 int hidden = 0;
476 for (const scoped_refptr<const Extension>& extension : extensions) {
477 if (!ShouldAddExtension(extension.get())) {
478 if (!ExtensionActionAPI::GetBrowserActionVisibility(extension_prefs_,
479 extension->id()))
480 ++hidden;
481 continue;
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;
488 } else {
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());
496 // Merge the lists.
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
525 // max.
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())
558 return;
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(
577 this,
578 NOTIFICATION_EXTENSION_BROWSER_ACTION_VISIBILITY_CHANGED,
579 content::Source<ExtensionPrefs>(extension_prefs_))) {
580 was_registered = true;
581 registrar_.Remove(
582 this,
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) {
589 registrar_.Add(this,
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_)
605 return;
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());
628 // ...Add the new...
629 Populate(&last_known_positions_);
631 // ...And notify.
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(
641 FROM_HERE,
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)
650 return i;
652 return -1;
655 bool ExtensionToolbarModel::ShowExtensionActionPopup(
656 const Extension* extension,
657 Browser* browser,
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);
668 return false;
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
677 // requested.
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);
693 break;
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();
705 ++id) {
706 for (ExtensionList::const_iterator extension = toolbar_items_.begin();
707 extension != toolbar_items_.end();
708 ++extension) {
709 if (*id == (*extension)->id())
710 highlighted_items_.push_back(*extension);
714 // If we have any items in |highlighted_items_|, then we entered highlighting
715 // mode.
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));
724 return 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_)
730 StopHighlighting();
731 return false;
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