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