ozone: evdev: Sync caps lock LED state to evdev
[chromium-blink-merge.git] / chrome / browser / extensions / extension_toolbar_model.cc
blob21451194f6f7fe83c88cd2b38252fca953928e0d
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 "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)
40 : profile_(profile),
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(
51 FROM_HERE,
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() {
69 // static
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,
83 size_t index) {
84 ExtensionList::iterator pos = toolbar_items_.begin();
85 while (pos != toolbar_items_.end() && (*pos)->id() != id)
86 ++pos;
87 if (pos == toolbar_items_.end()) {
88 NOTREACHED();
89 return;
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(),
96 id);
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(),
107 (*iter)->id()),
108 id);
109 toolbar_items_.insert(iter, extension);
110 } else {
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);
118 FOR_EACH_OBSERVER(
119 Observer, observers_, ToolbarExtensionMoved(extension.get(), index));
120 MaybeUpdateVisibilityPref(extension.get(), index);
121 UpdatePrefs();
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(
137 FROM_HERE,
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 if (std::find(toolbar_items_.begin(), toolbar_items_.end(), extension) !=
155 toolbar_items_.end()) {
156 FOR_EACH_OBSERVER(
157 Observer, observers_, ToolbarExtensionUpdated(extension));
161 void ExtensionToolbarModel::OnExtensionLoaded(
162 content::BrowserContext* browser_context,
163 const Extension* extension) {
164 // We don't want to add the same extension twice. It may have already been
165 // added by EXTENSION_BROWSER_ACTION_VISIBILITY_CHANGED below, if the user
166 // hides the browser action and then disables and enables the extension.
167 for (size_t i = 0; i < toolbar_items_.size(); i++) {
168 if (toolbar_items_[i].get() == extension)
169 return;
172 AddExtension(extension);
175 void ExtensionToolbarModel::OnExtensionUnloaded(
176 content::BrowserContext* browser_context,
177 const Extension* extension,
178 UnloadedExtensionInfo::Reason reason) {
179 RemoveExtension(extension);
182 void ExtensionToolbarModel::OnExtensionUninstalled(
183 content::BrowserContext* browser_context,
184 const Extension* extension,
185 extensions::UninstallReason reason) {
186 // Remove the extension id from the ordered list, if it exists (the extension
187 // might not be represented in the list because it might not have an icon).
188 ExtensionIdList::iterator pos =
189 std::find(last_known_positions_.begin(),
190 last_known_positions_.end(), extension->id());
192 if (pos != last_known_positions_.end()) {
193 last_known_positions_.erase(pos);
194 UpdatePrefs();
198 void ExtensionToolbarModel::Observe(
199 int type,
200 const content::NotificationSource& source,
201 const content::NotificationDetails& details) {
202 DCHECK_EQ(NOTIFICATION_EXTENSION_BROWSER_ACTION_VISIBILITY_CHANGED, type);
203 const Extension* extension =
204 ExtensionRegistry::Get(profile_)->GetExtensionById(
205 *content::Details<const std::string>(details).ptr(),
206 ExtensionRegistry::EVERYTHING);
208 bool visible = ExtensionActionAPI::GetBrowserActionVisibility(
209 extension_prefs_, extension->id());
210 // Hiding works differently with the new and old toolbars.
211 if (include_all_extensions_) {
212 // It's possible that we haven't added this extension yet, if its
213 // visibility was adjusted in the course of its initialization.
214 if (std::find(toolbar_items_.begin(), toolbar_items_.end(), extension) ==
215 toolbar_items_.end())
216 return;
218 int new_size = 0;
219 int new_index = 0;
220 if (visible) {
221 // If this action used to be hidden, we can't possibly be showing all.
222 DCHECK_LT(visible_icon_count(), toolbar_items_.size());
223 // Grow the bar by one and move the extension to the end of the visibles.
224 new_size = visible_icon_count() + 1;
225 new_index = new_size - 1;
226 } else {
227 // If we're hiding one, we must be showing at least one.
228 DCHECK_GE(visible_icon_count(), 0u);
229 // Shrink the bar by one and move the extension to the beginning of the
230 // overflow menu.
231 new_size = visible_icon_count() - 1;
232 new_index = new_size;
234 SetVisibleIconCount(new_size);
235 MoveExtensionIcon(extension->id(), new_index);
236 } else { // Don't include all extensions.
237 if (visible)
238 AddExtension(extension);
239 else
240 RemoveExtension(extension);
244 void ExtensionToolbarModel::OnReady() {
245 ExtensionRegistry* registry = ExtensionRegistry::Get(profile_);
246 InitializeExtensionList();
247 // Wait until the extension system is ready before observing any further
248 // changes so that the toolbar buttons can be shown in their stable ordering
249 // taken from prefs.
250 extension_registry_observer_.Add(registry);
251 extension_action_observer_.Add(ExtensionActionAPI::Get(profile_));
252 registrar_.Add(
253 this,
254 extensions::NOTIFICATION_EXTENSION_BROWSER_ACTION_VISIBILITY_CHANGED,
255 content::Source<ExtensionPrefs>(extension_prefs_));
258 size_t ExtensionToolbarModel::FindNewPositionFromLastKnownGood(
259 const Extension* extension) {
260 // See if we have last known good position for this extension.
261 size_t new_index = 0;
262 // Loop through the ID list of known positions, to count the number of visible
263 // extension icons preceding |extension|.
264 for (ExtensionIdList::const_iterator iter_id = last_known_positions_.begin();
265 iter_id < last_known_positions_.end(); ++iter_id) {
266 if ((*iter_id) == extension->id())
267 return new_index; // We've found the right position.
268 // Found an id, need to see if it is visible.
269 for (ExtensionList::const_iterator iter_ext = toolbar_items_.begin();
270 iter_ext < toolbar_items_.end(); ++iter_ext) {
271 if ((*iter_ext)->id() == (*iter_id)) {
272 // This extension is visible, update the index value.
273 ++new_index;
274 break;
279 // Position not found.
280 return toolbar_items_.size();
283 bool ExtensionToolbarModel::ShouldAddExtension(const Extension* extension) {
284 // In incognito mode, don't add any extensions that aren't incognito-enabled.
285 if (profile_->IsOffTheRecord() &&
286 !util::IsIncognitoEnabled(extension->id(), profile_))
287 return false;
289 ExtensionActionManager* action_manager =
290 ExtensionActionManager::Get(profile_);
291 if (include_all_extensions_) {
292 // In this case, we don't care about the browser action visibility, because
293 // we want to show each extension regardless.
294 // TODO(devlin): Extension actions which are not visible should be moved to
295 // the overflow menu by default.
296 return action_manager->GetExtensionAction(*extension) != NULL;
299 return action_manager->GetBrowserAction(*extension) &&
300 ExtensionActionAPI::GetBrowserActionVisibility(
301 extension_prefs_, extension->id());
304 void ExtensionToolbarModel::AddExtension(const Extension* extension) {
305 // We only use AddExtension() once the system is initialized.
306 DCHECK(extensions_initialized_);
307 if (!ShouldAddExtension(extension))
308 return;
310 // See if we have a last known good position for this extension.
311 bool is_new_extension =
312 std::find(last_known_positions_.begin(),
313 last_known_positions_.end(),
314 extension->id()) == last_known_positions_.end();
316 // New extensions go at the right (end) of the visible extensions. Other
317 // extensions go at their previous position.
318 size_t new_index = 0;
319 if (is_new_extension) {
320 new_index = visible_icon_count();
321 // For the last-known position, we use the index of the extension that is
322 // just before this extension, plus one. (Note that this isn't the same
323 // as new_index + 1, because last_known_positions_ can include disabled
324 // extensions.)
325 int new_last_known_index =
326 new_index == 0 ? 0 :
327 std::find(last_known_positions_.begin(),
328 last_known_positions_.end(),
329 toolbar_items_[new_index - 1]->id()) -
330 last_known_positions_.begin() + 1;
331 // In theory, the extension before this one should always
332 // be in last known positions, but if something funny happened with prefs,
333 // make sure we handle it.
334 // TODO(devlin): Track down these cases so we can CHECK this.
335 new_last_known_index =
336 std::min<int>(new_last_known_index, last_known_positions_.size());
337 last_known_positions_.insert(
338 last_known_positions_.begin() + new_last_known_index, extension->id());
339 UpdatePrefs();
340 } else {
341 new_index = FindNewPositionFromLastKnownGood(extension);
344 toolbar_items_.insert(toolbar_items_.begin() + new_index, extension);
346 // If we're currently highlighting, then even though we add a browser action
347 // to the full list (|toolbar_items_|, there won't be another *visible*
348 // browser action, which was what the observers care about.
349 if (!is_highlighting_) {
350 FOR_EACH_OBSERVER(
351 Observer, observers_, ToolbarExtensionAdded(extension, new_index));
353 int visible_count_delta = 0;
354 if (is_new_extension && !all_icons_visible()) {
355 // If this is a new extension (and not all extensions are visible), we
356 // expand the toolbar out so that the new one can be seen.
357 visible_count_delta = 1;
358 } else if (profile_->IsOffTheRecord()) {
359 // If this is an incognito profile, we also have to check to make sure the
360 // overflow matches the main bar's status.
361 ExtensionToolbarModel* main_model =
362 ExtensionToolbarModel::Get(profile_->GetOriginalProfile());
363 // Find what the index will be in the main bar. Because Observer calls are
364 // nondeterministic, we can't just assume the main bar will have the
365 // extension and look it up.
366 size_t main_index =
367 main_model->FindNewPositionFromLastKnownGood(extension);
368 bool visible = main_index < main_model->visible_icon_count();
369 // We may need to adjust the visible count if the incognito bar isn't
370 // showing all icons and this one is visible, or if it is showing all
371 // icons and this is hidden.
372 if (visible && !all_icons_visible())
373 visible_count_delta = 1;
374 else if (!visible && all_icons_visible())
375 visible_count_delta = -1;
378 if (visible_count_delta)
379 SetVisibleIconCount(visible_icon_count() + visible_count_delta);
382 MaybeUpdateVisibilityPref(extension, new_index);
385 void ExtensionToolbarModel::RemoveExtension(const Extension* extension) {
386 ExtensionList::iterator pos =
387 std::find(toolbar_items_.begin(), toolbar_items_.end(), extension);
388 if (pos == toolbar_items_.end())
389 return;
391 // If our visible count is set to the current size, we need to decrement it.
392 if (visible_icon_count_ == static_cast<int>(toolbar_items_.size()))
393 SetVisibleIconCount(toolbar_items_.size() - 1);
395 toolbar_items_.erase(pos);
397 // If we're in highlight mode, we also have to remove the extension from
398 // the highlighted list.
399 if (is_highlighting_) {
400 pos = std::find(highlighted_items_.begin(),
401 highlighted_items_.end(),
402 extension);
403 if (pos != highlighted_items_.end()) {
404 highlighted_items_.erase(pos);
405 FOR_EACH_OBSERVER(
406 Observer, observers_, ToolbarExtensionRemoved(extension));
407 // If the highlighted list is now empty, we stop highlighting.
408 if (highlighted_items_.empty())
409 StopHighlighting();
411 } else {
412 FOR_EACH_OBSERVER(Observer, observers_, ToolbarExtensionRemoved(extension));
415 UpdatePrefs();
418 // Combine the currently enabled extensions that have browser actions (which
419 // we get from the ExtensionRegistry) with the ordering we get from the
420 // pref service. For robustness we use a somewhat inefficient process:
421 // 1. Create a vector of extensions sorted by their pref values. This vector may
422 // have holes.
423 // 2. Create a vector of extensions that did not have a pref value.
424 // 3. Remove holes from the sorted vector and append the unsorted vector.
425 void ExtensionToolbarModel::InitializeExtensionList() {
426 DCHECK(toolbar_items_.empty()); // We shouldn't have any items yet.
428 last_known_positions_ = extension_prefs_->GetToolbarOrder();
429 if (profile_->IsOffTheRecord())
430 IncognitoPopulate();
431 else
432 Populate(&last_known_positions_);
434 extensions_initialized_ = true;
435 MaybeUpdateVisibilityPrefs();
436 FOR_EACH_OBSERVER(Observer, observers_, OnToolbarModelInitialized());
439 void ExtensionToolbarModel::Populate(ExtensionIdList* positions) {
440 DCHECK(!profile_->IsOffTheRecord());
441 const ExtensionSet& extensions =
442 ExtensionRegistry::Get(profile_)->enabled_extensions();
443 // Items that have explicit positions.
444 ExtensionList sorted(positions->size(), NULL);
445 // The items that don't have explicit positions.
446 ExtensionList unsorted;
448 // Create the lists.
449 int hidden = 0;
450 for (const scoped_refptr<const Extension>& extension : extensions) {
451 if (!ShouldAddExtension(extension.get())) {
452 if (!ExtensionActionAPI::GetBrowserActionVisibility(extension_prefs_,
453 extension->id()))
454 ++hidden;
455 continue;
458 ExtensionIdList::const_iterator pos =
459 std::find(positions->begin(), positions->end(), extension->id());
460 if (pos != positions->end()) {
461 sorted[pos - positions->begin()] = extension;
462 } else {
463 // Unknown extension - push it to the back of unsorted, and add it to the
464 // list of ids at the end.
465 unsorted.push_back(extension);
466 positions->push_back(extension->id());
470 // Merge the lists.
471 sorted.insert(sorted.end(), unsorted.begin(), unsorted.end());
472 toolbar_items_.reserve(sorted.size());
474 for (const scoped_refptr<const Extension>& extension : sorted) {
475 // It's possible for the extension order to contain items that aren't
476 // actually loaded on this machine. For example, when extension sync is on,
477 // we sync the extension order as-is but double-check with the user before
478 // syncing NPAPI-containing extensions, so if one of those is not actually
479 // synced, we'll get a NULL in the list. This sort of case can also happen
480 // if some error prevents an extension from loading.
481 if (extension.get()) {
482 // We don't notify observers of the added extension yet. Rather, observers
483 // should wait for the "OnToolbarModelInitialized" notification, and then
484 // bulk-update. (This saves a lot of bouncing-back-and-forth here, and
485 // allows observers to ensure that the extension system is always
486 // initialized before using the extensions).
487 toolbar_items_.push_back(extension);
491 UMA_HISTOGRAM_COUNTS_100(
492 "ExtensionToolbarModel.BrowserActionsPermanentlyHidden", hidden);
493 UMA_HISTOGRAM_COUNTS_100("ExtensionToolbarModel.BrowserActionsCount",
494 toolbar_items_.size());
496 if (!toolbar_items_.empty()) {
497 // Visible count can be -1, meaning: 'show all'. Since UMA converts negative
498 // values to 0, this would be counted as 'show none' unless we convert it to
499 // max.
500 UMA_HISTOGRAM_COUNTS_100("ExtensionToolbarModel.BrowserActionsVisible",
501 visible_icon_count_ == -1 ?
502 base::HistogramBase::kSampleType_MAX :
503 visible_icon_count_);
507 void ExtensionToolbarModel::IncognitoPopulate() {
508 DCHECK(profile_->IsOffTheRecord());
509 const ExtensionToolbarModel* original_model =
510 ExtensionToolbarModel::Get(profile_->GetOriginalProfile());
512 // Find the absolute value of the original model's count.
513 int original_visible = original_model->visible_icon_count();
515 // In incognito mode, we show only those extensions that are
516 // incognito-enabled. Further, any actions that were overflowed in regular
517 // mode are still overflowed. Order is the same as in regular mode.
518 visible_icon_count_ = 0;
519 for (ExtensionList::const_iterator iter =
520 original_model->toolbar_items_.begin();
521 iter != original_model->toolbar_items_.end(); ++iter) {
522 if (ShouldAddExtension(iter->get())) {
523 toolbar_items_.push_back(*iter);
524 if (iter - original_model->toolbar_items_.begin() < original_visible)
525 ++visible_icon_count_;
530 void ExtensionToolbarModel::UpdatePrefs() {
531 if (!extension_prefs_ || profile_->IsOffTheRecord())
532 return;
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, size_t 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();
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(
551 this,
552 NOTIFICATION_EXTENSION_BROWSER_ACTION_VISIBILITY_CHANGED,
553 content::Source<ExtensionPrefs>(extension_prefs_))) {
554 was_registered = true;
555 registrar_.Remove(
556 this,
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) {
563 registrar_.Add(this,
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_)
579 return;
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);
593 // Clear out the old...
594 while (!toolbar_items_.empty()) {
595 scoped_refptr<const Extension> extension = toolbar_items_.back();
596 toolbar_items_.pop_back();
597 FOR_EACH_OBSERVER(
598 Observer, observers_, ToolbarExtensionRemoved(extension.get()));
600 DCHECK(toolbar_items_.empty());
602 // ...Add the new...
603 Populate(&last_known_positions_);
605 // ...And notify.
606 for (size_t i = 0; i < toolbar_items().size(); ++i) {
607 FOR_EACH_OBSERVER(Observer,
608 observers_,
609 ToolbarExtensionAdded(toolbar_items()[i].get(), i));
612 if (last_known_positions_.size() > pref_position_size) {
613 // Need to update pref because we have extra icons. But can't call
614 // UpdatePrefs() directly within observation closure.
615 base::MessageLoop::current()->PostTask(
616 FROM_HERE,
617 base::Bind(&ExtensionToolbarModel::UpdatePrefs,
618 weak_ptr_factory_.GetWeakPtr()));
622 int ExtensionToolbarModel::GetIndexForId(const std::string& id) const {
623 for (size_t i = 0; i < toolbar_items().size(); ++i) {
624 if (toolbar_items()[i]->id() == id)
625 return i;
627 return -1;
630 bool ExtensionToolbarModel::ShowExtensionActionPopup(
631 const Extension* extension,
632 Browser* browser,
633 bool grant_active_tab) {
634 ObserverListBase<Observer>::Iterator it(observers_);
635 Observer* obs = NULL;
636 // Look for the Observer associated with the browser.
637 // This would be cleaner if we had an abstract class for the Toolbar UI
638 // (like we do for LocationBar), but sadly, we don't.
639 while ((obs = it.GetNext()) != NULL) {
640 if (obs->GetBrowser() == browser)
641 return obs->ShowExtensionActionPopup(extension, grant_active_tab);
643 return false;
646 void ExtensionToolbarModel::EnsureVisibility(
647 const ExtensionIdList& extension_ids) {
648 if (all_icons_visible())
649 return; // Already showing all.
651 // Otherwise, make sure we have enough room to show all the extensions
652 // requested.
653 if (visible_icon_count() < extension_ids.size())
654 SetVisibleIconCount(extension_ids.size());
656 if (all_icons_visible())
657 return; // May have been set to max by SetVisibleIconCount.
659 // Guillotine's Delight: Move an orange noble to the front of the line.
660 for (ExtensionIdList::const_iterator it = extension_ids.begin();
661 it != extension_ids.end(); ++it) {
662 for (ExtensionList::const_iterator extension = toolbar_items_.begin();
663 extension != toolbar_items_.end(); ++extension) {
664 if ((*extension)->id() == (*it)) {
665 if (extension - toolbar_items_.begin() >=
666 static_cast<int>(visible_icon_count()))
667 MoveExtensionIcon((*extension)->id(), 0);
668 break;
674 bool ExtensionToolbarModel::HighlightExtensions(
675 const ExtensionIdList& extension_ids) {
676 highlighted_items_.clear();
678 for (ExtensionIdList::const_iterator id = extension_ids.begin();
679 id != extension_ids.end();
680 ++id) {
681 for (ExtensionList::const_iterator extension = toolbar_items_.begin();
682 extension != toolbar_items_.end();
683 ++extension) {
684 if (*id == (*extension)->id())
685 highlighted_items_.push_back(*extension);
689 // If we have any items in |highlighted_items_|, then we entered highlighting
690 // mode.
691 if (highlighted_items_.size()) {
692 old_visible_icon_count_ = visible_icon_count_;
693 is_highlighting_ = true;
694 if (visible_icon_count() < extension_ids.size())
695 SetVisibleIconCount(extension_ids.size());
697 FOR_EACH_OBSERVER(Observer, observers_, ToolbarHighlightModeChanged(true));
698 return true;
701 // Otherwise, we didn't enter highlighting mode (and, in fact, exited it if
702 // we were otherwise in it).
703 if (is_highlighting_)
704 StopHighlighting();
705 return false;
708 void ExtensionToolbarModel::StopHighlighting() {
709 if (is_highlighting_) {
710 highlighted_items_.clear();
711 is_highlighting_ = false;
712 if (old_visible_icon_count_ != visible_icon_count_)
713 SetVisibleIconCount(old_visible_icon_count_);
714 FOR_EACH_OBSERVER(Observer, observers_, ToolbarHighlightModeChanged(false));
718 } // namespace extensions