Home | History | Annotate | Download | only in extensions
      1 // Copyright (c) 2011 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 "chrome/browser/extensions/extension_prefs.h"
      8 #include "chrome/browser/extensions/extension_service.h"
      9 #include "chrome/browser/prefs/pref_service.h"
     10 #include "chrome/browser/profiles/profile.h"
     11 #include "chrome/common/extensions/extension.h"
     12 #include "chrome/common/pref_names.h"
     13 #include "content/common/notification_service.h"
     14 
     15 ExtensionToolbarModel::ExtensionToolbarModel(ExtensionService* service)
     16     : service_(service),
     17       prefs_(service->profile()->GetPrefs()),
     18       extensions_initialized_(false) {
     19   DCHECK(service_);
     20 
     21   registrar_.Add(this, NotificationType::EXTENSION_LOADED,
     22                  Source<Profile>(service_->profile()));
     23   registrar_.Add(this, NotificationType::EXTENSION_UNLOADED,
     24                  Source<Profile>(service_->profile()));
     25   registrar_.Add(this, NotificationType::EXTENSIONS_READY,
     26                  Source<Profile>(service_->profile()));
     27   registrar_.Add(this,
     28                  NotificationType::EXTENSION_BROWSER_ACTION_VISIBILITY_CHANGED,
     29                  NotificationService::AllSources());
     30 
     31   visible_icon_count_ = prefs_->GetInteger(prefs::kExtensionToolbarSize);
     32 }
     33 
     34 ExtensionToolbarModel::~ExtensionToolbarModel() {
     35 }
     36 
     37 void ExtensionToolbarModel::DestroyingProfile() {
     38   registrar_.RemoveAll();
     39 }
     40 
     41 void ExtensionToolbarModel::AddObserver(Observer* observer) {
     42   observers_.AddObserver(observer);
     43 }
     44 
     45 void ExtensionToolbarModel::RemoveObserver(Observer* observer) {
     46   observers_.RemoveObserver(observer);
     47 }
     48 
     49 void ExtensionToolbarModel::MoveBrowserAction(const Extension* extension,
     50                                               int index) {
     51   ExtensionList::iterator pos = std::find(begin(), end(), extension);
     52   if (pos == end()) {
     53     NOTREACHED();
     54     return;
     55   }
     56   toolitems_.erase(pos);
     57 
     58   int i = 0;
     59   bool inserted = false;
     60   for (ExtensionList::iterator iter = begin(); iter != end(); ++iter, ++i) {
     61     if (i == index) {
     62       toolitems_.insert(iter, make_scoped_refptr(extension));
     63       inserted = true;
     64       break;
     65     }
     66   }
     67 
     68   if (!inserted) {
     69     DCHECK_EQ(index, static_cast<int>(toolitems_.size()));
     70     index = toolitems_.size();
     71 
     72     toolitems_.push_back(make_scoped_refptr(extension));
     73   }
     74 
     75   FOR_EACH_OBSERVER(Observer, observers_, BrowserActionMoved(extension, index));
     76 
     77   UpdatePrefs();
     78 }
     79 
     80 void ExtensionToolbarModel::SetVisibleIconCount(int count) {
     81   visible_icon_count_ = count == static_cast<int>(size()) ? -1 : count;
     82   prefs_->SetInteger(prefs::kExtensionToolbarSize, visible_icon_count_);
     83   prefs_->ScheduleSavePersistentPrefs();
     84 }
     85 
     86 void ExtensionToolbarModel::Observe(NotificationType type,
     87                                     const NotificationSource& source,
     88                                     const NotificationDetails& details) {
     89   if (type == NotificationType::EXTENSIONS_READY) {
     90     InitializeExtensionList();
     91     return;
     92   }
     93 
     94   if (!service_->is_ready())
     95     return;
     96 
     97   const Extension* extension = NULL;
     98   if (type == NotificationType::EXTENSION_UNLOADED) {
     99     extension = Details<UnloadedExtensionInfo>(details)->extension;
    100   } else {
    101     extension = Details<const Extension>(details).ptr();
    102   }
    103   if (type == NotificationType::EXTENSION_LOADED) {
    104     // We don't want to add the same extension twice. It may have already been
    105     // added by EXTENSION_BROWSER_ACTION_VISIBILITY_CHANGED below, if the user
    106     // hides the browser action and then disables and enables the extension.
    107     for (size_t i = 0; i < toolitems_.size(); i++) {
    108       if (toolitems_[i].get() == extension)
    109         return;  // Already exists.
    110     }
    111     if (service_->GetBrowserActionVisibility(extension))
    112       AddExtension(extension);
    113   } else if (type == NotificationType::EXTENSION_UNLOADED) {
    114     RemoveExtension(extension);
    115   } else if (type ==
    116              NotificationType::EXTENSION_BROWSER_ACTION_VISIBILITY_CHANGED) {
    117     if (service_->GetBrowserActionVisibility(extension))
    118       AddExtension(extension);
    119     else
    120       RemoveExtension(extension);
    121   } else {
    122     NOTREACHED() << "Received unexpected notification";
    123   }
    124 }
    125 
    126 void ExtensionToolbarModel::AddExtension(const Extension* extension) {
    127   // We only care about extensions with browser actions.
    128   if (!extension->browser_action())
    129     return;
    130 
    131   if (extension->id() == last_extension_removed_ &&
    132       last_extension_removed_index_ < toolitems_.size()) {
    133     toolitems_.insert(begin() + last_extension_removed_index_,
    134                       make_scoped_refptr(extension));
    135     FOR_EACH_OBSERVER(Observer, observers_,
    136         BrowserActionAdded(extension, last_extension_removed_index_));
    137   } else {
    138     toolitems_.push_back(make_scoped_refptr(extension));
    139     FOR_EACH_OBSERVER(Observer, observers_,
    140                       BrowserActionAdded(extension, toolitems_.size() - 1));
    141   }
    142 
    143   last_extension_removed_ = "";
    144   last_extension_removed_index_ = -1;
    145 
    146   UpdatePrefs();
    147 }
    148 
    149 void ExtensionToolbarModel::RemoveExtension(const Extension* extension) {
    150   ExtensionList::iterator pos = std::find(begin(), end(), extension);
    151   if (pos == end()) {
    152     return;
    153   }
    154 
    155   last_extension_removed_ = extension->id();
    156   last_extension_removed_index_ = pos - begin();
    157 
    158   toolitems_.erase(pos);
    159   FOR_EACH_OBSERVER(Observer, observers_,
    160                     BrowserActionRemoved(extension));
    161 
    162   UpdatePrefs();
    163 }
    164 
    165 // Combine the currently enabled extensions that have browser actions (which
    166 // we get from the ExtensionService) with the ordering we get from the
    167 // pref service. For robustness we use a somewhat inefficient process:
    168 // 1. Create a vector of extensions sorted by their pref values. This vector may
    169 // have holes.
    170 // 2. Create a vector of extensions that did not have a pref value.
    171 // 3. Remove holes from the sorted vector and append the unsorted vector.
    172 void ExtensionToolbarModel::InitializeExtensionList() {
    173   DCHECK(service_->is_ready());
    174 
    175   std::vector<std::string> pref_order = service_->extension_prefs()->
    176       GetToolbarOrder();
    177   // Items that have a pref for their position.
    178   ExtensionList sorted;
    179   sorted.resize(pref_order.size(), NULL);
    180   // The items that don't have a pref for their position.
    181   ExtensionList unsorted;
    182 
    183   // Create the lists.
    184   for (size_t i = 0; i < service_->extensions()->size(); ++i) {
    185     const Extension* extension = service_->extensions()->at(i);
    186     if (!extension->browser_action())
    187       continue;
    188     if (!service_->GetBrowserActionVisibility(extension))
    189       continue;
    190 
    191     std::vector<std::string>::iterator pos =
    192         std::find(pref_order.begin(), pref_order.end(), extension->id());
    193     if (pos != pref_order.end()) {
    194       int index = std::distance(pref_order.begin(), pos);
    195       sorted[index] = extension;
    196     } else {
    197       unsorted.push_back(make_scoped_refptr(extension));
    198     }
    199   }
    200 
    201   // Merge the lists.
    202   toolitems_.reserve(sorted.size() + unsorted.size());
    203   for (ExtensionList::iterator iter = sorted.begin();
    204        iter != sorted.end(); ++iter) {
    205     if (*iter != NULL)
    206       toolitems_.push_back(*iter);
    207   }
    208   toolitems_.insert(toolitems_.end(), unsorted.begin(), unsorted.end());
    209 
    210   // Inform observers.
    211   for (size_t i = 0; i < toolitems_.size(); i++) {
    212     FOR_EACH_OBSERVER(Observer, observers_,
    213                       BrowserActionAdded(toolitems_[i], i));
    214   }
    215 
    216   UpdatePrefs();
    217 
    218   extensions_initialized_ = true;
    219   FOR_EACH_OBSERVER(Observer, observers_, ModelLoaded());
    220 }
    221 
    222 void ExtensionToolbarModel::UpdatePrefs() {
    223   if (!service_->extension_prefs())
    224     return;
    225 
    226   std::vector<std::string> ids;
    227   ids.reserve(toolitems_.size());
    228   for (ExtensionList::iterator iter = begin(); iter != end(); ++iter)
    229     ids.push_back((*iter)->id());
    230   service_->extension_prefs()->SetToolbarOrder(ids);
    231 }
    232 
    233 const Extension* ExtensionToolbarModel::GetExtensionByIndex(int index) const {
    234   return toolitems_[index];
    235 }
    236 
    237 int ExtensionToolbarModel::IncognitoIndexToOriginal(int incognito_index) {
    238   int original_index = 0, i = 0;
    239   for (ExtensionList::iterator iter = begin(); iter != end();
    240        ++iter, ++original_index) {
    241     if (service_->IsIncognitoEnabled((*iter)->id())) {
    242       if (incognito_index == i)
    243         break;
    244       ++i;
    245     }
    246   }
    247   return original_index;
    248 }
    249 
    250 int ExtensionToolbarModel::OriginalIndexToIncognito(int original_index) {
    251   int incognito_index = 0, i = 0;
    252   for (ExtensionList::iterator iter = begin(); iter != end();
    253        ++iter, ++i) {
    254     if (original_index == i)
    255       break;
    256     if (service_->IsIncognitoEnabled((*iter)->id()))
    257       ++incognito_index;
    258   }
    259   return incognito_index;
    260 }
    261