Disable view source for Developer Tools.
[chromium-blink-merge.git] / chrome / browser / extensions / extension_toolbar_model.cc
blobb1bb1a07e8feae4abe429038a9866bd4e1d4532c
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 <string>
9 #include "base/prefs/pref_service.h"
10 #include "chrome/browser/chrome_notification_types.h"
11 #include "chrome/browser/extensions/api/extension_action/extension_action_api.h"
12 #include "chrome/browser/extensions/extension_action.h"
13 #include "chrome/browser/extensions/extension_action_manager.h"
14 #include "chrome/browser/extensions/extension_prefs.h"
15 #include "chrome/browser/extensions/extension_service.h"
16 #include "chrome/browser/extensions/extension_tab_util.h"
17 #include "chrome/browser/extensions/extension_toolbar_model_factory.h"
18 #include "chrome/browser/extensions/extension_util.h"
19 #include "chrome/browser/extensions/tab_helper.h"
20 #include "chrome/browser/profiles/profile.h"
21 #include "chrome/browser/ui/browser.h"
22 #include "chrome/browser/ui/tabs/tab_strip_model.h"
23 #include "chrome/common/pref_names.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/pref_names.h"
28 #include "extensions/common/extension.h"
29 #include "extensions/common/extension_set.h"
30 #include "extensions/common/feature_switch.h"
32 using extensions::Extension;
33 using extensions::ExtensionIdList;
34 using extensions::ExtensionList;
36 bool ExtensionToolbarModel::Observer::BrowserActionShowPopup(
37 const extensions::Extension* extension) {
38 return false;
41 ExtensionToolbarModel::ExtensionToolbarModel(
42 Profile* profile,
43 extensions::ExtensionPrefs* extension_prefs)
44 : profile_(profile),
45 extension_prefs_(extension_prefs),
46 prefs_(profile_->GetPrefs()),
47 extensions_initialized_(false),
48 weak_ptr_factory_(this) {
49 registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_LOADED,
50 content::Source<Profile>(profile_));
51 registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_UNLOADED,
52 content::Source<Profile>(profile_));
53 registrar_.Add(this, chrome::NOTIFICATION_EXTENSIONS_READY,
54 content::Source<Profile>(profile_));
55 registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_UNINSTALLED,
56 content::Source<Profile>(profile_));
57 registrar_.Add(
58 this, chrome::NOTIFICATION_EXTENSION_BROWSER_ACTION_VISIBILITY_CHANGED,
59 content::Source<extensions::ExtensionPrefs>(extension_prefs_));
61 visible_icon_count_ = prefs_->GetInteger(
62 extensions::pref_names::kToolbarSize);
64 pref_change_registrar_.Init(prefs_);
65 pref_change_callback_ =
66 base::Bind(&ExtensionToolbarModel::OnExtensionToolbarPrefChange,
67 base::Unretained(this));
68 pref_change_registrar_.Add(extensions::pref_names::kToolbar,
69 pref_change_callback_);
72 ExtensionToolbarModel::~ExtensionToolbarModel() {
75 // static
76 ExtensionToolbarModel* ExtensionToolbarModel::Get(Profile* profile) {
77 return ExtensionToolbarModelFactory::GetForProfile(profile);
80 void ExtensionToolbarModel::AddObserver(Observer* observer) {
81 observers_.AddObserver(observer);
84 void ExtensionToolbarModel::RemoveObserver(Observer* observer) {
85 observers_.RemoveObserver(observer);
88 void ExtensionToolbarModel::MoveBrowserAction(const Extension* extension,
89 int index) {
90 ExtensionList::iterator pos = std::find(toolbar_items_.begin(),
91 toolbar_items_.end(), extension);
92 if (pos == toolbar_items_.end()) {
93 NOTREACHED();
94 return;
96 toolbar_items_.erase(pos);
98 ExtensionIdList::iterator pos_id;
99 pos_id = std::find(last_known_positions_.begin(),
100 last_known_positions_.end(), extension->id());
101 if (pos_id != last_known_positions_.end())
102 last_known_positions_.erase(pos_id);
104 int i = 0;
105 bool inserted = false;
106 for (ExtensionList::iterator iter = toolbar_items_.begin();
107 iter != toolbar_items_.end();
108 ++iter, ++i) {
109 if (i == index) {
110 pos_id = std::find(last_known_positions_.begin(),
111 last_known_positions_.end(), (*iter)->id());
112 last_known_positions_.insert(pos_id, extension->id());
114 toolbar_items_.insert(iter, make_scoped_refptr(extension));
115 inserted = true;
116 break;
120 if (!inserted) {
121 DCHECK_EQ(index, static_cast<int>(toolbar_items_.size()));
122 index = toolbar_items_.size();
124 toolbar_items_.push_back(make_scoped_refptr(extension));
125 last_known_positions_.push_back(extension->id());
128 FOR_EACH_OBSERVER(Observer, observers_, BrowserActionMoved(extension, index));
130 UpdatePrefs();
133 ExtensionToolbarModel::Action ExtensionToolbarModel::ExecuteBrowserAction(
134 const Extension* extension,
135 Browser* browser,
136 GURL* popup_url_out,
137 bool should_grant) {
138 content::WebContents* web_contents = NULL;
139 int tab_id = 0;
140 if (!extensions::ExtensionTabUtil::GetDefaultTab(
141 browser, &web_contents, &tab_id)) {
142 return ACTION_NONE;
145 ExtensionAction* browser_action =
146 extensions::ExtensionActionManager::Get(profile_)->
147 GetBrowserAction(*extension);
149 // For browser actions, visibility == enabledness.
150 if (!browser_action->GetIsVisible(tab_id))
151 return ACTION_NONE;
153 if (should_grant) {
154 extensions::TabHelper::FromWebContents(web_contents)->
155 active_tab_permission_granter()->GrantIfRequested(extension);
158 if (browser_action->HasPopup(tab_id)) {
159 if (popup_url_out)
160 *popup_url_out = browser_action->GetPopupUrl(tab_id);
161 return ACTION_SHOW_POPUP;
164 extensions::ExtensionActionAPI::BrowserActionExecuted(
165 browser->profile(), *browser_action, web_contents);
166 return ACTION_NONE;
169 void ExtensionToolbarModel::SetVisibleIconCount(int count) {
170 visible_icon_count_ =
171 count == static_cast<int>(toolbar_items_.size()) ? -1 : count;
172 prefs_->SetInteger(extensions::pref_names::kToolbarSize, visible_icon_count_);
175 void ExtensionToolbarModel::Observe(
176 int type,
177 const content::NotificationSource& source,
178 const content::NotificationDetails& details) {
179 ExtensionService* extension_service =
180 extensions::ExtensionSystem::Get(profile_)->extension_service();
181 if (!extension_service || !extension_service->is_ready())
182 return;
184 if (type == chrome::NOTIFICATION_EXTENSIONS_READY) {
185 InitializeExtensionList(extension_service);
186 return;
189 const Extension* extension = NULL;
190 if (type == chrome::NOTIFICATION_EXTENSION_UNLOADED) {
191 extension = content::Details<extensions::UnloadedExtensionInfo>(
192 details)->extension;
193 } else if (type ==
194 chrome::NOTIFICATION_EXTENSION_BROWSER_ACTION_VISIBILITY_CHANGED) {
195 extension = extension_service->GetExtensionById(
196 *content::Details<const std::string>(details).ptr(), true);
197 } else {
198 extension = content::Details<const Extension>(details).ptr();
200 if (type == chrome::NOTIFICATION_EXTENSION_LOADED) {
201 // We don't want to add the same extension twice. It may have already been
202 // added by EXTENSION_BROWSER_ACTION_VISIBILITY_CHANGED below, if the user
203 // hides the browser action and then disables and enables the extension.
204 for (size_t i = 0; i < toolbar_items_.size(); i++) {
205 if (toolbar_items_[i].get() == extension)
206 return; // Already exists.
208 if (extensions::ExtensionActionAPI::GetBrowserActionVisibility(
209 extension_prefs_, extension->id())) {
210 AddExtension(extension);
212 } else if (type == chrome::NOTIFICATION_EXTENSION_UNLOADED) {
213 RemoveExtension(extension);
214 } else if (type == chrome::NOTIFICATION_EXTENSION_UNINSTALLED) {
215 UninstalledExtension(extension);
216 } else if (type ==
217 chrome::NOTIFICATION_EXTENSION_BROWSER_ACTION_VISIBILITY_CHANGED) {
218 if (extensions::ExtensionActionAPI::GetBrowserActionVisibility(
219 extension_prefs_, extension->id())) {
220 AddExtension(extension);
221 } else {
222 RemoveExtension(extension);
224 } else {
225 NOTREACHED() << "Received unexpected notification";
229 size_t ExtensionToolbarModel::FindNewPositionFromLastKnownGood(
230 const Extension* extension) {
231 // See if we have last known good position for this extension.
232 size_t new_index = 0;
233 // Loop through the ID list of known positions, to count the number of visible
234 // browser action icons preceding |extension|.
235 for (ExtensionIdList::const_iterator iter_id = last_known_positions_.begin();
236 iter_id < last_known_positions_.end(); ++iter_id) {
237 if ((*iter_id) == extension->id())
238 return new_index; // We've found the right position.
239 // Found an id, need to see if it is visible.
240 for (ExtensionList::const_iterator iter_ext = toolbar_items_.begin();
241 iter_ext < toolbar_items_.end(); ++iter_ext) {
242 if ((*iter_ext)->id().compare(*iter_id) == 0) {
243 // This extension is visible, update the index value.
244 ++new_index;
245 break;
250 return -1;
253 void ExtensionToolbarModel::AddExtension(const Extension* extension) {
254 // We only care about extensions with browser actions.
255 if (!extensions::ExtensionActionManager::Get(profile_)->
256 GetBrowserAction(*extension)) {
257 return;
260 size_t new_index = -1;
262 // See if we have a last known good position for this extension.
263 ExtensionIdList::iterator last_pos = std::find(last_known_positions_.begin(),
264 last_known_positions_.end(),
265 extension->id());
266 if (last_pos != last_known_positions_.end()) {
267 new_index = FindNewPositionFromLastKnownGood(extension);
268 if (new_index != toolbar_items_.size()) {
269 toolbar_items_.insert(toolbar_items_.begin() + new_index,
270 make_scoped_refptr(extension));
271 } else {
272 toolbar_items_.push_back(make_scoped_refptr(extension));
274 } else {
275 // This is a never before seen extension, that was added to the end. Make
276 // sure to reflect that.
277 toolbar_items_.push_back(make_scoped_refptr(extension));
278 last_known_positions_.push_back(extension->id());
279 new_index = toolbar_items_.size() - 1;
280 UpdatePrefs();
283 FOR_EACH_OBSERVER(Observer, observers_,
284 BrowserActionAdded(extension, new_index));
287 void ExtensionToolbarModel::RemoveExtension(const Extension* extension) {
288 ExtensionList::iterator pos =
289 std::find(toolbar_items_.begin(), toolbar_items_.end(), extension);
290 if (pos == toolbar_items_.end())
291 return;
293 toolbar_items_.erase(pos);
294 FOR_EACH_OBSERVER(Observer, observers_,
295 BrowserActionRemoved(extension));
297 UpdatePrefs();
300 void ExtensionToolbarModel::UninstalledExtension(const Extension* extension) {
301 // Remove the extension id from the ordered list, if it exists (the extension
302 // might not be represented in the list because it might not have an icon).
303 ExtensionIdList::iterator pos =
304 std::find(last_known_positions_.begin(),
305 last_known_positions_.end(), extension->id());
307 if (pos != last_known_positions_.end()) {
308 last_known_positions_.erase(pos);
309 UpdatePrefs();
313 // Combine the currently enabled extensions that have browser actions (which
314 // we get from the ExtensionService) with the ordering we get from the
315 // pref service. For robustness we use a somewhat inefficient process:
316 // 1. Create a vector of extensions sorted by their pref values. This vector may
317 // have holes.
318 // 2. Create a vector of extensions that did not have a pref value.
319 // 3. Remove holes from the sorted vector and append the unsorted vector.
320 void ExtensionToolbarModel::InitializeExtensionList(ExtensionService* service) {
321 DCHECK(service->is_ready());
323 last_known_positions_ = extension_prefs_->GetToolbarOrder();
324 Populate(last_known_positions_, service);
326 extensions_initialized_ = true;
327 FOR_EACH_OBSERVER(Observer, observers_, VisibleCountChanged());
330 void ExtensionToolbarModel::Populate(
331 const extensions::ExtensionIdList& positions,
332 ExtensionService* service) {
333 // Items that have explicit positions.
334 ExtensionList sorted;
335 sorted.resize(positions.size(), NULL);
336 // The items that don't have explicit positions.
337 ExtensionList unsorted;
339 extensions::ExtensionActionManager* extension_action_manager =
340 extensions::ExtensionActionManager::Get(profile_);
342 // Create the lists.
343 for (extensions::ExtensionSet::const_iterator it =
344 service->extensions()->begin();
345 it != service->extensions()->end(); ++it) {
346 const Extension* extension = it->get();
347 if (!extension_action_manager->GetBrowserAction(*extension))
348 continue;
349 if (!extensions::ExtensionActionAPI::GetBrowserActionVisibility(
350 extension_prefs_, extension->id())) {
351 continue;
354 extensions::ExtensionIdList::const_iterator pos =
355 std::find(positions.begin(), positions.end(), extension->id());
356 if (pos != positions.end())
357 sorted[pos - positions.begin()] = extension;
358 else
359 unsorted.push_back(make_scoped_refptr(extension));
362 // Erase current icons.
363 for (size_t i = 0; i < toolbar_items_.size(); i++) {
364 FOR_EACH_OBSERVER(
365 Observer, observers_, BrowserActionRemoved(toolbar_items_[i].get()));
367 toolbar_items_.clear();
369 // Merge the lists.
370 toolbar_items_.reserve(sorted.size() + unsorted.size());
371 for (ExtensionList::const_iterator iter = sorted.begin();
372 iter != sorted.end(); ++iter) {
373 // It's possible for the extension order to contain items that aren't
374 // actually loaded on this machine. For example, when extension sync is on,
375 // we sync the extension order as-is but double-check with the user before
376 // syncing NPAPI-containing extensions, so if one of those is not actually
377 // synced, we'll get a NULL in the list. This sort of case can also happen
378 // if some error prevents an extension from loading.
379 if (iter->get() != NULL)
380 toolbar_items_.push_back(*iter);
382 toolbar_items_.insert(toolbar_items_.end(), unsorted.begin(),
383 unsorted.end());
385 // Inform observers.
386 for (size_t i = 0; i < toolbar_items_.size(); i++) {
387 FOR_EACH_OBSERVER(
388 Observer, observers_, BrowserActionAdded(toolbar_items_[i].get(), i));
392 void ExtensionToolbarModel::UpdatePrefs() {
393 if (!extension_prefs_)
394 return;
396 // Don't observe change caused by self.
397 pref_change_registrar_.Remove(extensions::pref_names::kToolbar);
398 extension_prefs_->SetToolbarOrder(last_known_positions_);
399 pref_change_registrar_.Add(extensions::pref_names::kToolbar,
400 pref_change_callback_);
403 int ExtensionToolbarModel::IncognitoIndexToOriginal(int incognito_index) {
404 int original_index = 0, i = 0;
405 ExtensionService* extension_service =
406 extensions::ExtensionSystem::Get(profile_)->extension_service();
407 for (ExtensionList::iterator iter = toolbar_items_.begin();
408 iter != toolbar_items_.end();
409 ++iter, ++original_index) {
410 if (extension_util::IsIncognitoEnabled((*iter)->id(), extension_service)) {
411 if (incognito_index == i)
412 break;
413 ++i;
416 return original_index;
419 int ExtensionToolbarModel::OriginalIndexToIncognito(int original_index) {
420 int incognito_index = 0, i = 0;
421 ExtensionService* extension_service =
422 extensions::ExtensionSystem::Get(profile_)->extension_service();
423 for (ExtensionList::iterator iter = toolbar_items_.begin();
424 iter != toolbar_items_.end();
425 ++iter, ++i) {
426 if (original_index == i)
427 break;
428 if (extension_util::IsIncognitoEnabled((*iter)->id(), extension_service))
429 ++incognito_index;
431 return incognito_index;
434 void ExtensionToolbarModel::OnExtensionToolbarPrefChange() {
435 // If extensions are not ready, defer to later Populate() call.
436 if (!extensions_initialized_)
437 return;
439 // Recalculate |last_known_positions_| to be |pref_positions| followed by
440 // ones that are only in |last_known_positions_|.
441 extensions::ExtensionIdList pref_positions =
442 extension_prefs_->GetToolbarOrder();
443 size_t pref_position_size = pref_positions.size();
444 for (size_t i = 0; i < last_known_positions_.size(); ++i) {
445 if (std::find(pref_positions.begin(), pref_positions.end(),
446 last_known_positions_[i]) == pref_positions.end()) {
447 pref_positions.push_back(last_known_positions_[i]);
450 last_known_positions_.swap(pref_positions);
452 // Re-populate.
453 Populate(last_known_positions_,
454 extensions::ExtensionSystem::Get(profile_)->extension_service());
456 if (last_known_positions_.size() > pref_position_size) {
457 // Need to update pref because we have extra icons. But can't call
458 // UpdatePrefs() directly within observation closure.
459 base::MessageLoop::current()->PostTask(
460 FROM_HERE,
461 base::Bind(&ExtensionToolbarModel::UpdatePrefs,
462 weak_ptr_factory_.GetWeakPtr()));
466 bool ExtensionToolbarModel::ShowBrowserActionPopup(
467 const extensions::Extension* extension) {
468 ObserverListBase<Observer>::Iterator it(observers_);
469 Observer* obs = NULL;
470 while ((obs = it.GetNext()) != NULL) {
471 // Stop after first popup since it should only show in the active window.
472 if (obs->BrowserActionShowPopup(extension))
473 return true;
475 return false;
478 void ExtensionToolbarModel::EnsureVisibility(
479 const extensions::ExtensionIdList& extension_ids) {
480 if (visible_icon_count_ == -1)
481 return; // Already showing all.
483 // Otherwise, make sure we have enough room to show all the extensions
484 // requested.
485 if (visible_icon_count_ < static_cast<int>(extension_ids.size())) {
486 SetVisibleIconCount(extension_ids.size());
488 // Inform observers.
489 FOR_EACH_OBSERVER(Observer, observers_, VisibleCountChanged());
492 if (visible_icon_count_ == -1)
493 return; // May have been set to max by SetVisibleIconCount.
495 // Guillotine's Delight: Move an orange noble to the front of the line.
496 for (ExtensionIdList::const_iterator it = extension_ids.begin();
497 it != extension_ids.end(); ++it) {
498 for (ExtensionList::const_iterator extension = toolbar_items_.begin();
499 extension != toolbar_items_.end(); ++extension) {
500 if ((*extension)->id() == (*it)) {
501 if (extension - toolbar_items_.begin() >= visible_icon_count_)
502 MoveBrowserAction(*extension, 0);
503 break;