Add a webstorePrivate API to show a permission prompt for delegated bundle installs
[chromium-blink-merge.git] / chrome / browser / extensions / extension_toolbar_model.cc
blobdcca35eaae4cd8de20dac7ac4d074ae70354f0af
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/location.h"
11 #include "base/metrics/histogram.h"
12 #include "base/metrics/histogram_base.h"
13 #include "base/prefs/pref_service.h"
14 #include "base/single_thread_task_runner.h"
15 #include "base/thread_task_runner_handle.h"
16 #include "chrome/browser/chrome_notification_types.h"
17 #include "chrome/browser/extensions/api/extension_action/extension_action_api.h"
18 #include "chrome/browser/extensions/extension_action_manager.h"
19 #include "chrome/browser/extensions/extension_tab_util.h"
20 #include "chrome/browser/extensions/extension_toolbar_model_factory.h"
21 #include "chrome/browser/extensions/extension_util.h"
22 #include "chrome/browser/extensions/tab_helper.h"
23 #include "chrome/browser/profiles/profile.h"
24 #include "chrome/browser/ui/browser.h"
25 #include "chrome/browser/ui/tabs/tab_strip_model.h"
26 #include "content/public/browser/notification_details.h"
27 #include "content/public/browser/notification_source.h"
28 #include "content/public/browser/web_contents.h"
29 #include "extensions/browser/extension_prefs.h"
30 #include "extensions/browser/extension_registry.h"
31 #include "extensions/browser/extension_system.h"
32 #include "extensions/browser/pref_names.h"
33 #include "extensions/common/extension.h"
34 #include "extensions/common/extension_set.h"
35 #include "extensions/common/feature_switch.h"
36 #include "extensions/common/manifest_constants.h"
37 #include "extensions/common/one_shot_event.h"
39 namespace extensions {
41 ExtensionToolbarModel::ExtensionToolbarModel(Profile* profile,
42 ExtensionPrefs* extension_prefs)
43 : profile_(profile),
44 extension_prefs_(extension_prefs),
45 prefs_(profile_->GetPrefs()),
46 extension_action_api_(ExtensionActionAPI::Get(profile_)),
47 extensions_initialized_(false),
48 include_all_extensions_(
49 FeatureSwitch::extension_action_redesign()->IsEnabled()),
50 is_highlighting_(false),
51 extension_action_observer_(this),
52 extension_registry_observer_(this),
53 weak_ptr_factory_(this) {
54 ExtensionSystem::Get(profile_)->ready().Post(
55 FROM_HERE,
56 base::Bind(&ExtensionToolbarModel::OnReady,
57 weak_ptr_factory_.GetWeakPtr()));
58 visible_icon_count_ = prefs_->GetInteger(pref_names::kToolbarSize);
60 // We only care about watching the prefs if not in incognito mode.
61 if (!profile_->IsOffTheRecord()) {
62 pref_change_registrar_.Init(prefs_);
63 pref_change_callback_ =
64 base::Bind(&ExtensionToolbarModel::OnExtensionToolbarPrefChange,
65 base::Unretained(this));
66 pref_change_registrar_.Add(pref_names::kToolbar, pref_change_callback_);
70 ExtensionToolbarModel::~ExtensionToolbarModel() {
73 // static
74 ExtensionToolbarModel* ExtensionToolbarModel::Get(Profile* profile) {
75 return ExtensionToolbarModelFactory::GetForProfile(profile);
78 void ExtensionToolbarModel::AddObserver(Observer* observer) {
79 observers_.AddObserver(observer);
82 void ExtensionToolbarModel::RemoveObserver(Observer* observer) {
83 observers_.RemoveObserver(observer);
86 void ExtensionToolbarModel::MoveExtensionIcon(const std::string& id,
87 size_t index) {
88 ExtensionList::iterator pos = toolbar_items_.begin();
89 while (pos != toolbar_items_.end() && (*pos)->id() != id)
90 ++pos;
91 if (pos == toolbar_items_.end()) {
92 NOTREACHED();
93 return;
95 scoped_refptr<const Extension> extension = *pos;
96 toolbar_items_.erase(pos);
98 ExtensionIdList::iterator pos_id = std::find(last_known_positions_.begin(),
99 last_known_positions_.end(),
100 id);
101 if (pos_id != last_known_positions_.end())
102 last_known_positions_.erase(pos_id);
104 if (index < toolbar_items_.size()) {
105 // If the index is not at the end, find the item currently at |index|, and
106 // insert |extension| before it in both |toolbar_items_| and
107 // |last_known_positions_|.
108 ExtensionList::iterator iter = toolbar_items_.begin() + index;
109 last_known_positions_.insert(std::find(last_known_positions_.begin(),
110 last_known_positions_.end(),
111 (*iter)->id()),
112 id);
113 toolbar_items_.insert(iter, extension);
114 } else {
115 // Otherwise, put |extension| at the end.
116 DCHECK_EQ(toolbar_items_.size(), index);
117 index = toolbar_items_.size();
118 toolbar_items_.push_back(extension);
119 last_known_positions_.push_back(id);
122 FOR_EACH_OBSERVER(Observer, observers_,
123 OnToolbarExtensionMoved(extension.get(), index));
124 MaybeUpdateVisibilityPref(extension.get(), index);
125 UpdatePrefs();
128 void ExtensionToolbarModel::SetVisibleIconCount(size_t count) {
129 visible_icon_count_ = (count >= toolbar_items_.size()) ? -1 : count;
131 // Only set the prefs if we're not in highlight mode and the profile is not
132 // incognito. Highlight mode is designed to be a transitory state, and should
133 // not persist across browser restarts (though it may be re-entered), and we
134 // don't store anything in incognito.
135 if (!is_highlighting_ && !profile_->IsOffTheRecord()) {
136 // Additionally, if we are using the new toolbar, any icons which are in the
137 // overflow menu are considered "hidden". But it so happens that the times
138 // we are likely to call SetVisibleIconCount() are also those when we are
139 // in flux. So wait for things to cool down before setting the prefs.
140 base::ThreadTaskRunnerHandle::Get()->PostTask(
141 FROM_HERE,
142 base::Bind(&ExtensionToolbarModel::MaybeUpdateVisibilityPrefs,
143 weak_ptr_factory_.GetWeakPtr()));
144 prefs_->SetInteger(pref_names::kToolbarSize, visible_icon_count_);
147 FOR_EACH_OBSERVER(Observer, observers_, OnToolbarVisibleCountChanged());
150 void ExtensionToolbarModel::OnExtensionActionUpdated(
151 ExtensionAction* extension_action,
152 content::WebContents* web_contents,
153 content::BrowserContext* browser_context) {
154 const Extension* extension =
155 ExtensionRegistry::Get(profile_)->enabled_extensions().GetByID(
156 extension_action->extension_id());
157 // Notify observers if the extension exists and is in the model.
158 if (std::find(toolbar_items_.begin(), toolbar_items_.end(), extension) !=
159 toolbar_items_.end()) {
160 FOR_EACH_OBSERVER(Observer, observers_,
161 OnToolbarExtensionUpdated(extension));
165 void ExtensionToolbarModel::OnExtensionActionVisibilityChanged(
166 const std::string& extension_id,
167 bool is_now_visible) {
168 const Extension* extension =
169 ExtensionRegistry::Get(profile_)->GetExtensionById(
170 extension_id, ExtensionRegistry::EVERYTHING);
172 // Hiding works differently with the new and old toolbars.
173 if (include_all_extensions_) {
174 // It's possible that we haven't added this extension yet, if its
175 // visibility was adjusted in the course of its initialization.
176 if (std::find(toolbar_items_.begin(), toolbar_items_.end(), extension) ==
177 toolbar_items_.end())
178 return;
180 int new_size = 0;
181 int new_index = 0;
182 if (is_now_visible) {
183 // If this action used to be hidden, we can't possibly be showing all.
184 DCHECK_LT(visible_icon_count(), toolbar_items_.size());
185 // Grow the bar by one and move the extension to the end of the visibles.
186 new_size = visible_icon_count() + 1;
187 new_index = new_size - 1;
188 } else {
189 // If we're hiding one, we must be showing at least one.
190 DCHECK_GE(visible_icon_count(), 0u);
191 // Shrink the bar by one and move the extension to the beginning of the
192 // overflow menu.
193 new_size = visible_icon_count() - 1;
194 new_index = new_size;
196 SetVisibleIconCount(new_size);
197 MoveExtensionIcon(extension->id(), new_index);
198 } else { // Don't include all extensions.
199 if (is_now_visible)
200 AddExtension(extension);
201 else
202 RemoveExtension(extension);
206 void ExtensionToolbarModel::OnExtensionLoaded(
207 content::BrowserContext* browser_context,
208 const Extension* extension) {
209 // We don't want to add the same extension twice. It may have already been
210 // added by EXTENSION_BROWSER_ACTION_VISIBILITY_CHANGED below, if the user
211 // hides the browser action and then disables and enables the extension.
212 for (size_t i = 0; i < toolbar_items_.size(); i++) {
213 if (toolbar_items_[i].get() == extension)
214 return;
217 AddExtension(extension);
220 void ExtensionToolbarModel::OnExtensionUnloaded(
221 content::BrowserContext* browser_context,
222 const Extension* extension,
223 UnloadedExtensionInfo::Reason reason) {
224 RemoveExtension(extension);
227 void ExtensionToolbarModel::OnExtensionUninstalled(
228 content::BrowserContext* browser_context,
229 const Extension* extension,
230 extensions::UninstallReason reason) {
231 // Remove the extension id from the ordered list, if it exists (the extension
232 // might not be represented in the list because it might not have an icon).
233 ExtensionIdList::iterator pos =
234 std::find(last_known_positions_.begin(),
235 last_known_positions_.end(), extension->id());
237 if (pos != last_known_positions_.end()) {
238 last_known_positions_.erase(pos);
239 UpdatePrefs();
243 void ExtensionToolbarModel::OnReady() {
244 ExtensionRegistry* registry = ExtensionRegistry::Get(profile_);
245 InitializeExtensionList();
246 // Wait until the extension system is ready before observing any further
247 // changes so that the toolbar buttons can be shown in their stable ordering
248 // taken from prefs.
249 extension_registry_observer_.Add(registry);
250 extension_action_observer_.Add(extension_action_api_);
253 size_t ExtensionToolbarModel::FindNewPositionFromLastKnownGood(
254 const Extension* extension) {
255 // See if we have last known good position for this extension.
256 size_t new_index = 0;
257 // Loop through the ID list of known positions, to count the number of visible
258 // extension icons preceding |extension|.
259 for (ExtensionIdList::const_iterator iter_id = last_known_positions_.begin();
260 iter_id < last_known_positions_.end(); ++iter_id) {
261 if ((*iter_id) == extension->id())
262 return new_index; // We've found the right position.
263 // Found an id, need to see if it is visible.
264 for (ExtensionList::const_iterator iter_ext = toolbar_items_.begin();
265 iter_ext < toolbar_items_.end(); ++iter_ext) {
266 if ((*iter_ext)->id() == (*iter_id)) {
267 // This extension is visible, update the index value.
268 ++new_index;
269 break;
274 // Position not found.
275 return toolbar_items_.size();
278 bool ExtensionToolbarModel::ShouldAddExtension(const Extension* extension) {
279 // In incognito mode, don't add any extensions that aren't incognito-enabled.
280 if (profile_->IsOffTheRecord() &&
281 !util::IsIncognitoEnabled(extension->id(), profile_))
282 return false;
284 ExtensionActionManager* action_manager =
285 ExtensionActionManager::Get(profile_);
286 if (include_all_extensions_) {
287 // In this case, we don't care about the browser action visibility, because
288 // we want to show each extension regardless.
289 // TODO(devlin): Extension actions which are not visible should be moved to
290 // the overflow menu by default.
291 return action_manager->GetExtensionAction(*extension) != NULL;
294 return action_manager->GetBrowserAction(*extension) &&
295 extension_action_api_->GetBrowserActionVisibility(extension->id());
298 void ExtensionToolbarModel::AddExtension(const Extension* extension) {
299 // We only use AddExtension() once the system is initialized.
300 DCHECK(extensions_initialized_);
301 if (!ShouldAddExtension(extension))
302 return;
304 // See if we have a last known good position for this extension.
305 bool is_new_extension =
306 std::find(last_known_positions_.begin(),
307 last_known_positions_.end(),
308 extension->id()) == last_known_positions_.end();
310 // New extensions go at the right (end) of the visible extensions. Other
311 // extensions go at their previous position.
312 size_t new_index = 0;
313 if (is_new_extension) {
314 new_index = Manifest::IsComponentLocation(extension->location()) ?
315 0 : visible_icon_count();
316 // For the last-known position, we use the index of the extension that is
317 // just before this extension, plus one. (Note that this isn't the same
318 // as new_index + 1, because last_known_positions_ can include disabled
319 // extensions.)
320 int new_last_known_index =
321 new_index == 0 ? 0 :
322 std::find(last_known_positions_.begin(),
323 last_known_positions_.end(),
324 toolbar_items_[new_index - 1]->id()) -
325 last_known_positions_.begin() + 1;
326 // In theory, the extension before this one should always
327 // be in last known positions, but if something funny happened with prefs,
328 // make sure we handle it.
329 // TODO(devlin): Track down these cases so we can CHECK this.
330 new_last_known_index =
331 std::min<int>(new_last_known_index, last_known_positions_.size());
332 last_known_positions_.insert(
333 last_known_positions_.begin() + new_last_known_index, extension->id());
334 UpdatePrefs();
335 } else {
336 new_index = FindNewPositionFromLastKnownGood(extension);
339 toolbar_items_.insert(toolbar_items_.begin() + new_index, extension);
341 // If we're currently highlighting, then even though we add a browser action
342 // to the full list (|toolbar_items_|, there won't be another *visible*
343 // browser action, which was what the observers care about.
344 if (!is_highlighting_) {
345 FOR_EACH_OBSERVER(Observer, observers_,
346 OnToolbarExtensionAdded(extension, new_index));
348 int visible_count_delta = 0;
349 if (is_new_extension && !all_icons_visible()) {
350 // If this is a new extension (and not all extensions are visible), we
351 // expand the toolbar out so that the new one can be seen.
352 visible_count_delta = 1;
353 } else if (profile_->IsOffTheRecord()) {
354 // If this is an incognito profile, we also have to check to make sure the
355 // overflow matches the main bar's status.
356 ExtensionToolbarModel* main_model =
357 ExtensionToolbarModel::Get(profile_->GetOriginalProfile());
358 // Find what the index will be in the main bar. Because Observer calls are
359 // nondeterministic, we can't just assume the main bar will have the
360 // extension and look it up.
361 size_t main_index =
362 main_model->FindNewPositionFromLastKnownGood(extension);
363 bool visible = main_index < main_model->visible_icon_count();
364 // We may need to adjust the visible count if the incognito bar isn't
365 // showing all icons and this one is visible, or if it is showing all
366 // icons and this is hidden.
367 if (visible && !all_icons_visible())
368 visible_count_delta = 1;
369 else if (!visible && all_icons_visible())
370 visible_count_delta = -1;
373 if (visible_count_delta)
374 SetVisibleIconCount(visible_icon_count() + visible_count_delta);
377 MaybeUpdateVisibilityPref(extension, new_index);
380 void ExtensionToolbarModel::RemoveExtension(const Extension* extension) {
381 ExtensionList::iterator pos =
382 std::find(toolbar_items_.begin(), toolbar_items_.end(), extension);
383 if (pos == toolbar_items_.end())
384 return;
386 // If our visible count is set to the current size, we need to decrement it.
387 if (visible_icon_count_ == static_cast<int>(toolbar_items_.size()))
388 SetVisibleIconCount(toolbar_items_.size() - 1);
390 toolbar_items_.erase(pos);
392 // If we're in highlight mode, we also have to remove the extension from
393 // the highlighted list.
394 if (is_highlighting_) {
395 pos = std::find(highlighted_items_.begin(),
396 highlighted_items_.end(),
397 extension);
398 if (pos != highlighted_items_.end()) {
399 highlighted_items_.erase(pos);
400 FOR_EACH_OBSERVER(Observer, observers_,
401 OnToolbarExtensionRemoved(extension));
402 // If the highlighted list is now empty, we stop highlighting.
403 if (highlighted_items_.empty())
404 StopHighlighting();
406 } else {
407 FOR_EACH_OBSERVER(Observer, observers_,
408 OnToolbarExtensionRemoved(extension));
411 UpdatePrefs();
414 // Combine the currently enabled extensions that have browser actions (which
415 // we get from the ExtensionRegistry) with the ordering we get from the
416 // pref service. For robustness we use a somewhat inefficient process:
417 // 1. Create a vector of extensions sorted by their pref values. This vector may
418 // have holes.
419 // 2. Create a vector of extensions that did not have a pref value.
420 // 3. Remove holes from the sorted vector and append the unsorted vector.
421 void ExtensionToolbarModel::InitializeExtensionList() {
422 DCHECK(toolbar_items_.empty()); // We shouldn't have any items yet.
424 last_known_positions_ = extension_prefs_->GetToolbarOrder();
425 if (profile_->IsOffTheRecord())
426 IncognitoPopulate();
427 else
428 Populate(&last_known_positions_);
430 extensions_initialized_ = true;
431 MaybeUpdateVisibilityPrefs();
432 FOR_EACH_OBSERVER(Observer, observers_, OnToolbarModelInitialized());
435 void ExtensionToolbarModel::Populate(ExtensionIdList* positions) {
436 DCHECK(!profile_->IsOffTheRecord());
437 const ExtensionSet& extensions =
438 ExtensionRegistry::Get(profile_)->enabled_extensions();
439 // Items that have explicit positions.
440 ExtensionList sorted(positions->size(), NULL);
441 // The items that don't have explicit positions.
442 ExtensionList unsorted;
444 // Create the lists.
445 int hidden = 0;
446 for (const scoped_refptr<const Extension>& extension : extensions) {
447 if (!ShouldAddExtension(extension.get())) {
448 if (!extension_action_api_->GetBrowserActionVisibility(extension->id()))
449 ++hidden;
450 continue;
453 ExtensionIdList::const_iterator pos =
454 std::find(positions->begin(), positions->end(), extension->id());
455 if (pos != positions->end()) {
456 sorted[pos - positions->begin()] = extension;
457 } else {
458 // Unknown extension - push it to the back of unsorted, and add it to the
459 // list of ids at the end.
460 unsorted.push_back(extension);
461 positions->push_back(extension->id());
465 // Merge the lists.
466 sorted.insert(sorted.end(), unsorted.begin(), unsorted.end());
467 toolbar_items_.reserve(sorted.size());
469 for (const scoped_refptr<const Extension>& extension : sorted) {
470 // It's possible for the extension order to contain items that aren't
471 // actually loaded on this machine. For example, when extension sync is on,
472 // we sync the extension order as-is but double-check with the user before
473 // syncing NPAPI-containing extensions, so if one of those is not actually
474 // synced, we'll get a NULL in the list. This sort of case can also happen
475 // if some error prevents an extension from loading.
476 if (extension.get()) {
477 // We don't notify observers of the added extension yet. Rather, observers
478 // should wait for the "OnToolbarModelInitialized" notification, and then
479 // bulk-update. (This saves a lot of bouncing-back-and-forth here, and
480 // allows observers to ensure that the extension system is always
481 // initialized before using the extensions).
482 toolbar_items_.push_back(extension);
486 UMA_HISTOGRAM_COUNTS_100(
487 "ExtensionToolbarModel.BrowserActionsPermanentlyHidden", hidden);
488 UMA_HISTOGRAM_COUNTS_100("ExtensionToolbarModel.BrowserActionsCount",
489 toolbar_items_.size());
491 if (!toolbar_items_.empty()) {
492 // Visible count can be -1, meaning: 'show all'. Since UMA converts negative
493 // values to 0, this would be counted as 'show none' unless we convert it to
494 // max.
495 UMA_HISTOGRAM_COUNTS_100("ExtensionToolbarModel.BrowserActionsVisible",
496 visible_icon_count_ == -1 ?
497 base::HistogramBase::kSampleType_MAX :
498 visible_icon_count_);
502 void ExtensionToolbarModel::IncognitoPopulate() {
503 DCHECK(profile_->IsOffTheRecord());
504 const ExtensionToolbarModel* original_model =
505 ExtensionToolbarModel::Get(profile_->GetOriginalProfile());
507 // Find the absolute value of the original model's count.
508 int original_visible = original_model->visible_icon_count();
510 // In incognito mode, we show only those extensions that are
511 // incognito-enabled. Further, any actions that were overflowed in regular
512 // mode are still overflowed. Order is the same as in regular mode.
513 visible_icon_count_ = 0;
514 for (ExtensionList::const_iterator iter =
515 original_model->toolbar_items_.begin();
516 iter != original_model->toolbar_items_.end(); ++iter) {
517 if (ShouldAddExtension(iter->get())) {
518 toolbar_items_.push_back(*iter);
519 if (iter - original_model->toolbar_items_.begin() < original_visible)
520 ++visible_icon_count_;
525 void ExtensionToolbarModel::UpdatePrefs() {
526 if (!extension_prefs_ || profile_->IsOffTheRecord())
527 return;
529 // Don't observe change caused by self.
530 pref_change_registrar_.Remove(pref_names::kToolbar);
531 extension_prefs_->SetToolbarOrder(last_known_positions_);
532 pref_change_registrar_.Add(pref_names::kToolbar, pref_change_callback_);
535 void ExtensionToolbarModel::MaybeUpdateVisibilityPref(
536 const Extension* extension, size_t index) {
537 // We only update the visibility pref for hidden/not hidden based on the
538 // overflow menu with the new toolbar design.
539 if (include_all_extensions_ && !profile_->IsOffTheRecord()) {
540 bool visible = index < visible_icon_count();
541 if (visible != extension_action_api_->GetBrowserActionVisibility(
542 extension->id())) {
543 // Don't observe changes caused by ourselves.
544 bool was_registered = false;
545 if (extension_action_observer_.IsObserving(extension_action_api_)) {
546 was_registered = true;
547 extension_action_observer_.RemoveAll();
549 extension_action_api_->SetBrowserActionVisibility(extension->id(),
550 visible);
551 if (was_registered)
552 extension_action_observer_.Add(extension_action_api_);
557 void ExtensionToolbarModel::MaybeUpdateVisibilityPrefs() {
558 for (size_t i = 0u; i < toolbar_items_.size(); ++i)
559 MaybeUpdateVisibilityPref(toolbar_items_[i].get(), i);
562 void ExtensionToolbarModel::OnExtensionToolbarPrefChange() {
563 // If extensions are not ready, defer to later Populate() call.
564 if (!extensions_initialized_)
565 return;
567 // Recalculate |last_known_positions_| to be |pref_positions| followed by
568 // ones that are only in |last_known_positions_|.
569 ExtensionIdList pref_positions = extension_prefs_->GetToolbarOrder();
570 size_t pref_position_size = pref_positions.size();
571 for (size_t i = 0; i < last_known_positions_.size(); ++i) {
572 if (std::find(pref_positions.begin(), pref_positions.end(),
573 last_known_positions_[i]) == pref_positions.end()) {
574 pref_positions.push_back(last_known_positions_[i]);
577 last_known_positions_.swap(pref_positions);
579 int desired_index = 0;
580 // Loop over the updated list of last known positions, moving any extensions
581 // that are in the wrong place.
582 for (const std::string& id : last_known_positions_) {
583 int current_index = GetIndexForId(id);
584 if (current_index == -1)
585 continue;
586 if (current_index != desired_index) {
587 scoped_refptr<const Extension> extension = toolbar_items_[current_index];
588 toolbar_items_.erase(toolbar_items_.begin() + current_index);
589 toolbar_items_.insert(toolbar_items_.begin() + desired_index, extension);
590 // Notify the observers to keep them up-to-date.
591 FOR_EACH_OBSERVER(
592 Observer, observers_,
593 OnToolbarExtensionMoved(extension.get(), desired_index));
595 ++desired_index;
598 if (last_known_positions_.size() > pref_position_size) {
599 // Need to update pref because we have extra icons. But can't call
600 // UpdatePrefs() directly within observation closure.
601 base::ThreadTaskRunnerHandle::Get()->PostTask(
602 FROM_HERE, base::Bind(&ExtensionToolbarModel::UpdatePrefs,
603 weak_ptr_factory_.GetWeakPtr()));
607 int ExtensionToolbarModel::GetIndexForId(const std::string& id) const {
608 for (size_t i = 0; i < toolbar_items().size(); ++i) {
609 if (toolbar_items()[i]->id() == id)
610 return i;
612 return -1;
615 bool ExtensionToolbarModel::ShowExtensionActionPopup(
616 const Extension* extension,
617 Browser* browser,
618 bool grant_active_tab) {
619 base::ObserverListBase<Observer>::Iterator it(&observers_);
620 Observer* obs = NULL;
621 // Look for the Observer associated with the browser.
622 // This would be cleaner if we had an abstract class for the Toolbar UI
623 // (like we do for LocationBar), but sadly, we don't.
624 while ((obs = it.GetNext()) != NULL) {
625 if (obs->GetBrowser() == browser)
626 return obs->ShowExtensionActionPopup(extension, grant_active_tab);
628 return false;
631 void ExtensionToolbarModel::EnsureVisibility(
632 const ExtensionIdList& extension_ids) {
633 if (all_icons_visible())
634 return; // Already showing all.
636 // Otherwise, make sure we have enough room to show all the extensions
637 // requested.
638 if (visible_icon_count() < extension_ids.size())
639 SetVisibleIconCount(extension_ids.size());
641 if (all_icons_visible())
642 return; // May have been set to max by SetVisibleIconCount.
644 // Guillotine's Delight: Move an orange noble to the front of the line.
645 for (ExtensionIdList::const_iterator it = extension_ids.begin();
646 it != extension_ids.end(); ++it) {
647 for (ExtensionList::const_iterator extension = toolbar_items_.begin();
648 extension != toolbar_items_.end(); ++extension) {
649 if ((*extension)->id() == (*it)) {
650 if (extension - toolbar_items_.begin() >=
651 static_cast<int>(visible_icon_count()))
652 MoveExtensionIcon((*extension)->id(), 0);
653 break;
659 bool ExtensionToolbarModel::HighlightExtensions(
660 const ExtensionIdList& extension_ids) {
661 highlighted_items_.clear();
663 for (ExtensionIdList::const_iterator id = extension_ids.begin();
664 id != extension_ids.end();
665 ++id) {
666 for (ExtensionList::const_iterator extension = toolbar_items_.begin();
667 extension != toolbar_items_.end();
668 ++extension) {
669 if (*id == (*extension)->id())
670 highlighted_items_.push_back(*extension);
674 // If we have any items in |highlighted_items_|, then we entered highlighting
675 // mode.
676 if (highlighted_items_.size()) {
677 old_visible_icon_count_ = visible_icon_count_;
678 if (visible_icon_count() < extension_ids.size())
679 SetVisibleIconCount(extension_ids.size());
681 // It's important that is_highlighting_ is changed immediately before the
682 // observers are notified since it changes the result of toolbar_items().
683 is_highlighting_ = true;
684 FOR_EACH_OBSERVER(Observer, observers_,
685 OnToolbarHighlightModeChanged(true));
686 return true;
689 // Otherwise, we didn't enter highlighting mode (and, in fact, exited it if
690 // we were otherwise in it).
691 if (is_highlighting_)
692 StopHighlighting();
693 return false;
696 void ExtensionToolbarModel::StopHighlighting() {
697 if (is_highlighting_) {
698 if (old_visible_icon_count_ != visible_icon_count_)
699 SetVisibleIconCount(old_visible_icon_count_);
701 // It's important that is_highlighting_ is changed immediately before the
702 // observers are notified since it changes the result of toolbar_items().
703 is_highlighting_ = false;
704 FOR_EACH_OBSERVER(Observer, observers_,
705 OnToolbarHighlightModeChanged(false));
706 // For the same reason, we don't clear highlighted_items_ until after the
707 // mode changed.
708 highlighted_items_.clear();
712 bool ExtensionToolbarModel::RedesignIsShowingNewIcons() const {
713 for (const scoped_refptr<const Extension>& extension : toolbar_items_) {
714 // Without the redesign, we only show extensions with browser actions.
715 // Any extension without a browser action is an indication that we're
716 // showing something new.
717 if (!extension->manifest()->HasKey(manifest_keys::kBrowserAction))
718 return true;
720 return false;
723 } // namespace extensions