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 25class 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 34ExtensionNameComparator::ExtensionNameComparator(icu::Collator* collator) 35 : collator_(collator) { 36} 37 38bool 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. 47class 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 70namespace { 71void 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 90bool HasBackgroundAppPermission( 91 const std::set<std::string>& api_permissions) { 92 return Extension::HasApiPermission( 93 api_permissions, Extension::kBackgroundPermission); 94} 95} // namespace 96 97void 98BackgroundApplicationListModel::Observer::OnApplicationDataChanged( 99 const Extension* extension) { 100} 101 102void 103BackgroundApplicationListModel::Observer::OnApplicationListChanged() { 104} 105 106BackgroundApplicationListModel::Observer::~Observer() { 107} 108 109BackgroundApplicationListModel::Application::~Application() { 110} 111 112BackgroundApplicationListModel::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 121void 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 131void 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 139BackgroundApplicationListModel::~BackgroundApplicationListModel() { 140 STLDeleteContainerPairSecondPointers(applications_.begin(), 141 applications_.end()); 142} 143 144BackgroundApplicationListModel::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 161void BackgroundApplicationListModel::AddObserver(Observer* observer) { 162 observers_.AddObserver(observer); 163} 164 165void 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 183void 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 192const Extension* BackgroundApplicationListModel::GetExtension( 193 int position) const { 194 DCHECK(position >= 0 && static_cast<size_t>(position) < extensions_.size()); 195 return extensions_[position]; 196} 197 198const BackgroundApplicationListModel::Application* 199BackgroundApplicationListModel::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 206BackgroundApplicationListModel::Application* 207BackgroundApplicationListModel::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 213const 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 222int 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 237bool BackgroundApplicationListModel::IsBackgroundApp( 238 const Extension& extension) { 239 return HasBackgroundAppPermission(extension.api_permissions()); 240} 241 242void 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 266void BackgroundApplicationListModel::OnApplicationDataChanged( 267 const Extension* extension) { 268 FOR_EACH_OBSERVER(Observer, observers_, OnApplicationDataChanged(extension)); 269} 270 271void 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 279void BackgroundApplicationListModel::OnExtensionUnloaded( 280 const Extension* extension) { 281 if (!IsBackgroundApp(*extension)) 282 return; 283 Update(); 284 DissociateApplicationData(extension); 285} 286 287void 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. 295void 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