Home | History | Annotate | Download | only in browser
      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/background_application_list_model.h"
      6 
      7 #include <algorithm>
      8 #include <set>
      9 
     10 #include "base/stl_util-inl.h"
     11 #include "base/utf_string_conversions.h"
     12 #include "chrome/app/chrome_command_ids.h"
     13 #include "chrome/browser/background_mode_manager.h"
     14 #include "chrome/browser/browser_process.h"
     15 #include "chrome/browser/extensions/extension_prefs.h"
     16 #include "chrome/browser/extensions/extension_service.h"
     17 #include "chrome/browser/extensions/image_loading_tracker.h"
     18 #include "chrome/browser/profiles/profile.h"
     19 #include "chrome/common/extensions/extension.h"
     20 #include "chrome/common/extensions/extension_resource.h"
     21 #include "content/common/notification_details.h"
     22 #include "content/common/notification_source.h"
     23 #include "ui/base/l10n/l10n_util_collator.h"
     24 
     25 class ExtensionNameComparator {
     26  public:
     27   explicit ExtensionNameComparator(icu::Collator* collator);
     28   bool operator()(const Extension* x, const Extension* y);
     29 
     30  private:
     31   icu::Collator* collator_;
     32 };
     33 
     34 ExtensionNameComparator::ExtensionNameComparator(icu::Collator* collator)
     35   : collator_(collator) {
     36 }
     37 
     38 bool ExtensionNameComparator::operator()(const Extension* x,
     39                                          const Extension* y) {
     40   return l10n_util::StringComparator<string16>(collator_)(
     41     UTF8ToUTF16(x->name()),
     42     UTF8ToUTF16(y->name()));
     43 }
     44 
     45 // Background application representation, private to the
     46 // BackgroundApplicationListModel class.
     47 class BackgroundApplicationListModel::Application
     48   : public ImageLoadingTracker::Observer {
     49  public:
     50   Application(BackgroundApplicationListModel* model,
     51               const Extension* an_extension);
     52 
     53   virtual ~Application();
     54 
     55   // Invoked when a request icon is available.
     56   virtual void OnImageLoaded(SkBitmap* image,
     57                              const ExtensionResource& resource,
     58                              int index);
     59 
     60   // Uses the FILE thread to request this extension's icon, sized
     61   // appropriately.
     62   void RequestIcon(Extension::Icons size);
     63 
     64   const Extension* extension_;
     65   scoped_ptr<SkBitmap> icon_;
     66   BackgroundApplicationListModel* model_;
     67   ImageLoadingTracker tracker_;
     68 };
     69 
     70 namespace {
     71 void GetServiceApplications(ExtensionService* service,
     72                             ExtensionList* applications_result) {
     73   const ExtensionList* extensions = service->extensions();
     74 
     75   for (ExtensionList::const_iterator cursor = extensions->begin();
     76        cursor != extensions->end();
     77        ++cursor) {
     78     const Extension* extension = *cursor;
     79     if (BackgroundApplicationListModel::IsBackgroundApp(*extension))
     80       applications_result->push_back(extension);
     81   }
     82   std::string locale = g_browser_process->GetApplicationLocale();
     83   icu::Locale loc(locale.c_str());
     84   UErrorCode error = U_ZERO_ERROR;
     85   scoped_ptr<icu::Collator> collator(icu::Collator::createInstance(loc, error));
     86   sort(applications_result->begin(), applications_result->end(),
     87        ExtensionNameComparator(collator.get()));
     88 }
     89 
     90 bool HasBackgroundAppPermission(
     91     const std::set<std::string>& api_permissions) {
     92   return Extension::HasApiPermission(
     93       api_permissions, Extension::kBackgroundPermission);
     94 }
     95 }  // namespace
     96 
     97 void
     98 BackgroundApplicationListModel::Observer::OnApplicationDataChanged(
     99     const Extension* extension) {
    100 }
    101 
    102 void
    103 BackgroundApplicationListModel::Observer::OnApplicationListChanged() {
    104 }
    105 
    106 BackgroundApplicationListModel::Observer::~Observer() {
    107 }
    108 
    109 BackgroundApplicationListModel::Application::~Application() {
    110 }
    111 
    112 BackgroundApplicationListModel::Application::Application(
    113     BackgroundApplicationListModel* model,
    114     const Extension* extension)
    115     : extension_(extension),
    116       icon_(NULL),
    117       model_(model),
    118       ALLOW_THIS_IN_INITIALIZER_LIST(tracker_(this)) {
    119 }
    120 
    121 void BackgroundApplicationListModel::Application::OnImageLoaded(
    122     SkBitmap* image,
    123     const ExtensionResource& resource,
    124     int index) {
    125   if (!image)
    126     return;
    127   icon_.reset(new SkBitmap(*image));
    128   model_->OnApplicationDataChanged(extension_);
    129 }
    130 
    131 void BackgroundApplicationListModel::Application::RequestIcon(
    132     Extension::Icons size) {
    133   ExtensionResource resource = extension_->GetIconResource(
    134       size, ExtensionIconSet::MATCH_BIGGER);
    135   tracker_.LoadImage(extension_, resource, gfx::Size(size, size),
    136                      ImageLoadingTracker::CACHE);
    137 }
    138 
    139 BackgroundApplicationListModel::~BackgroundApplicationListModel() {
    140   STLDeleteContainerPairSecondPointers(applications_.begin(),
    141                                        applications_.end());
    142 }
    143 
    144 BackgroundApplicationListModel::BackgroundApplicationListModel(Profile* profile)
    145     : profile_(profile) {
    146   DCHECK(profile_);
    147   registrar_.Add(this,
    148                  NotificationType::EXTENSION_LOADED,
    149                  Source<Profile>(profile));
    150   registrar_.Add(this,
    151                  NotificationType::EXTENSION_UNLOADED,
    152                  Source<Profile>(profile));
    153   registrar_.Add(this,
    154                  NotificationType::EXTENSIONS_READY,
    155                  Source<Profile>(profile));
    156   ExtensionService* service = profile->GetExtensionService();
    157   if (service && service->is_ready())
    158     Update();
    159 }
    160 
    161 void BackgroundApplicationListModel::AddObserver(Observer* observer) {
    162   observers_.AddObserver(observer);
    163 }
    164 
    165 void BackgroundApplicationListModel::AssociateApplicationData(
    166     const Extension* extension) {
    167   DCHECK(IsBackgroundApp(*extension));
    168   Application* application = FindApplication(extension);
    169   if (!application) {
    170     // App position is used as a dynamic command and so must be less than any
    171     // predefined command id.
    172     if (applications_.size() >= IDC_MinimumLabelValue) {
    173       LOG(ERROR) << "Background application limit of " << IDC_MinimumLabelValue
    174                  << " exceeded.  Ignoring.";
    175       return;
    176     }
    177     application = new Application(this, extension);
    178     applications_[extension->id()] = application;
    179     application->RequestIcon(Extension::EXTENSION_ICON_BITTY);
    180   }
    181 }
    182 
    183 void BackgroundApplicationListModel::DissociateApplicationData(
    184     const Extension* extension) {
    185   ApplicationMap::iterator found = applications_.find(extension->id());
    186   if (found != applications_.end()) {
    187     delete found->second;
    188     applications_.erase(found);
    189   }
    190 }
    191 
    192 const Extension* BackgroundApplicationListModel::GetExtension(
    193     int position) const {
    194   DCHECK(position >= 0 && static_cast<size_t>(position) < extensions_.size());
    195   return extensions_[position];
    196 }
    197 
    198 const BackgroundApplicationListModel::Application*
    199 BackgroundApplicationListModel::FindApplication(
    200     const Extension* extension) const {
    201   const std::string& id = extension->id();
    202   ApplicationMap::const_iterator found = applications_.find(id);
    203   return (found == applications_.end()) ? NULL : found->second;
    204 }
    205 
    206 BackgroundApplicationListModel::Application*
    207 BackgroundApplicationListModel::FindApplication(const Extension* extension) {
    208   const std::string& id = extension->id();
    209   ApplicationMap::iterator found = applications_.find(id);
    210   return (found == applications_.end()) ? NULL : found->second;
    211 }
    212 
    213 const SkBitmap* BackgroundApplicationListModel::GetIcon(
    214     const Extension* extension) {
    215   const Application* application = FindApplication(extension);
    216   if (application)
    217     return application->icon_.get();
    218   AssociateApplicationData(extension);
    219   return NULL;
    220 }
    221 
    222 int BackgroundApplicationListModel::GetPosition(
    223     const Extension* extension) const {
    224   int position = 0;
    225   const std::string& id = extension->id();
    226   for (ExtensionList::const_iterator cursor = extensions_.begin();
    227        cursor != extensions_.end();
    228        ++cursor, ++position) {
    229     if (id == cursor->get()->id())
    230       return position;
    231   }
    232   NOTREACHED();
    233   return -1;
    234 }
    235 
    236 // static
    237 bool BackgroundApplicationListModel::IsBackgroundApp(
    238     const Extension& extension) {
    239   return HasBackgroundAppPermission(extension.api_permissions());
    240 }
    241 
    242 void BackgroundApplicationListModel::Observe(
    243     NotificationType type,
    244     const NotificationSource& source,
    245     const NotificationDetails& details) {
    246   if (type == NotificationType::EXTENSIONS_READY) {
    247     Update();
    248     return;
    249   }
    250   ExtensionService* service = profile_->GetExtensionService();
    251   if (!service || !service->is_ready())
    252     return;
    253 
    254   switch (type.value) {
    255     case NotificationType::EXTENSION_LOADED:
    256       OnExtensionLoaded(Details<Extension>(details).ptr());
    257       break;
    258     case NotificationType::EXTENSION_UNLOADED:
    259       OnExtensionUnloaded(Details<UnloadedExtensionInfo>(details)->extension);
    260       break;
    261     default:
    262       NOTREACHED() << "Received unexpected notification";
    263   }
    264 }
    265 
    266 void BackgroundApplicationListModel::OnApplicationDataChanged(
    267     const Extension* extension) {
    268   FOR_EACH_OBSERVER(Observer, observers_, OnApplicationDataChanged(extension));
    269 }
    270 
    271 void BackgroundApplicationListModel::OnExtensionLoaded(Extension* extension) {
    272   // We only care about extensions that are background applications
    273   if (!IsBackgroundApp(*extension))
    274     return;
    275   AssociateApplicationData(extension);
    276   Update();
    277 }
    278 
    279 void BackgroundApplicationListModel::OnExtensionUnloaded(
    280     const Extension* extension) {
    281   if (!IsBackgroundApp(*extension))
    282     return;
    283   Update();
    284   DissociateApplicationData(extension);
    285 }
    286 
    287 void BackgroundApplicationListModel::RemoveObserver(Observer* observer) {
    288   observers_.RemoveObserver(observer);
    289 }
    290 
    291 // Update queries the extensions service of the profile with which the model was
    292 // initialized to determine the current set of background applications.  If that
    293 // differs from the old list, it generates OnApplicationListChanged events for
    294 // each observer.
    295 void BackgroundApplicationListModel::Update() {
    296   ExtensionService* service = profile_->GetExtensionService();
    297 
    298   // Discover current background applications, compare with previous list, which
    299   // is consistently sorted, and notify observers if they differ.
    300   ExtensionList extensions;
    301   GetServiceApplications(service, &extensions);
    302   ExtensionList::const_iterator old_cursor = extensions_.begin();
    303   ExtensionList::const_iterator new_cursor = extensions.begin();
    304   while (old_cursor != extensions_.end() &&
    305          new_cursor != extensions.end() &&
    306          (*old_cursor)->name() == (*new_cursor)->name() &&
    307          (*old_cursor)->id() == (*new_cursor)->id()) {
    308     ++old_cursor;
    309     ++new_cursor;
    310   }
    311   if (old_cursor != extensions_.end() || new_cursor != extensions.end()) {
    312     extensions_ = extensions;
    313     FOR_EACH_OBSERVER(Observer, observers_, OnApplicationListChanged());
    314   }
    315 }
    316