• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.
4 
5 #include "chrome/browser/extensions/extension_toolbar_model.h"
6 
7 #include <algorithm>
8 #include <string>
9 
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 "chrome/common/pref_names.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"
36 
37 namespace extensions {
38 
ExtensionToolbarModel(Profile * profile,ExtensionPrefs * extension_prefs)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);
56   pref_change_registrar_.Init(prefs_);
57   pref_change_callback_ =
58       base::Bind(&ExtensionToolbarModel::OnExtensionToolbarPrefChange,
59                  base::Unretained(this));
60   pref_change_registrar_.Add(pref_names::kToolbar, pref_change_callback_);
61 }
62 
~ExtensionToolbarModel()63 ExtensionToolbarModel::~ExtensionToolbarModel() {
64 }
65 
66 // static
Get(Profile * profile)67 ExtensionToolbarModel* ExtensionToolbarModel::Get(Profile* profile) {
68   return ExtensionToolbarModelFactory::GetForProfile(profile);
69 }
70 
AddObserver(Observer * observer)71 void ExtensionToolbarModel::AddObserver(Observer* observer) {
72   observers_.AddObserver(observer);
73 }
74 
RemoveObserver(Observer * observer)75 void ExtensionToolbarModel::RemoveObserver(Observer* observer) {
76   observers_.RemoveObserver(observer);
77 }
78 
MoveExtensionIcon(const Extension * extension,int index)79 void ExtensionToolbarModel::MoveExtensionIcon(const Extension* extension,
80                                               int index) {
81   ExtensionList::iterator pos = std::find(toolbar_items_.begin(),
82       toolbar_items_.end(), extension);
83   if (pos == toolbar_items_.end()) {
84     NOTREACHED();
85     return;
86   }
87   toolbar_items_.erase(pos);
88 
89   ExtensionIdList::iterator pos_id;
90   pos_id = std::find(last_known_positions_.begin(),
91                      last_known_positions_.end(), extension->id());
92   if (pos_id != last_known_positions_.end())
93     last_known_positions_.erase(pos_id);
94 
95   int i = 0;
96   bool inserted = false;
97   for (ExtensionList::iterator iter = toolbar_items_.begin();
98        iter != toolbar_items_.end();
99        ++iter, ++i) {
100     if (i == index) {
101       pos_id = std::find(last_known_positions_.begin(),
102                          last_known_positions_.end(), (*iter)->id());
103       last_known_positions_.insert(pos_id, extension->id());
104 
105       toolbar_items_.insert(iter, make_scoped_refptr(extension));
106       inserted = true;
107       break;
108     }
109   }
110 
111   if (!inserted) {
112     DCHECK_EQ(index, static_cast<int>(toolbar_items_.size()));
113     index = toolbar_items_.size();
114 
115     toolbar_items_.push_back(make_scoped_refptr(extension));
116     last_known_positions_.push_back(extension->id());
117   }
118 
119   FOR_EACH_OBSERVER(
120       Observer, observers_, ToolbarExtensionMoved(extension, index));
121   MaybeUpdateVisibilityPref(extension, index);
122   UpdatePrefs();
123 }
124 
SetVisibleIconCount(int count)125 void ExtensionToolbarModel::SetVisibleIconCount(int count) {
126   visible_icon_count_ =
127       count == static_cast<int>(toolbar_items_.size()) ? -1 : count;
128 
129   // Only set the prefs if we're not in highlight mode. Highlight mode is
130   // designed to be a transitory state, and should not persist across browser
131   // restarts (though it may be re-entered).
132   if (!is_highlighting_) {
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_);
142   }
143 }
144 
OnExtensionActionUpdated(ExtensionAction * extension_action,content::WebContents * web_contents,content::BrowserContext * browser_context)145 void ExtensionToolbarModel::OnExtensionActionUpdated(
146     ExtensionAction* extension_action,
147     content::WebContents* web_contents,
148     content::BrowserContext* browser_context) {
149   const Extension* extension =
150       ExtensionRegistry::Get(profile_)->enabled_extensions().GetByID(
151           extension_action->extension_id());
152   // Notify observers if the extension exists and is in the model.
153   if (extension &&
154       std::find(toolbar_items_.begin(),
155                 toolbar_items_.end(),
156                 extension) != toolbar_items_.end()) {
157     FOR_EACH_OBSERVER(Observer, observers_, ToolbarExtensionUpdated(extension));
158   }
159 }
160 
OnExtensionLoaded(content::BrowserContext * browser_context,const Extension * 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;
170   }
171 
172   AddExtension(extension);
173 }
174 
OnExtensionUnloaded(content::BrowserContext * browser_context,const Extension * extension,UnloadedExtensionInfo::Reason reason)175 void ExtensionToolbarModel::OnExtensionUnloaded(
176     content::BrowserContext* browser_context,
177     const Extension* extension,
178     UnloadedExtensionInfo::Reason reason) {
179   RemoveExtension(extension);
180 }
181 
OnExtensionUninstalled(content::BrowserContext * browser_context,const Extension * extension,extensions::UninstallReason reason)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());
191 
192   if (pos != last_known_positions_.end()) {
193     last_known_positions_.erase(pos);
194     UpdatePrefs();
195   }
196 }
197 
Observe(int type,const content::NotificationSource & source,const content::NotificationDetails & details)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);
207 
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     int new_size = 0;
213     int new_index = 0;
214     if (visible) {
215       // If this action used to be hidden, we can't possible be showing all.
216       DCHECK_NE(-1, visible_icon_count_);
217       // Grow the bar by one and move the extension to the end of the visibles.
218       new_size = visible_icon_count_ + 1;
219       new_index = new_size - 1;
220     } else {
221       // If we're hiding one, we must be showing at least one.
222       DCHECK_NE(visible_icon_count_, 0);
223       // Shrink the bar by one and move the extension to the beginning of the
224       // overflow menu.
225       new_size = visible_icon_count_ == -1 ?
226                      toolbar_items_.size() - 1 : visible_icon_count_ - 1;
227       new_index = new_size;
228     }
229     SetVisibleIconCount(new_size);
230     MoveExtensionIcon(extension, new_index);
231     FOR_EACH_OBSERVER(Observer, observers_, ToolbarVisibleCountChanged());
232   } else {  // Don't include all extensions.
233     if (visible)
234       AddExtension(extension);
235     else
236       RemoveExtension(extension);
237   }
238 }
239 
OnReady()240 void ExtensionToolbarModel::OnReady() {
241   ExtensionRegistry* registry = ExtensionRegistry::Get(profile_);
242   InitializeExtensionList(registry->enabled_extensions());
243   // Wait until the extension system is ready before observing any further
244   // changes so that the toolbar buttons can be shown in their stable ordering
245   // taken from prefs.
246   extension_registry_observer_.Add(registry);
247   extension_action_observer_.Add(ExtensionActionAPI::Get(profile_));
248   registrar_.Add(
249       this,
250       extensions::NOTIFICATION_EXTENSION_BROWSER_ACTION_VISIBILITY_CHANGED,
251       content::Source<ExtensionPrefs>(extension_prefs_));
252 }
253 
FindNewPositionFromLastKnownGood(const Extension * extension)254 size_t ExtensionToolbarModel::FindNewPositionFromLastKnownGood(
255     const Extension* extension) {
256   // See if we have last known good position for this extension.
257   size_t new_index = 0;
258   // Loop through the ID list of known positions, to count the number of visible
259   // extension icons preceding |extension|.
260   for (ExtensionIdList::const_iterator iter_id = last_known_positions_.begin();
261        iter_id < last_known_positions_.end(); ++iter_id) {
262     if ((*iter_id) == extension->id())
263       return new_index;  // We've found the right position.
264     // Found an id, need to see if it is visible.
265     for (ExtensionList::const_iterator iter_ext = toolbar_items_.begin();
266          iter_ext < toolbar_items_.end(); ++iter_ext) {
267       if ((*iter_ext)->id().compare(*iter_id) == 0) {
268         // This extension is visible, update the index value.
269         ++new_index;
270         break;
271       }
272     }
273   }
274 
275   // Position not found.
276   return toolbar_items_.size();
277 }
278 
ShouldAddExtension(const Extension * extension)279 bool ExtensionToolbarModel::ShouldAddExtension(const Extension* extension) {
280   ExtensionActionManager* action_manager =
281       ExtensionActionManager::Get(profile_);
282   if (include_all_extensions_) {
283     // In this case, we don't care about the browser action visibility, because
284     // we want to show each extension regardless.
285     // TODO(devlin): Extension actions which are not visible should be moved to
286     // the overflow menu by default.
287     return action_manager->GetBrowserAction(*extension) ||
288            action_manager->GetPageAction(*extension);
289   }
290 
291   return action_manager->GetBrowserAction(*extension) &&
292          ExtensionActionAPI::GetBrowserActionVisibility(
293              extension_prefs_, extension->id());
294 }
295 
AddExtension(const Extension * extension)296 void ExtensionToolbarModel::AddExtension(const Extension* extension) {
297   if (!ShouldAddExtension(extension))
298     return;
299 
300   size_t new_index = toolbar_items_.size();
301 
302   // See if we have a last known good position for this extension.
303   ExtensionIdList::iterator last_pos = std::find(last_known_positions_.begin(),
304                                                  last_known_positions_.end(),
305                                                  extension->id());
306   if (last_pos != last_known_positions_.end()) {
307     new_index = FindNewPositionFromLastKnownGood(extension);
308     if (new_index != toolbar_items_.size()) {
309       toolbar_items_.insert(toolbar_items_.begin() + new_index,
310                             make_scoped_refptr(extension));
311     } else {
312       toolbar_items_.push_back(make_scoped_refptr(extension));
313     }
314   } else {
315     // This is a never before seen extension, that was added to the end. Make
316     // sure to reflect that. (|new_index| was set above.)
317     toolbar_items_.push_back(make_scoped_refptr(extension));
318     last_known_positions_.push_back(extension->id());
319     UpdatePrefs();
320   }
321 
322   MaybeUpdateVisibilityPref(extension, new_index);
323 
324   // If we're currently highlighting, then even though we add a browser action
325   // to the full list (|toolbar_items_|, there won't be another *visible*
326   // browser action, which was what the observers care about.
327   if (!is_highlighting_) {
328     FOR_EACH_OBSERVER(
329         Observer, observers_, ToolbarExtensionAdded(extension, new_index));
330   }
331 }
332 
RemoveExtension(const Extension * extension)333 void ExtensionToolbarModel::RemoveExtension(const Extension* extension) {
334   ExtensionList::iterator pos =
335       std::find(toolbar_items_.begin(), toolbar_items_.end(), extension);
336   if (pos == toolbar_items_.end())
337     return;
338 
339   toolbar_items_.erase(pos);
340 
341   // If we're in highlight mode, we also have to remove the extension from
342   // the highlighted list.
343   if (is_highlighting_) {
344     pos = std::find(highlighted_items_.begin(),
345                     highlighted_items_.end(),
346                     extension);
347     if (pos != highlighted_items_.end()) {
348       highlighted_items_.erase(pos);
349       FOR_EACH_OBSERVER(
350           Observer, observers_, ToolbarExtensionRemoved(extension));
351       // If the highlighted list is now empty, we stop highlighting.
352       if (highlighted_items_.empty())
353         StopHighlighting();
354     }
355   } else {
356     FOR_EACH_OBSERVER(Observer, observers_, ToolbarExtensionRemoved(extension));
357   }
358 
359   UpdatePrefs();
360 }
361 
362 // Combine the currently enabled extensions that have browser actions (which
363 // we get from the ExtensionRegistry) with the ordering we get from the
364 // pref service. For robustness we use a somewhat inefficient process:
365 // 1. Create a vector of extensions sorted by their pref values. This vector may
366 // have holes.
367 // 2. Create a vector of extensions that did not have a pref value.
368 // 3. Remove holes from the sorted vector and append the unsorted vector.
InitializeExtensionList(const ExtensionSet & extensions)369 void ExtensionToolbarModel::InitializeExtensionList(
370     const ExtensionSet& extensions) {
371   last_known_positions_ = extension_prefs_->GetToolbarOrder();
372   Populate(last_known_positions_, extensions);
373 
374   extensions_initialized_ = true;
375   MaybeUpdateVisibilityPrefs();
376   FOR_EACH_OBSERVER(Observer, observers_, ToolbarVisibleCountChanged());
377 }
378 
Populate(const ExtensionIdList & positions,const ExtensionSet & extensions)379 void ExtensionToolbarModel::Populate(const ExtensionIdList& positions,
380                                      const ExtensionSet& extensions) {
381   // Items that have explicit positions.
382   ExtensionList sorted;
383   sorted.resize(positions.size(), NULL);
384   // The items that don't have explicit positions.
385   ExtensionList unsorted;
386 
387   ExtensionActionManager* extension_action_manager =
388       ExtensionActionManager::Get(profile_);
389 
390   // Create the lists.
391   int hidden = 0;
392   for (ExtensionSet::const_iterator it = extensions.begin();
393        it != extensions.end();
394        ++it) {
395     const Extension* extension = it->get();
396     if (!ShouldAddExtension(extension)) {
397       if (extension_action_manager->GetBrowserAction(*extension))
398         ++hidden;
399       continue;
400     }
401 
402     ExtensionIdList::const_iterator pos =
403         std::find(positions.begin(), positions.end(), extension->id());
404     if (pos != positions.end())
405       sorted[pos - positions.begin()] = extension;
406     else
407       unsorted.push_back(make_scoped_refptr(extension));
408   }
409 
410   size_t items_count = toolbar_items_.size();
411   for (size_t i = 0; i < items_count; i++) {
412     const Extension* extension = toolbar_items_.back().get();
413     // By popping the extension here (before calling BrowserActionRemoved),
414     // we will not shrink visible count by one after BrowserActionRemoved
415     // calls SetVisibleCount.
416     toolbar_items_.pop_back();
417     FOR_EACH_OBSERVER(
418         Observer, observers_, ToolbarExtensionRemoved(extension));
419   }
420   DCHECK(toolbar_items_.empty());
421 
422   // Merge the lists.
423   toolbar_items_.reserve(sorted.size() + unsorted.size());
424 
425   for (ExtensionList::const_iterator iter = sorted.begin();
426        iter != sorted.end(); ++iter) {
427     // It's possible for the extension order to contain items that aren't
428     // actually loaded on this machine.  For example, when extension sync is on,
429     // we sync the extension order as-is but double-check with the user before
430     // syncing NPAPI-containing extensions, so if one of those is not actually
431     // synced, we'll get a NULL in the list.  This sort of case can also happen
432     // if some error prevents an extension from loading.
433     if (iter->get() != NULL) {
434       toolbar_items_.push_back(*iter);
435       FOR_EACH_OBSERVER(
436           Observer,
437           observers_,
438           ToolbarExtensionAdded(iter->get(), toolbar_items_.size() - 1));
439     }
440   }
441   for (ExtensionList::const_iterator iter = unsorted.begin();
442        iter != unsorted.end(); ++iter) {
443     if (iter->get() != NULL) {
444       toolbar_items_.push_back(*iter);
445       FOR_EACH_OBSERVER(
446           Observer,
447           observers_,
448           ToolbarExtensionAdded(iter->get(), toolbar_items_.size() - 1));
449     }
450   }
451 
452   UMA_HISTOGRAM_COUNTS_100(
453       "ExtensionToolbarModel.BrowserActionsPermanentlyHidden", hidden);
454   UMA_HISTOGRAM_COUNTS_100("ExtensionToolbarModel.BrowserActionsCount",
455                            toolbar_items_.size());
456 
457   if (!toolbar_items_.empty()) {
458     // Visible count can be -1, meaning: 'show all'. Since UMA converts negative
459     // values to 0, this would be counted as 'show none' unless we convert it to
460     // max.
461     UMA_HISTOGRAM_COUNTS_100("ExtensionToolbarModel.BrowserActionsVisible",
462                              visible_icon_count_ == -1 ?
463                                  base::HistogramBase::kSampleType_MAX :
464                                  visible_icon_count_);
465   }
466 }
467 
UpdatePrefs()468 void ExtensionToolbarModel::UpdatePrefs() {
469   if (!extension_prefs_)
470     return;
471 
472   // Don't observe change caused by self.
473   pref_change_registrar_.Remove(pref_names::kToolbar);
474   extension_prefs_->SetToolbarOrder(last_known_positions_);
475   pref_change_registrar_.Add(pref_names::kToolbar, pref_change_callback_);
476 }
477 
MaybeUpdateVisibilityPref(const Extension * extension,int index)478 void ExtensionToolbarModel::MaybeUpdateVisibilityPref(
479     const Extension* extension, int index) {
480   // We only update the visibility pref for hidden/not hidden based on the
481   // overflow menu with the new toolbar design.
482   if (include_all_extensions_) {
483     bool visible = index < visible_icon_count_ || visible_icon_count_ == -1;
484     if (visible != ExtensionActionAPI::GetBrowserActionVisibility(
485                        extension_prefs_, extension->id())) {
486       // Don't observe changes caused by ourselves.
487       bool was_registered = false;
488       if (registrar_.IsRegistered(
489               this,
490               NOTIFICATION_EXTENSION_BROWSER_ACTION_VISIBILITY_CHANGED,
491               content::Source<ExtensionPrefs>(extension_prefs_))) {
492         was_registered = true;
493         registrar_.Remove(
494             this,
495             NOTIFICATION_EXTENSION_BROWSER_ACTION_VISIBILITY_CHANGED,
496             content::Source<ExtensionPrefs>(extension_prefs_));
497       }
498       ExtensionActionAPI::SetBrowserActionVisibility(
499           extension_prefs_, extension->id(), visible);
500       if (was_registered) {
501         registrar_.Add(this,
502                        NOTIFICATION_EXTENSION_BROWSER_ACTION_VISIBILITY_CHANGED,
503                        content::Source<ExtensionPrefs>(extension_prefs_));
504       }
505     }
506   }
507 }
508 
MaybeUpdateVisibilityPrefs()509 void ExtensionToolbarModel::MaybeUpdateVisibilityPrefs() {
510   for (size_t i = 0u; i < toolbar_items_.size(); ++i)
511     MaybeUpdateVisibilityPref(toolbar_items_[i].get(), i);
512 }
513 
IncognitoIndexToOriginal(int incognito_index)514 int ExtensionToolbarModel::IncognitoIndexToOriginal(int incognito_index) {
515   int original_index = 0, i = 0;
516   for (ExtensionList::iterator iter = toolbar_items_.begin();
517        iter != toolbar_items_.end();
518        ++iter, ++original_index) {
519     if (util::IsIncognitoEnabled((*iter)->id(), profile_)) {
520       if (incognito_index == i)
521         break;
522       ++i;
523     }
524   }
525   return original_index;
526 }
527 
OriginalIndexToIncognito(int original_index)528 int ExtensionToolbarModel::OriginalIndexToIncognito(int original_index) {
529   int incognito_index = 0, i = 0;
530   for (ExtensionList::iterator iter = toolbar_items_.begin();
531        iter != toolbar_items_.end();
532        ++iter, ++i) {
533     if (original_index == i)
534       break;
535     if (util::IsIncognitoEnabled((*iter)->id(), profile_))
536       ++incognito_index;
537   }
538   return incognito_index;
539 }
540 
OnExtensionToolbarPrefChange()541 void ExtensionToolbarModel::OnExtensionToolbarPrefChange() {
542   // If extensions are not ready, defer to later Populate() call.
543   if (!extensions_initialized_)
544     return;
545 
546   // Recalculate |last_known_positions_| to be |pref_positions| followed by
547   // ones that are only in |last_known_positions_|.
548   ExtensionIdList pref_positions = extension_prefs_->GetToolbarOrder();
549   size_t pref_position_size = pref_positions.size();
550   for (size_t i = 0; i < last_known_positions_.size(); ++i) {
551     if (std::find(pref_positions.begin(), pref_positions.end(),
552                   last_known_positions_[i]) == pref_positions.end()) {
553       pref_positions.push_back(last_known_positions_[i]);
554     }
555   }
556   last_known_positions_.swap(pref_positions);
557 
558   // Re-populate.
559   Populate(last_known_positions_,
560            ExtensionRegistry::Get(profile_)->enabled_extensions());
561 
562   if (last_known_positions_.size() > pref_position_size) {
563     // Need to update pref because we have extra icons. But can't call
564     // UpdatePrefs() directly within observation closure.
565     base::MessageLoop::current()->PostTask(
566         FROM_HERE,
567         base::Bind(&ExtensionToolbarModel::UpdatePrefs,
568                    weak_ptr_factory_.GetWeakPtr()));
569   }
570 }
571 
ShowExtensionActionPopup(const Extension * extension,Browser * browser,bool grant_active_tab)572 bool ExtensionToolbarModel::ShowExtensionActionPopup(
573     const Extension* extension,
574     Browser* browser,
575     bool grant_active_tab) {
576   ObserverListBase<Observer>::Iterator it(observers_);
577   Observer* obs = NULL;
578   // Look for the Observer associated with the browser.
579   // This would be cleaner if we had an abstract class for the Toolbar UI
580   // (like we do for LocationBar), but sadly, we don't.
581   while ((obs = it.GetNext()) != NULL) {
582     if (obs->GetBrowser() == browser)
583       return obs->ShowExtensionActionPopup(extension, grant_active_tab);
584   }
585   return false;
586 }
587 
EnsureVisibility(const ExtensionIdList & extension_ids)588 void ExtensionToolbarModel::EnsureVisibility(
589     const ExtensionIdList& extension_ids) {
590   if (visible_icon_count_ == -1)
591     return;  // Already showing all.
592 
593   // Otherwise, make sure we have enough room to show all the extensions
594   // requested.
595   if (visible_icon_count_ < static_cast<int>(extension_ids.size())) {
596     SetVisibleIconCount(extension_ids.size());
597 
598     // Inform observers.
599     FOR_EACH_OBSERVER(Observer, observers_, ToolbarVisibleCountChanged());
600   }
601 
602   if (visible_icon_count_ == -1)
603     return;  // May have been set to max by SetVisibleIconCount.
604 
605   // Guillotine's Delight: Move an orange noble to the front of the line.
606   for (ExtensionIdList::const_iterator it = extension_ids.begin();
607        it != extension_ids.end(); ++it) {
608     for (ExtensionList::const_iterator extension = toolbar_items_.begin();
609          extension != toolbar_items_.end(); ++extension) {
610       if ((*extension)->id() == (*it)) {
611         if (extension - toolbar_items_.begin() >= visible_icon_count_)
612           MoveExtensionIcon(extension->get(), 0);
613         break;
614       }
615     }
616   }
617 }
618 
HighlightExtensions(const ExtensionIdList & extension_ids)619 bool ExtensionToolbarModel::HighlightExtensions(
620     const ExtensionIdList& extension_ids) {
621   highlighted_items_.clear();
622 
623   for (ExtensionIdList::const_iterator id = extension_ids.begin();
624        id != extension_ids.end();
625        ++id) {
626     for (ExtensionList::const_iterator extension = toolbar_items_.begin();
627          extension != toolbar_items_.end();
628          ++extension) {
629       if (*id == (*extension)->id())
630         highlighted_items_.push_back(*extension);
631     }
632   }
633 
634   // If we have any items in |highlighted_items_|, then we entered highlighting
635   // mode.
636   if (highlighted_items_.size()) {
637     old_visible_icon_count_ = visible_icon_count_;
638     is_highlighting_ = true;
639     if (visible_icon_count_ != -1 &&
640         visible_icon_count_ < static_cast<int>(extension_ids.size())) {
641       SetVisibleIconCount(extension_ids.size());
642       FOR_EACH_OBSERVER(Observer, observers_, ToolbarVisibleCountChanged());
643     }
644 
645     FOR_EACH_OBSERVER(Observer, observers_, ToolbarHighlightModeChanged(true));
646     return true;
647   }
648 
649   // Otherwise, we didn't enter highlighting mode (and, in fact, exited it if
650   // we were otherwise in it).
651   if (is_highlighting_)
652     StopHighlighting();
653   return false;
654 }
655 
StopHighlighting()656 void ExtensionToolbarModel::StopHighlighting() {
657   if (is_highlighting_) {
658     highlighted_items_.clear();
659     is_highlighting_ = false;
660     if (old_visible_icon_count_ != visible_icon_count_) {
661       SetVisibleIconCount(old_visible_icon_count_);
662       FOR_EACH_OBSERVER(Observer, observers_, ToolbarVisibleCountChanged());
663     }
664     FOR_EACH_OBSERVER(Observer, observers_, ToolbarHighlightModeChanged(false));
665   }
666 }
667 
SetVisibleIconCountForTest(size_t visible_icons)668 void ExtensionToolbarModel::SetVisibleIconCountForTest(size_t visible_icons) {
669   SetVisibleIconCount(visible_icons);
670   FOR_EACH_OBSERVER(Observer, observers_, ToolbarVisibleCountChanged());
671 }
672 
673 }  // namespace extensions
674